Update DEP-5 uri
[debian/omnibook.git] / nbsmi.c
1 /*
2  * nbsmi.c -- Toshiba SMI low-level acces code
3  * 
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2, or (at your option) any
7  * later version.
8  * 
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * Written by Mathieu BĂ©rard <mathieu.berard@crans.org>, 2006
15  *
16  * Sources of inspirations for this code were:
17  * -Toshiba via provided hardware specification
18  * -Thorsten Zachmann with the 's1bl' project
19  * -Frederico Munoz with the 'tecra_acpi' project
20  * Thanks to them
21  */
22
23 #include "omnibook.h"
24 #include "hardware.h"
25 #include <linux/preempt.h>
26 #include <linux/pci.h>
27 #include <linux/kref.h>
28 #include <asm/io.h>
29 #include <asm/mc146818rtc.h>
30 #include <linux/workqueue.h>
31 #include <linux/delay.h>
32
33 /* copied from drivers/input/serio/i8042-io.h */
34 #define I8042_KBD_PHYS_DESC "isa0060/serio0"
35
36 /*
37  * ATI's IXP PCI-LPC bridge
38  */
39 #define INTEL_PMBASE    0x40
40 #define INTEL_GPE0_EN   0x2c
41
42 #define BUFFER_SIZE     0x20
43 #define INTEL_OFFSET    0x60
44 #define INTEL_SMI_PORT  0xb2    /* APM_CNT port in INTEL ICH specs */
45
46 /*
47  * Toshiba Specs state 0xef here but:
48  * -this would overflow (ef + 19 > ff)
49  * -code from Toshiba use e0, which make much more sense
50  */
51
52 #define ATI_OFFSET      0xe0
53 #define ATI_SMI_PORT    0xb0
54
55 #define EC_INDEX_PORT   0x300
56 #define EC_DATA_PORT    0x301
57
58 /* Masks decode for GetAeral */
59 #define WLEX_MASK       0x4
60 #define WLAT_MASK       0x8
61 #define BTEX_MASK       0x1
62 #define BTAT_MASK       0x2
63
64 /*
65  * Private data of this backend
66  */
67 struct nbsmi_backend_data {
68         struct pci_dev *lpc_bridge;     /* Southbridge chip ISA bridge/LPC interface PCI device */
69         u8 start_offset;                /* Start offset in CMOS memory */
70         struct input_dev *nbsmi_input_dev;
71         struct work_struct fnkey_work;
72 };
73
74 /*
75  * Possible list of supported southbridges
76  * Here mostly to implement a more or less clean PCI probing
77  * Works only because of previous DMI probing.
78  * It's in compal.c
79  */
80 extern const struct pci_device_id lpc_bridge_table[];
81
82 /*
83  * Since we are going to trigger an SMI, all registers (I assume this does not
84  * include esp and maybe ebp) and eflags may be mangled in the
85  * process. 
86  * We also disable preemtion and IRQs upon SMI call.
87  */
88 static inline u32 ati_do_smi_call(u16 function)
89 {
90         unsigned long flags;
91         u32 retval = 0;
92
93         local_irq_save(flags);
94         preempt_disable();
95         
96 /*
97  * eflags, eax, ebx, ecx, edx, esi and edi are clobbered upon writing to SMI_PORT
98  * thus the clobber list.
99  *
100  * Equivalent pseudocode:
101  *
102  * eax = function; [non null]
103  * outw(eax, ATI_SMI_PORT); <- This Trigger an SMI
104  * if( eax == 0 ) [success if eax has been cleared]
105  *      goto out;
106  * if( inb(ATI_SMI_PORT + 1) == 0) [if not in eax, success maybe be stored here]
107  *      goto out;
108  * retval = -EIO; [too bad]
109  * out:
110  */
111         __asm__ __volatile__("outw  %%ax, %2;   \
112                               orw %%ax, %%ax;   \
113                               jz 1f;            \
114                               inw %3, %%ax;     \
115                               orw %%ax, %%ax;   \
116                               jz 1f;            \
117                               movl %4, %0;      \
118                               1:;"
119                              : "=m" (retval)
120                              : "a"(function), "N"(ATI_SMI_PORT), "N"(ATI_SMI_PORT+1), "i"(-EIO)
121                              : "memory", "ebx", "ecx", "edx", "esi", "edi", "cc");
122
123         local_irq_restore(flags);
124         preempt_enable_no_resched();
125         return retval;
126 }
127
128 static inline u32 intel_do_smi_call(u16 function, struct pci_dev *lpc_bridge)
129 {
130         u32 state;
131         unsigned long flags;
132         u32 retval = 0;
133         u32 sci_en = 0;
134
135         local_irq_save(flags);
136         preempt_disable();
137
138 /* 
139  * We get the PMBASE offset ( bits 15:7 at 0x40 offset of PCI config space )
140  * And we access offset 2c (GPE0_EN), save the state, disable all SCI
141  * and restore the state after the SMI call
142  */                     
143         pci_read_config_dword(lpc_bridge, INTEL_PMBASE, &sci_en);
144         sci_en = sci_en & 0xff80; /* Keep bits 15:7 */
145         sci_en += INTEL_GPE0_EN;  /* GPEO_EN offset */
146         state = inl(sci_en);
147         outl(0, sci_en);
148
149 /*
150  * eflags, eax, ebx, ecx, edx, esi and edi are clobbered upon writing to SMI_PORT
151  * thus the clobber list.
152  *
153  * Equivalent pseudocode:
154  *
155  * eax = function; [non null]
156  * outw(eax, INTEL_SMI_PORT); <- This Trigger an SMI
157  * if( eax == 0 ) [success if eax has been cleared]
158  *      goto out; 
159  * retval = -EIO; [too bad]
160  * out:
161  */
162         __asm__ __volatile__("outw %%ax, %2;    \
163                               orw %%ax, %%ax;   \
164                               jz 1f;            \
165                               movl %3, %0;      \
166                               1:;"
167                              : "=m" (retval)
168                              : "a"(function), "N"(INTEL_SMI_PORT), "i"(-EIO)
169                              : "memory", "ebx", "ecx", "edx", "esi", "edi", "cc");
170
171         outl(state, sci_en);
172         local_irq_restore(flags);
173         preempt_enable_no_resched();
174         return retval;
175 }
176
177 static int nbsmi_smi_command(u16 function, 
178                              const u8 * inputbuffer,
179                              u8 * outputbuffer,
180                              const struct nbsmi_backend_data *priv_data)
181 {
182         int count;
183         u32 retval = 0;
184         
185
186         for (count = 0; count < BUFFER_SIZE; count++) {
187                 outb(count + priv_data->start_offset, RTC_PORT(2));
188                 outb(*(inputbuffer + count), RTC_PORT(3));
189         }
190
191 /* 
192  * We have to write 0xe4XX to smi_port
193  * where XX is the SMI function code
194  */
195         function = (function & 0xff) << 8;
196         function |= 0xe4;
197
198         switch (priv_data->lpc_bridge->vendor) {
199         case PCI_VENDOR_ID_INTEL:
200                 retval = intel_do_smi_call(function, priv_data->lpc_bridge);
201                 break;
202         case PCI_VENDOR_ID_ATI:
203                 retval = ati_do_smi_call(function);
204                 break;
205         default:
206                 BUG();
207         }
208
209         if (retval)
210                 printk(O_ERR "smi_command failed with error %u.\n", retval);
211
212         for (count = 0; count < BUFFER_SIZE; count++) {
213                 outb(count + priv_data->start_offset, RTC_PORT(2));
214                 *(outputbuffer + count) = inb(RTC_PORT(3));
215         }
216
217         return retval;
218 }
219
220 static int nbsmi_smi_read_command(const struct omnibook_operation *io_op, u8 * data)
221 {
222         int retval;
223         u8 *inputbuffer;
224         u8 *outputbuffer;
225         struct nbsmi_backend_data *priv_data = io_op->backend->data;
226
227         if (!priv_data)
228                 return -ENODEV;
229
230         inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
231         if (!inputbuffer) {
232                 retval = -ENOMEM;
233                 goto error1;
234         }
235
236         outputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
237         if (!outputbuffer) {
238                 retval = -ENOMEM;
239                 goto error2;
240         }
241
242         retval = nbsmi_smi_command((u16) io_op->read_addr, inputbuffer, outputbuffer, priv_data);
243         if (retval)
244                 goto out;
245
246         *data = outputbuffer[0];
247
248         if (io_op->read_mask)
249                 *data &= io_op->read_mask;
250
251       out:
252         kfree(outputbuffer);
253       error2:
254         kfree(inputbuffer);
255       error1:
256         return retval;
257 }
258
259 static int nbsmi_smi_write_command(const struct omnibook_operation *io_op, u8 data)
260 {
261         int retval;
262         u8 *inputbuffer;
263         u8 *outputbuffer;
264         struct nbsmi_backend_data *priv_data = io_op->backend->data;
265
266         if (!priv_data)
267                 return -ENODEV;
268
269         inputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
270         if (!inputbuffer) {
271                 retval = -ENOMEM;
272                 goto error1;
273         }
274
275         outputbuffer = kcalloc(BUFFER_SIZE, sizeof(u8), GFP_KERNEL);
276         if (!outputbuffer) {
277                 retval = -ENOMEM;
278                 goto error2;
279         }
280
281         inputbuffer[0] = data;
282
283         retval = nbsmi_smi_command((u16) io_op->write_addr, inputbuffer, outputbuffer, priv_data);
284
285         kfree(outputbuffer);
286       error2:
287         kfree(inputbuffer);
288       error1:
289         return retval;
290 }
291
292 /*
293  * Read/Write to INDEX/DATA interface at port 0x300 (SMSC Mailbox registers)
294  */
295 static inline void nbsmi_ec_read_command(u8 index, u8 * data)
296 {
297         outb(index, EC_INDEX_PORT);
298         *data = inb(EC_DATA_PORT);
299 }
300
301 #if 0
302 static inline void nbsmi_ec_write_command(u8 index, u8 data)
303 {
304         outb(index, EC_INDEX_PORT);
305         outb(data, EC_DATA_PORT);
306 }
307 #endif
308
309
310 /*
311  * Hotkeys workflow:
312  * 1. Fn+Foo pressed
313  * 2. Scancode 0x6d generated by kbd controller
314  * 3. Scancode 0x6d caught by omnibook input handler
315  * 4. SMI Call issued -> Got keycode of last actually pressed Fn key
316  * 5. nbsmi_scan_table used to associate a detected keycode with a generated one
317  * 6. Generated keycode issued using the omnibook input device
318  */
319
320 /*
321  * The input handler should only bind with the standard AT keyboard.
322  * XXX: Scancode 0x6d won't be detected if the keyboard has already been
323  * grabbed (the Xorg event input driver do that)
324  */
325 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
326 static int hook_connect(struct input_handler *handler,
327                                          struct input_dev *dev,
328                                          const struct input_device_id *id)
329 #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
330 static struct input_handle *hook_connect(struct input_handler *handler,
331                                          struct input_dev *dev,
332                                          const struct input_device_id *id)
333 #else
334 static struct input_handle *hook_connect(struct input_handler *handler,
335                                          struct input_dev *dev,
336                                          struct input_device_id *id)
337 #endif
338 {
339         struct input_handle *handle;
340         int error;
341
342         /* the 0x0001 vendor magic number is found in atkbd.c */
343         if(!(dev->id.bustype == BUS_I8042 && dev->id.vendor == 0x0001))
344                 goto out_nobind;
345
346         if(!strstr(dev->phys, I8042_KBD_PHYS_DESC))
347                 goto out_nobind;
348
349         dprintk("hook_connect for device %s.\n", dev->name);
350
351         if(dev->grab)
352                 printk(O_WARN "Input device is grabbed by %s, Fn hotkeys won't work.\n",
353                         dev->grab->name);
354
355         handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
356         if (!handle)
357 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
358                 return -ENOMEM;
359 #else
360                 return NULL;
361 #endif
362
363         handle->dev = dev;
364         handle->handler = handler;
365         handle->name = "omnibook_scancode_hook";
366         handle->private = handler->private;
367
368 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
369         error = input_register_handle(handle);
370         if (error) {
371                 dprintk("register_handle failed\n");
372                 goto out_nobind_free;
373         } 
374         error = input_open_device(handle);
375         if (error) {
376                 dprintk("register_handle failed\n");
377                 input_unregister_handle(handle);
378                 goto out_nobind_free;
379         } 
380         
381 #else
382         error = input_open_device(handle);
383         if (error==0) dprintk("Input device opened\n");
384         else { 
385                 dprintk("opening input device failed\n");
386                 goto out_nobind_free;
387         }
388 #endif
389
390 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
391         return 0;
392 out_nobind_free:
393         kfree(handle);
394 out_nobind:
395         return -ENODEV;
396 #else
397         return handle;
398 out_nobind_free:
399         kfree(handle);
400 out_nobind:
401         return NULL;
402 #endif  
403 }
404
405 static void hook_disconnect(struct input_handle *handle)
406 {
407         dprintk("hook_disconnect.\n");
408         input_close_device(handle);
409 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
410         input_unregister_handle(handle);
411 #endif
412         kfree(handle);
413 }
414
415 /*
416  * Hook for scancode 0x6d. Actual handling is done in a workqueue as 
417  * the nbsmi backend might sleep.
418  */
419
420 static void hook_event(struct input_handle *handle, unsigned int event_type,
421                       unsigned int event_code, int value)
422 {
423         if (event_type == EV_MSC && event_code == MSC_SCAN && value == SMI_FN_SCAN)
424                 schedule_work(&((struct nbsmi_backend_data *)handle->private)->fnkey_work);
425 }
426
427 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
428 static const struct input_device_id hook_ids[] = {
429 #else
430 static struct input_device_id hook_ids[] = {
431 #endif
432         {
433                 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
434                 .evbit = { BIT(EV_KEY) },
435         },
436         { },    /* Terminating entry */
437 };
438
439 static struct input_handler hook_handler = {
440         .event          = hook_event,
441         .connect        = hook_connect,
442         .disconnect     = hook_disconnect,
443         .name           = OMNIBOOK_MODULE_NAME,
444         .id_table       = hook_ids,
445 };
446
447 /*
448  * Define some KEY_ that may be missing in input.h for some kernel versions
449  */
450 #ifndef KEY_WLAN
451 #define KEY_WLAN 238
452 #endif 
453
454 /*
455  * Detected scancode to keycode table
456  */
457 static const struct {
458         unsigned int scancode;
459         unsigned int keycode;
460 } nbsmi_scan_table[] = {
461         { KEY_ESC, KEY_MUTE},
462         { KEY_F1, KEY_FN_F1},
463         { KEY_F2, KEY_PROG1},
464         { KEY_F3, KEY_SLEEP},
465         { KEY_F4, KEY_SUSPEND},
466         { KEY_F5, KEY_SWITCHVIDEOMODE},
467         { KEY_F6, KEY_BRIGHTNESSDOWN},
468         { KEY_F7, KEY_BRIGHTNESSUP},
469         { KEY_F8, KEY_WLAN},
470         { KEY_F9, KEY_FN_F9},
471         { KEY_SPACE, KEY_ZOOM},
472         { 0,0},
473 };
474
475 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
476 static void omnibook_handle_fnkey(struct work_struct *work);
477 #else
478 static void omnibook_handle_fnkey(void* data);
479 #endif
480
481 /*
482  * Register the input handler and the input device in the input subsystem
483  */
484 static int register_input_subsystem(struct nbsmi_backend_data *priv_data)
485 {
486         int i, retval = 0;
487         struct input_dev *nbsmi_input_dev;
488
489         nbsmi_input_dev = input_allocate_device();
490         if (!nbsmi_input_dev) {
491                 retval = -ENOMEM;
492                 goto out;
493         }
494
495         nbsmi_input_dev->name = "Omnibook NbSMI scancode generator";
496         nbsmi_input_dev->phys = "omnibook/input0";
497         nbsmi_input_dev->id.bustype = BUS_HOST;
498         
499         set_bit(EV_KEY, nbsmi_input_dev->evbit);
500         
501         for(i=0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++)
502                 set_bit(nbsmi_scan_table[i].keycode, nbsmi_input_dev->keybit);
503
504         retval = input_register_device(nbsmi_input_dev);
505         if(retval) {
506                 input_free_device(nbsmi_input_dev);
507                 goto out;
508         }
509
510         priv_data->nbsmi_input_dev = nbsmi_input_dev;
511
512 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
513         INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey);
514 #else
515         INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey, priv_data);
516 #endif
517
518
519         hook_handler.private = priv_data;
520
521 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
522         retval = input_register_handler(&hook_handler); 
523 #else
524         input_register_handler(&hook_handler);
525 #endif
526
527         out:    
528         return retval;
529 }
530
531 /*
532  * Try to init the backend
533  * This function can be called blindly as it use a kref
534  * to check if the init sequence was already done.
535  */
536 static int omnibook_nbsmi_init(const struct omnibook_operation *io_op)
537 {
538         int retval = 0;
539         int i;
540         u8 ec_data;
541         u32 smi_port = 0;
542         struct nbsmi_backend_data *priv_data;
543
544         /* ectypes other than TSM40 have no business with this backend */
545         if (!(omnibook_ectype & TSM40))
546                 return -ENODEV;
547
548         if (io_op->backend->already_failed) {
549                 dprintk("NbSmi backend init already failed, skipping.\n");
550                 return -ENODEV;
551         }
552
553         if (!io_op->backend->data) {
554                 /* Fist use of the backend */
555                 dprintk("Try to init NbSmi\n");
556                 mutex_init(&io_op->backend->mutex);
557                 mutex_lock(&io_op->backend->mutex);
558                 kref_init(&io_op->backend->kref);
559
560                 priv_data = kzalloc(sizeof(struct nbsmi_backend_data), GFP_KERNEL);
561                 if (!priv_data) {
562                         retval = -ENOMEM;
563                         goto error0;
564                 }
565
566                 /* PCI probing: find the LPC Super I/O bridge PCI device */
567                 for (i = 0; !priv_data->lpc_bridge && lpc_bridge_table[i].vendor; ++i)
568                         priv_data->lpc_bridge =
569                             pci_get_device(lpc_bridge_table[i].vendor, lpc_bridge_table[i].device,
570                                            NULL);
571
572                 if (!priv_data->lpc_bridge) {
573                         printk(O_ERR "Fail to find a supported LPC I/O bridge, please report\n");
574                         retval = -ENODEV;
575                         goto error1;
576                 }
577
578                 if ((retval = pci_enable_device(priv_data->lpc_bridge))) {
579                         printk(O_ERR "Unable to enable PCI device.\n");
580                         goto error2;
581                 }
582
583                 switch (priv_data->lpc_bridge->vendor) {
584                 case PCI_VENDOR_ID_INTEL:
585                         priv_data->start_offset = INTEL_OFFSET;
586                         smi_port = INTEL_SMI_PORT;
587                         break;
588                 case PCI_VENDOR_ID_ATI:
589                         priv_data->start_offset = ATI_OFFSET;
590                         smi_port = ATI_SMI_PORT;
591                         break;
592                 default:
593                         BUG();
594                 }
595
596                 if (!request_region(smi_port, 2, OMNIBOOK_MODULE_NAME)) {
597                         printk(O_ERR "Request SMI I/O region error\n");
598                         retval = -ENODEV;
599                         goto error2;
600                 }
601
602                 if (!request_region(EC_INDEX_PORT, 2, OMNIBOOK_MODULE_NAME)) {
603                         printk(O_ERR "Request EC I/O region error\n");
604                         retval = -ENODEV;
605                         goto error3;
606                 }
607
608                 /*
609                  * Try some heuristic tests to avoid enabling this interface on unsuported laptops:
610                  * See what a port 300h read index 8f gives. Guess there is nothing if read 0xff
611                  */
612
613                 nbsmi_ec_read_command(SMI_FN_PRESSED, &ec_data);
614                 dprintk("NbSmi test probe read: %x\n", ec_data);
615                 if (ec_data == 0xff) {
616                         printk(O_ERR "Probing at SMSC Mailbox registers failed, disabling NbSmi\n");
617                         retval = -ENODEV;
618                         goto error4;
619                 }
620
621                 retval = register_input_subsystem(priv_data);
622                 if(retval)
623                         goto error4;
624
625                 io_op->backend->data = priv_data;
626
627                 dprintk("NbSmi init ok\n");
628                 mutex_unlock(&io_op->backend->mutex);
629                 return 0;
630         } else {
631                 dprintk("NbSmi has already been initialized\n");
632                 kref_get(&io_op->backend->kref);
633                 return 0;
634         }
635       error4:
636         release_region(EC_INDEX_PORT, 2);
637       error3:
638         release_region(smi_port, 2);
639       error2:
640         pci_dev_put(priv_data->lpc_bridge);
641       error1:
642         kfree(priv_data);
643         io_op->backend->data = NULL;
644       error0:
645         io_op->backend->already_failed = 1;
646         mutex_unlock(&io_op->backend->mutex);
647         mutex_destroy(&io_op->backend->mutex);
648         return retval;
649 }
650
651 /*
652  * Free all allocated stuff and unregister from the input subsystem
653  */
654 static void nbsmi_free(struct kref *ref)
655 {
656         u32 smi_port = 0;
657         struct omnibook_backend *backend;
658         struct nbsmi_backend_data *priv_data;
659
660         dprintk("NbSmi not used anymore: disposing\n");
661
662         backend = container_of(ref, struct omnibook_backend, kref);
663         priv_data = backend->data;
664
665         flush_scheduled_work();
666         input_unregister_handler(&hook_handler);
667         input_unregister_device(priv_data->nbsmi_input_dev);
668
669         mutex_lock(&backend->mutex);
670
671         switch (priv_data->lpc_bridge->vendor) {
672         case PCI_VENDOR_ID_INTEL:
673                 smi_port = INTEL_SMI_PORT;
674                 break;
675         case PCI_VENDOR_ID_ATI:
676                 smi_port = ATI_SMI_PORT;
677                 break;
678         default:
679                 BUG();
680         }
681
682         pci_dev_put(priv_data->lpc_bridge);
683         release_region(smi_port, 2);
684         release_region(EC_INDEX_PORT, 2);
685         kfree(priv_data);
686         backend->data = NULL;
687         mutex_unlock(&backend->mutex);
688         mutex_destroy(&backend->mutex);
689 }
690
691 static void omnibook_nbsmi_exit(const struct omnibook_operation *io_op)
692 {
693         /* ectypes other than TSM40 have no business with this backend */
694         BUG_ON(!(omnibook_ectype & TSM40));
695         dprintk("Trying to dispose NbSmi\n");
696         kref_put(&io_op->backend->kref, nbsmi_free);
697 }
698
699 /*
700  * Adjust the lcd backlight level by delta.
701  * Used for Fn+F6/F7 keypress
702  */
703 static int adjust_brighness(int delta)
704 {
705         struct omnibook_feature *lcd_feature = omnibook_find_feature("lcd");
706         struct omnibook_operation *io_op;
707         int retval = 0;
708         u8 brgt;
709
710         if(!lcd_feature)
711                 return -ENODEV;
712
713         io_op = lcd_feature->io_op;
714
715         mutex_lock(&io_op->backend->mutex);
716
717         if(( retval = __backend_byte_read(io_op, &brgt)))
718                 goto out;       
719
720         dprintk("FnF6/F7 pressed: adjusting britghtnes.\n");
721
722         if (((int) brgt + delta) < 0)
723                 brgt = 0;
724         else if ((brgt + delta) > omnibook_max_brightness)
725                 brgt = omnibook_max_brightness;
726         else
727                 brgt += delta;
728
729         retval = __backend_byte_write(io_op, brgt);
730
731         out:
732         mutex_unlock(&io_op->backend->mutex);
733         return retval;
734 }
735
736 static const struct omnibook_operation last_scan_op = SIMPLE_BYTE(SMI,SMI_GET_FN_LAST_SCAN,0);
737
738 /*
739  * Workqueue handler for Fn hotkeys
740  */
741 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
742 static void omnibook_handle_fnkey(struct work_struct *work)
743 #else
744 static void omnibook_handle_fnkey(void* data)
745 #endif
746 {
747         int i;
748         u8 gen_scan;
749         struct input_dev *input_dev;
750
751         if(backend_byte_read(&last_scan_op, &gen_scan))
752                 return;
753
754         dprintk("detected scancode %x.\n", gen_scan);
755         switch(gen_scan) {
756         case KEY_F6:
757                 adjust_brighness(-1);
758                 break;
759         case KEY_F7:
760                 adjust_brighness(+1);
761                 break;
762         }
763
764         for(i = 0 ; i < ARRAY_SIZE(nbsmi_scan_table); i++) {
765                 if( gen_scan == nbsmi_scan_table[i].scancode) {
766                         dprintk("generating keycode %i.\n", nbsmi_scan_table[i].keycode);
767 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
768                         input_dev = container_of(work, struct nbsmi_backend_data, fnkey_work)->nbsmi_input_dev;
769 #else
770                         input_dev = ((struct nbsmi_backend_data *) data)->nbsmi_input_dev;
771 #endif
772                         omnibook_report_key(input_dev, nbsmi_scan_table[i].keycode);
773                         break;
774                 }
775         }
776 }
777
778 static int omnibook_nbsmi_get_wireless(const struct omnibook_operation *io_op, unsigned int *state)
779 {
780         int retval = 0;
781         struct omnibook_operation aerial_op = SIMPLE_BYTE(SMI, SMI_GET_KILL_SWITCH, 0);
782         u8 data;
783
784         if ((retval = nbsmi_smi_read_command(&aerial_op, &data)))
785                 goto out;
786
787         dprintk("get_wireless (kill switch) raw_state: %x\n", data);
788
789         *state = data ? KILLSWITCH : 0;
790
791         aerial_op.read_addr = SMI_GET_AERIAL;
792
793         if ((retval = nbsmi_smi_read_command(&aerial_op, &data)))
794                 goto out;
795
796         dprintk("get_wireless (aerial) raw_state: %x\n", data);
797
798         *state |= (data & WLEX_MASK) ? WIFI_EX : 0;
799         *state |= (data & WLAT_MASK) ? WIFI_STA : 0;
800         *state |= (data & BTEX_MASK) ? BT_EX : 0;
801         *state |= (data & BTAT_MASK) ? BT_STA : 0;
802
803       out:
804         return retval;
805 }
806
807 static int omnibook_nbsmi_set_wireless(const struct omnibook_operation *io_op, unsigned int state)
808 {
809         int retval = 0;
810         u8 data;
811         struct omnibook_operation aerial_op = SIMPLE_BYTE(SMI, SMI_SET_AERIAL, 0);
812
813         data = !!(state & BT_STA);
814         data |= !!(state & WIFI_STA) << 0x1;
815
816         dprintk("set_wireless raw_state: %x\n", data);
817
818         retval = nbsmi_smi_write_command(&aerial_op, data);
819
820         return retval;
821 }
822
823 static int omnibook_nbmsi_hotkeys_get(const struct omnibook_operation *io_op, unsigned int *state)
824 {
825         int retval;
826         u8 data = 0;
827         struct omnibook_operation hotkeys_op = SIMPLE_BYTE(SMI, SMI_GET_FN_INTERFACE, 0);
828
829         retval = nbsmi_smi_read_command(&hotkeys_op, &data);
830         if (retval < 0)
831                 return retval;
832
833         dprintk("get_hotkeys raw_state: %x\n", data);
834
835         *state = (data & SMI_FN_KEYS_MASK) ? HKEY_FN : 0;
836         *state |= (data & SMI_STICK_KEYS_MASK) ? HKEY_STICK : 0;
837         *state |= (data & SMI_FN_TWICE_LOCK_MASK) ? HKEY_TWICE_LOCK : 0;
838         *state |= (data & SMI_FN_DOCK_MASK) ? HKEY_DOCK : 0;
839
840         return 0;
841 }
842
843
844 static int omnibook_nbmsi_hotkeys_set(const struct omnibook_operation *io_op, unsigned int state)
845 {
846         int i, retval;
847         u8 data, rdata;
848         struct omnibook_operation hotkeys_op = SIMPLE_BYTE(SMI, SMI_SET_FN_F5_INTERFACE, 0);    
849         u8* data_array;
850
851         data = !!(state & HKEY_FNF5);
852
853         dprintk("set_hotkeys (Fn F5) raw_state: %x\n", data);
854
855         retval = nbsmi_smi_write_command(&hotkeys_op, data);
856         if (retval < 0)
857                 return retval;
858
859         hotkeys_op.write_addr = SMI_SET_FN_INTERFACE;
860         hotkeys_op.read_addr =  SMI_GET_FN_INTERFACE;
861
862         data = (state & HKEY_FN) ? SMI_FN_KEYS_MASK : 0;
863         data |= (state & HKEY_STICK) ? SMI_STICK_KEYS_MASK : 0;
864         data |= (state & HKEY_TWICE_LOCK) ? SMI_FN_TWICE_LOCK_MASK : 0;
865         data |= (state & HKEY_DOCK) ? SMI_FN_DOCK_MASK : 0;
866
867         dprintk("set_hotkeys (Fn interface) raw_state: %x\n", data);
868
869         /*
870          * Hardware seems to be quite stubborn and multiple retries may be
871          * required. The criteria here is simple: retry until probed state match
872          * the requested one (with timeout).
873          */
874
875         data_array = kcalloc(250, sizeof(u8), GFP_KERNEL);
876         if(!data_array)
877                 return -ENODEV;
878
879         for (i = 0; i < 250; i++) {
880                 retval = nbsmi_smi_write_command(&hotkeys_op, data);
881                 if (retval)
882                         goto out;
883                 mdelay(1);
884                 retval = nbsmi_smi_read_command(&hotkeys_op, &rdata);
885                 if(retval)
886                         goto out;
887                 data_array[i] = rdata;
888                 if(rdata == data) {
889                         dprintk("check loop ok after %i iters\n.",i);
890                         retval = 0;
891                         goto out;
892                 }
893         }
894         dprintk("error or check loop timeout !!\n");
895         dprintk("forensics datas: ");
896         for (i = 0; i < 250; i++)
897                 dprintk_simple("%x ", data_array[i]);
898         dprintk_simple("\n");
899 out:
900         kfree(data_array);
901         return retval;
902 }
903
904 static const unsigned int nbsmi_display_mode_list[] = {
905         DISPLAY_LCD_ON,
906         DISPLAY_LCD_ON | DISPLAY_CRT_ON,
907         DISPLAY_CRT_ON,
908         DISPLAY_LCD_ON | DISPLAY_TVO_ON,
909         DISPLAY_TVO_ON,
910 };
911
912 static int omnibook_nbmsi_display_get(const struct omnibook_operation *io_op, unsigned int *state)
913 {
914         int retval = 0;
915         u8 data;
916
917         retval = nbsmi_smi_read_command(io_op, &data);
918         if (retval < 0)
919                 return retval;
920
921         if (data > (ARRAY_SIZE(nbsmi_display_mode_list) - 1))
922                 return -EIO;
923
924         *state = nbsmi_display_mode_list[data];
925
926         return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON;
927 }
928
929 static int omnibook_nbmsi_display_set(const struct omnibook_operation *io_op, unsigned int state)
930 {
931         int retval;
932         int i;
933         u8 matched = 255;
934
935         for (i = 0; i < ARRAY_SIZE(nbsmi_display_mode_list); i++) {
936                 if (nbsmi_display_mode_list[i] == state) {
937                         matched = i;
938                         break;
939                 }
940         }
941
942         if(matched == 255) {
943                 printk(O_ERR "Display mode %x is unsupported.\n", state);
944                 return -EINVAL;
945         }
946
947         retval = nbsmi_smi_write_command(io_op, matched);
948         if (retval < 0)
949                 return retval;
950
951         return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON;
952 }
953
954 struct omnibook_backend nbsmi_backend = {
955         .name = "nbsmi",
956         .hotkeys_read_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK,
957         .hotkeys_write_cap = HKEY_FN | HKEY_STICK | HKEY_TWICE_LOCK | HKEY_DOCK | HKEY_FNF5,
958         .init = omnibook_nbsmi_init,
959         .exit = omnibook_nbsmi_exit,
960         .byte_read = nbsmi_smi_read_command,
961         .byte_write = nbsmi_smi_write_command,
962         .aerial_get = omnibook_nbsmi_get_wireless,
963         .aerial_set = omnibook_nbsmi_set_wireless,
964         .hotkeys_get = omnibook_nbmsi_hotkeys_get,
965         .hotkeys_set = omnibook_nbmsi_hotkeys_set,
966         .display_get = omnibook_nbmsi_display_get,
967         .display_set = omnibook_nbmsi_display_set,
968 };