Update DEP-5 uri
[debian/omnibook.git] / acpi.c
1 /*
2  * acpi.c -- ACPI methods low-level access code for TSM70 class laptops
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  */
17
18 #include "omnibook.h"
19 #include "hardware.h"
20
21 #ifdef CONFIG_ACPI
22
23 #include <acpi/acpi_drivers.h>
24 #include <linux/workqueue.h>
25
26 /* copied from drivers/input/serio/i8042-io.h */
27 #define I8042_KBD_PHYS_DESC "isa0060/serio0"
28
29 /*
30  * ACPI backend masks and strings
31  */
32
33 #define GET_WIRELESS_METHOD "ANTR"
34 #define SET_WIRELESS_METHOD "ANTW"
35 #define WLEX_MASK       0x4
36 #define WLAT_MASK       0x1
37 #define BTEX_MASK       0x8
38 #define BTAT_MASK       0x2
39 #define KLSW_MASK       0x10
40
41 #define GET_DISPLAY_METHOD "DOSS"
42 #define SET_DISPLAY_METHOD "DOSW"
43 /* Display reading masks CADL = detected, CSTE = enabled */
44 #define LCD_CADL        0x10
45 #define CRT_CADL        0x20
46 #define TVO_CADL        0x40
47 #define DVI_CADL        0x80
48 #define LCD_CSTE        0x1
49 #define CRT_CSTE        0x2
50 #define TVO_CSTE        0x4
51 #define DVI_CSTE        0x8
52
53 /* TSX205 Video-Out methods and return values */
54 #define TSX205_SET_DISPLAY_METHOD "STBL"
55 #define TSX205_SLI_DISPLAY_METHOD "SL01.VGA1.STBL"
56 /* NOTE: Method DSSW seems to be some sort of auto-detect method */
57 #define TSX205_AUTO_DISPLAY_METHOD "DSSW"
58 #define TSX205_DSPY_DE  0x1F    /* DE - Detected and Enabled */
59 #define TSX205_DSPY_DN  0x1D    /* DN - Detected and Not enabled */
60 #define TSX205_DSPY_NE  0x0F    /* NE - Not detected and Enabled */
61 #define TSX205_DSPY_NN  0x0D    /* NN - Not detected and Not enabled */
62
63 #define GET_THROTTLE_METHOD "THRO"
64 #define SET_THROTTLE_METHOD "CLCK"
65
66 static char ec_dev_list[][20] = {
67         "\\_SB.PCI0.LPCB.EC0",
68         "\\_SB.PCI0.LPC0.EC0",
69 };
70
71 /* TSX205 HCI and display handles */
72 static char tsx205_dev_list[][20] = {
73         "\\_SB.VALZ",
74         "\\_SB.PCI0.PEGP.VGA"
75 };
76
77 /* TSX205 GET video-out methods */
78 static char tsx205_video_list[][20] = {
79         "LCD._DCS",
80         "CRT._DCS",
81         "TV._DCS",
82         "DVI._DCS",
83         "SL01.VGA1.LCD._DCS",
84         "SL01.VGA1.CRT._DCS",
85         "SL01.VGA1.TV._DCS",
86         "SL01.VGA1.DVI._DCS",
87 };
88
89 #define TOSHIBA_ACPI_BT_CLASS "bluetooth"
90 #define TOSHIBA_ACPI_DEVICE_NAME "bluetooth adapter"
91
92 #define TOSH_BT_ACTIVATE_USB    "AUSB"
93 #define TOSH_BT_DISABLE_USB     "DUSB"
94 #define TOSH_BT_POWER_ON        "BTPO"
95 #define TOSH_BT_POWER_OFF       "BTPF"
96 #define TOSH_BT_STATUS          "BTST"
97 #define TOSH_BT_KSST_MASK       0x1
98 #define TOSH_BT_USB_MASK        0x40
99 #define TOSH_BT_POWER_MASK      0x80
100
101 /*
102  * ACPI driver for Toshiba Bluetooth device
103  */
104 static int omnibook_acpi_bt_add(struct acpi_device *device);
105 static int omnibook_acpi_bt_remove(struct acpi_device *device, int type);
106
107
108 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
109 static const struct acpi_device_id omnibook_bt_ids[] = {
110         {"TOS6205", 0},
111         {"", 0},
112 };
113
114 static struct acpi_driver omnibook_bt_driver = {
115         .name   = OMNIBOOK_MODULE_NAME,
116         .class  = TOSHIBA_ACPI_BT_CLASS,
117         .ids    = omnibook_bt_ids,
118         .ops    = {
119                         .add    =  omnibook_acpi_bt_add,
120                         .remove =  omnibook_acpi_bt_remove,
121                   },
122 };
123 #else /* 2.6.23 */
124 static struct acpi_driver omnibook_bt_driver = {
125         .name   = OMNIBOOK_MODULE_NAME,
126         .class  = TOSHIBA_ACPI_BT_CLASS,
127         .ids    = "TOS6205",
128         .ops    = {
129                         .add    =  omnibook_acpi_bt_add,
130                         .remove =  omnibook_acpi_bt_remove,
131                   },
132 };
133 #endif /* 2.6.23 */
134
135
136 /*
137  * ACPI backend private data structure
138  */
139 struct acpi_backend_data {
140         acpi_handle ec_handle;  /* Handle on ACPI EC device */
141         acpi_handle bt_handle;  /* Handle on ACPI BT device */
142         acpi_handle hci_handle; /* Handle on ACPI HCI device */
143         acpi_handle dis_handle; /* Handle on ACPI Display device */
144         unsigned has_antr_antw:1; /* Are there ANTR/ANTW methods in the EC device ? */
145         unsigned has_doss_dosw:1; /* Are there DOSS/DOSW methods in the EC device ? */
146         unsigned has_sli:1; /* Does the laptop has SLI enabled ? */
147         struct input_dev *acpi_input_dev;
148         struct work_struct fnkey_work;
149 };
150
151 /*
152  * Hotkeys workflow:
153  * 1. Fn+Foo pressed
154  * 2. Scancode 0x6e generated by kbd controller
155  * 3. Scancode 0x6e caught by omnibook input handler
156  * 4. INFO method has keycode of last actually pressed Fn key
157  * 5. acpi_scan_table used to associate a detected keycode with a generated one
158  * 6. Generated keycode issued using the omnibook input device
159  */
160
161 /*
162  * The input handler should only bind with the standard AT keyboard.
163  * XXX: Scancode 0x6e won't be detected if the keyboard has already been
164  * grabbed (the Xorg event input driver do that)
165  */
166 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
167 static int hook_connect(struct input_handler *handler,
168                                          struct input_dev *dev,
169                                          const struct input_device_id *id)
170 #elif (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
171 static struct input_handle *hook_connect(struct input_handler *handler,
172                                          struct input_dev *dev,
173                                          const struct input_device_id *id)
174 #else
175 static struct input_handle *hook_connect(struct input_handler *handler,
176                                          struct input_dev *dev,
177                                          struct input_device_id *id)
178 #endif
179 {
180         struct input_handle *handle;
181         int error;
182
183         /* the 0x0001 vendor magic number is found in atkbd.c */
184         if(!(dev->id.bustype == BUS_I8042 && dev->id.vendor == 0x0001))
185                 goto out_nobind;
186
187         if(!strstr(dev->phys, I8042_KBD_PHYS_DESC))
188                 goto out_nobind;
189
190         dprintk("hook_connect for device %s.\n", dev->name);
191
192         if(dev->grab)
193                 printk(O_WARN "Input device is grabbed by %s, Fn hotkeys won't work.\n",
194                         dev->grab->name);
195
196         handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
197         if (!handle)
198 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
199                 return -ENOMEM;
200 #else
201                 return NULL;
202 #endif
203
204         handle->dev = dev;
205         handle->handler = handler;
206         handle->name = "omnibook_scancode_hook";
207         handle->private = handler->private;
208
209 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
210         error = input_register_handle(handle);
211         if (error) {
212                 dprintk("register_handle failed\n");
213                 goto out_nobind_free;
214         } 
215         error = input_open_device(handle);
216         if (error) {
217                 dprintk("register_handle failed\n");
218                 input_unregister_handle(handle);
219                 goto out_nobind_free;
220         } 
221         
222 #else
223         status=input_open_device(handle);
224         if (error==0) dprintk("Input device opened\n");
225         else { 
226                 dprintk("opening input device failed\n");
227                 goto out_nobind_free;
228         }
229 #endif
230
231 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
232         return 0;
233 out_nobind_free:
234         kfree(handle);
235 out_nobind:
236         return -ENODEV;
237 #else
238         return handle;
239 out_nobind_free:
240         kfree(handle);
241 out_nobind:
242         return NULL;
243 #endif  
244 }
245
246 static void hook_disconnect(struct input_handle *handle)
247 {
248         dprintk("hook_disconnect.\n");
249         input_close_device(handle);
250 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,21))
251         input_unregister_handle(handle);
252 #endif
253         kfree(handle);
254 }
255
256 /*
257  * Hook for scancode 0x6e. Actual handling is done in a workqueue.
258  */
259 static void hook_event(struct input_handle *handle, unsigned int event_type,
260                       unsigned int event_code, int value)
261 {
262         if (event_type == EV_MSC && event_code == MSC_SCAN && value == ACPI_FN_SCAN)
263                 schedule_work(&((struct acpi_backend_data *)handle->private)->fnkey_work);
264 }
265
266 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
267 static const struct input_device_id hook_ids[] = {
268 #else
269 static struct input_device_id hook_ids[] = {
270 #endif
271         {
272                 .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
273                 .evbit = { BIT(EV_KEY) },
274         },
275         { },    /* Terminating entry */
276 };
277
278 static struct input_handler hook_handler = {
279         .event          = hook_event,
280         .connect        = hook_connect,
281         .disconnect     = hook_disconnect,
282         .name           = OMNIBOOK_MODULE_NAME,
283         .id_table       = hook_ids,
284 };
285
286 /*
287  * Detected scancode to keycode table
288  */
289 static const struct {
290         unsigned int scancode;
291         unsigned int keycode;
292 } acpi_scan_table[] = {
293         { HCI_FN_RELEASED,    KEY_FN},
294         { HCI_MUTE,           KEY_MUTE},
295         { HCI_BREAK,          KEY_COFFEE},
296         { HCI_1,              KEY_ZOOMOUT},
297         { HCI_2,              KEY_ZOOMIN},
298         { HCI_SPACE,          KEY_ZOOMRESET},
299         { HCI_BSM,            KEY_BATTERY},
300         { HCI_SUSPEND,        KEY_SLEEP},
301         { HCI_HIBERNATE,      KEY_SUSPEND},
302         { HCI_VIDEOOUT,       KEY_SWITCHVIDEOMODE},
303         { HCI_BRIGHTNESSDOWN, KEY_BRIGHTNESSDOWN},
304         { HCI_BRIGHTNESSUP,   KEY_BRIGHTNESSUP},
305         { HCI_WLAN,           KEY_WLAN},
306         { HCI_TOUCHPAD,       KEY_PROG1},
307         { HCI_FN_PRESSED,     KEY_FN},
308         { 0, 0},
309 };
310
311 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
312 static void omnibook_handle_fnkey(struct work_struct *work);
313 #else
314 static void omnibook_handle_fnkey(void* data);
315 #endif
316
317 /*
318  * Register the input handler and the input device in the input subsystem
319  */
320 static int register_input_subsystem(struct acpi_backend_data *priv_data)
321 {
322         int i, retval = 0;
323         struct input_dev *acpi_input_dev;
324
325         acpi_input_dev = input_allocate_device();
326         if (!acpi_input_dev) {
327                 retval = -ENOMEM;
328                 goto out;
329         }
330
331         acpi_input_dev->name = "Omnibook ACPI scancode generator";
332         acpi_input_dev->phys = "omnibook/input0";
333         acpi_input_dev->id.bustype = BUS_HOST;
334         
335         set_bit(EV_KEY, acpi_input_dev->evbit);
336         
337         for(i=0 ; i < ARRAY_SIZE(acpi_scan_table); i++)
338                 set_bit(acpi_scan_table[i].keycode, acpi_input_dev->keybit);
339
340         retval = input_register_device(acpi_input_dev);
341         if (retval) {
342                 input_free_device(acpi_input_dev);
343                 goto out;
344         }
345
346         priv_data->acpi_input_dev = acpi_input_dev;
347
348 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
349         INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey);
350 #else
351         INIT_WORK(&priv_data->fnkey_work, *omnibook_handle_fnkey, priv_data);
352 #endif
353
354
355         hook_handler.private = priv_data;
356
357 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
358         retval = input_register_handler(&hook_handler); 
359 #else
360         input_register_handler(&hook_handler);
361 #endif
362
363         out:    
364         return retval;
365 }
366
367 /*
368  * Execute an ACPI method which return either an integer or nothing
369  * and that require 0 or 1 numerical argument
370  * (acpi_evaluate_object wrapper)
371  */
372 static int omnibook_acpi_execute(acpi_handle dev_handle, char *method, const int *param, int *result)
373 {
374
375         struct acpi_object_list args_list;
376         struct acpi_buffer buff;
377         union acpi_object arg, out_objs[1];
378         
379         if (param) {
380                 args_list.count = 1;
381                 args_list.pointer = &arg;
382                 arg.type = ACPI_TYPE_INTEGER;
383                 arg.integer.value = *param;
384         } else
385                 args_list.count = 0;
386
387         buff.length = sizeof(out_objs);
388         buff.pointer = out_objs;
389
390         if (acpi_evaluate_object(dev_handle, method, &args_list, &buff) != AE_OK) {
391                 printk(O_ERR "ACPI method execution failed\n");
392                 return -EIO;
393         }
394
395         if (!result)            /* We don't care what the method returned here */
396                 return 0;
397
398         if (out_objs[0].type != ACPI_TYPE_INTEGER) {
399                 printk(O_ERR "ACPI method result is not a number\n");
400                 return -EINVAL;
401         }
402
403         *result = out_objs[0].integer.value;
404         return 0;
405 }
406
407 /*
408  * Probe for expected ACPI devices
409  */
410 static int omnibook_acpi_init(const struct omnibook_operation *io_op)
411 {
412         int retval = 0; 
413         acpi_handle dev_handle, method_handle, hci_handle, dis_handle;
414         int i;
415         int has_sli = 0;
416         struct acpi_backend_data *priv_data;
417         
418         if (unlikely(acpi_disabled)) {
419                 printk(O_ERR "ACPI is disabled: feature unavailable.\n");
420                 return -ENODEV;
421         }
422
423         if (!io_op->backend->data) {
424                 dprintk("Try to init ACPI backend\n");
425                 mutex_init(&io_op->backend->mutex);
426                 mutex_lock(&io_op->backend->mutex);
427                 kref_init(&io_op->backend->kref);
428                 priv_data = kzalloc(sizeof(struct acpi_backend_data), GFP_KERNEL);
429                 if (!priv_data) {
430                         retval = -ENOMEM;
431                         goto error0;
432                 }
433
434                 /* Locate ACPI EC device, acpi_get_handle set dev_handle to NULL if not found */
435                 for (i = 0; i < ARRAY_SIZE(ec_dev_list); i++) {
436                         if (acpi_get_handle(NULL, ec_dev_list[i], &dev_handle) == AE_OK) {
437                                 dprintk("ACPI EC device found\n");
438                                 priv_data->ec_handle = dev_handle;
439                                 break;
440                         }
441                 }
442                 
443                 if (!dev_handle) {
444                         printk(O_ERR "Can't get handle on ACPI EC device.\n");
445                         retval = -ENODEV;
446                         goto error1;
447                 }
448
449                 /* Probe for HCI and Display devices only on TSX205 models */
450                 if (omnibook_ectype & TSX205) {
451                         if (acpi_get_handle(NULL, tsx205_dev_list[0], &hci_handle) == AE_OK) {
452                                 dprintk("Toshiba X205 HCI device found\n");
453                                 priv_data->hci_handle = hci_handle;
454                         }
455
456                         if (!hci_handle) {
457                                 printk(O_ERR "Couldn't get HCI handle.\n");
458                                 retval = -ENODEV;
459                                 goto error1;
460                         }
461
462                         if (acpi_get_handle(NULL, tsx205_dev_list[1], &dis_handle) == AE_OK)
463                                 priv_data->dis_handle = dis_handle;
464
465                         if (!dis_handle) {
466                                 printk(O_ERR "Couldn't get X205 Display handle.\n");
467                                 retval = -ENODEV;
468                                 goto error1;
469                         }
470
471                         /* Does the laptop has SLI enabled? */
472                         omnibook_acpi_execute(dis_handle, (char *)TSX205_SLIVDO_METHOD, NULL, &has_sli);
473                         if (has_sli)
474                                 dprintk("Toshiba X205 Display device found (SLI).\n");
475                         else
476                                 dprintk("Toshiba X205 Display device found.\n");
477
478                         priv_data->has_sli = has_sli;
479                 }
480
481                 if ((acpi_get_handle( dev_handle, GET_WIRELESS_METHOD, &method_handle) == AE_OK) &&
482                     (acpi_get_handle( dev_handle, SET_WIRELESS_METHOD, &method_handle) == AE_OK))
483                         priv_data->has_antr_antw = 1;
484
485                 if (omnibook_ectype & TSX205) {
486                         if ((acpi_get_handle(dis_handle, TSX205_AUTO_DISPLAY_METHOD, &method_handle) ==  AE_OK) &&
487                             (acpi_get_handle(dis_handle, TSX205_AUTO_DISPLAY_METHOD, &method_handle) ==  AE_OK))
488                                 priv_data->has_doss_dosw = 1;
489                 } else {
490                         if ((acpi_get_handle( dev_handle, GET_DISPLAY_METHOD, &method_handle) == AE_OK) &&
491                             (acpi_get_handle( dev_handle, SET_DISPLAY_METHOD, &method_handle) == AE_OK))
492                                 priv_data->has_doss_dosw = 1;
493                 }
494
495                 retval = register_input_subsystem(priv_data);
496                 if(retval)
497                         goto error1;
498
499                 io_op->backend->data = (void *) priv_data;
500                 
501                 mutex_unlock(&io_op->backend->mutex);
502                 
503                 /* attempt to register Toshiba bluetooth ACPI driver */
504                 acpi_bus_register_driver(&omnibook_bt_driver);
505
506                 dprintk("ACPI backend init OK\n");
507                 
508                 return 0;
509
510         } else {
511                 dprintk("ACPI backend has already been initialized\n");
512                 kref_get(&io_op->backend->kref);
513                 return 0;
514         }
515                 
516         error1:
517         kfree(priv_data);
518         io_op->backend->data = NULL;
519         error0:
520         mutex_unlock(&io_op->backend->mutex);
521         mutex_destroy(&io_op->backend->mutex);
522         return retval;
523 }
524
525 static void omnibook_acpi_free(struct kref *ref)
526 {
527         struct omnibook_backend *backend;
528         struct acpi_backend_data *priv_data;
529
530         backend = container_of(ref, struct omnibook_backend, kref);
531         priv_data = backend->data;
532
533         dprintk("ACPI backend not used anymore: disposing\n");
534
535         
536         dprintk("ptr addr: %p driver name: %s\n",&omnibook_bt_driver, omnibook_bt_driver.name);
537         acpi_bus_unregister_driver(&omnibook_bt_driver);
538
539         flush_scheduled_work();
540         input_unregister_handler(&hook_handler);
541         input_unregister_device(priv_data->acpi_input_dev);
542         
543         mutex_lock(&backend->mutex);
544         kfree(backend->data);
545         backend->data = NULL;
546         mutex_unlock(&backend->mutex);
547         mutex_destroy(&backend->mutex);
548 }
549
550 static void omnibook_acpi_exit(const struct omnibook_operation *io_op)
551 {
552         dprintk("Trying to dispose ACPI backend\n");
553         kref_put(&io_op->backend->kref, omnibook_acpi_free);
554 }
555
556 /* forward declaration */
557 struct omnibook_backend acpi_backend;
558
559 /* Function taken from toshiba_acpi */
560 static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS])
561 {
562         struct acpi_backend_data *priv_data = acpi_backend.data;
563         struct acpi_object_list params;
564         union acpi_object in_objs[HCI_WORDS];
565         struct acpi_buffer results;
566         union acpi_object out_objs[HCI_WORDS + 1];
567         acpi_status status;
568         int i;
569
570         params.count = HCI_WORDS;
571         params.pointer = in_objs;
572         for (i = 0; i < HCI_WORDS; ++i) {
573                 in_objs[i].type = ACPI_TYPE_INTEGER;
574                 in_objs[i].integer.value = in[i];
575         }
576
577         results.length = sizeof(out_objs);
578         results.pointer = out_objs;
579
580         status = acpi_evaluate_object(priv_data->hci_handle, (char *)HCI_METHOD, &params,
581                                       &results);
582         if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) {
583                 for (i = 0; i < out_objs->package.count; ++i) {
584                         out[i] = out_objs->package.elements[i].integer.value;
585                 }
586         }
587
588         return status;
589 }
590
591 /*
592  * Set Bluetooth device state using the Toshiba BT device
593  */
594 static int set_bt_status(const struct acpi_backend_data *priv_data, unsigned int state)
595 {
596         int retval = 0;
597
598         if (state) {
599                 retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_ACTIVATE_USB, NULL, NULL);
600                 if (retval)
601                         goto out;
602                 retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_POWER_ON, NULL, NULL);
603                 if (retval)
604                         goto out;
605         } else {
606                 retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_DISABLE_USB, NULL, NULL);
607                 if (retval)
608                         goto out;
609                 retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_POWER_OFF, NULL, NULL);
610                 if (retval)
611                         goto out;
612         }
613         out:
614         return retval;
615 }
616
617 static int omnibook_acpi_bt_add(struct acpi_device *device)
618 {
619         int retval;
620         struct acpi_backend_data *priv_data = acpi_backend.data;
621         
622         dprintk("Enabling Toshiba Bluetooth ACPI device.\n");
623         strcpy(acpi_device_name(device), TOSHIBA_ACPI_DEVICE_NAME);
624         strcpy(acpi_device_class(device), TOSHIBA_ACPI_BT_CLASS);
625
626         /* Save handle in backend private data structure. ugly. */
627
628         mutex_lock(&acpi_backend.mutex);
629         priv_data->bt_handle = device->handle;
630         retval = set_bt_status(priv_data, 1);
631         mutex_unlock(&acpi_backend.mutex);
632
633         return retval;
634 }
635
636 static int omnibook_acpi_bt_remove(struct acpi_device *device, int type)
637 {
638         int retval;
639         struct acpi_backend_data *priv_data = acpi_backend.data;
640
641         mutex_lock(&acpi_backend.mutex);
642         dprintk("Disabling Toshiba Bluetooth ACPI device.\n");
643         retval = set_bt_status(priv_data, 0);
644         priv_data->bt_handle = NULL;
645         mutex_unlock(&acpi_backend.mutex);
646         
647         return retval;
648 }
649
650 /*
651  * Get Bluetooth status using the BTST method
652  */
653 static int get_bt_status(const struct acpi_backend_data *priv_data, unsigned int *state)
654 {
655         int retval = 0;
656         int raw_state;
657
658         if ((retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_STATUS, NULL, &raw_state)))
659                 return retval;
660
661         dprintk("BTST raw_state: %x\n", raw_state);
662
663         *state = BT_EX;
664         *state |= ((raw_state & TOSH_BT_USB_MASK) && (raw_state & TOSH_BT_POWER_MASK)) ? BT_STA : 0;
665
666         return retval;
667 }
668
669 /*
670  * Get the Bluetooth + Wireless status using the ANTR method
671  * FIXME: what if ANTR and BTST disagree ? we thrust ANTR for now
672  */
673 static int get_wireless_status(const struct acpi_backend_data *priv_data, unsigned int *state)
674 {
675         int retval = 0;
676         int raw_state;
677
678         if ((retval = omnibook_acpi_execute(priv_data->ec_handle, GET_WIRELESS_METHOD, NULL, &raw_state)))
679                 return retval;
680
681         dprintk("get_wireless raw_state: %x\n", raw_state);
682
683         *state = (raw_state & WLEX_MASK) ? WIFI_EX : 0;
684         *state |= (raw_state & WLAT_MASK) ? WIFI_STA : 0;
685         *state |= (raw_state & KLSW_MASK) ? KILLSWITCH : 0;
686         *state |= (raw_state & BTEX_MASK) ? BT_EX : 0;
687         *state |= (raw_state & BTAT_MASK) ? BT_STA : 0;
688
689         return retval;
690 }
691
692 static int get_tsx205_wireless_status(const struct acpi_backend_data *priv_data, unsigned int *state)
693 {
694         int retval = 0;
695         int raw_state;
696         u32 in[HCI_WORDS] = { HCI_GET, HCI_RF_CONTROL, 0, HCI_WIRELESS_CHECK, 0, 0 };
697         u32 out[HCI_WORDS];
698
699         hci_raw(in, out);
700
701         /* Now let's check the killswitch */
702         if ((retval = omnibook_acpi_execute(priv_data->ec_handle, TSX205_KILLSW_METHOD, NULL, &raw_state)))
703                 return retval;
704
705         dprintk("get_wireless raw_state: %x\n", out[2]);
706
707         *state = ((out[2] & 0xff)) ? WIFI_EX : 0;
708         *state |= (raw_state) ? WIFI_STA : 0;
709         *state |= (!raw_state) ? KILLSWITCH : 0;
710
711         /* And finally BT */
712         if ((retval = omnibook_acpi_execute(priv_data->bt_handle, TOSH_BT_STATUS, NULL, &raw_state)))
713                 return retval;
714         
715         *state |= BT_EX;
716         *state |= ((raw_state & TOSH_BT_USB_MASK) && (raw_state & TOSH_BT_POWER_MASK)) ? BT_STA : 0;
717
718         return retval;
719 }
720
721 static int omnibook_acpi_get_wireless(const struct omnibook_operation *io_op, unsigned int *state)
722 {
723         int retval;
724         struct acpi_backend_data *priv_data = io_op->backend->data;
725
726         /* use BTST (BT device) if we don't have ANTR/ANTW (EC device) */
727         if (omnibook_ectype & TSX205)
728                 retval = get_tsx205_wireless_status(priv_data, state);
729         else if (priv_data->has_antr_antw)
730                 retval = get_wireless_status(priv_data, state);
731         else if(priv_data->bt_handle)
732                 retval = get_bt_status(priv_data, state);
733         else
734                 retval = -ENODEV;
735
736         return retval;
737 }
738
739 /*
740  * Set the Bluetooth + Wireless status using the ANTW method
741  */
742 static int set_wireless_status(const struct acpi_backend_data *priv_data, unsigned int state)
743 {
744         int retval;
745         int raw_state;
746
747         raw_state = !!(state & WIFI_STA);       /* bit 0 */
748         raw_state |= !!(state & BT_STA) << 0x1; /* bit 1 */
749
750         dprintk("set_wireless raw_state: %x\n", raw_state);
751
752         retval = omnibook_acpi_execute(priv_data->ec_handle, SET_WIRELESS_METHOD, &raw_state, NULL);
753
754         return retval;
755 }
756
757 static int set_tsx205_wireless_status(const struct acpi_backend_data *priv_data, unsigned int state)
758 {
759         int retval;
760         int raw_state = !!(state & WIFI_STA);
761         u32 in[HCI_WORDS] = { HCI_SET, HCI_RF_CONTROL, raw_state, HCI_WIRELESS_POWER, 0, 0 };
762         u32 out[HCI_WORDS];
763
764         dprintk("set_wireless raw_state: %x\n", raw_state);
765
766         hci_raw(in, out);
767
768         raw_state |= !!(state & BT_STA) << 0x1; /* bit 1 */
769
770         /* BT status */
771         retval = set_bt_status(priv_data->bt_handle, state);
772
773         return retval;
774 }
775
776 static int omnibook_acpi_set_wireless(const struct omnibook_operation *io_op, unsigned int state)
777 {
778         int retval = -ENODEV;
779         struct acpi_backend_data *priv_data = io_op->backend->data;
780
781         /* First try the TSX205 methods */
782         if(omnibook_ectype & TSX205)
783                 retval = set_tsx205_wireless_status(priv_data, state);
784
785         /* Then try the ANTR/ANTW methods */
786         if(priv_data->has_antr_antw)
787                 retval = set_wireless_status(priv_data, state);
788         
789         /* Then try the bluetooth ACPI device if present */
790         if(priv_data->bt_handle)
791                 retval = set_bt_status(priv_data, (state & BT_STA));
792
793         return retval;
794 }
795
796 static int tsx205_get_display(const struct acpi_backend_data *priv_data, unsigned int *state, unsigned int device)
797 {
798         int retval = 0;
799         int raw_state = 0;
800
801         retval = omnibook_acpi_execute(priv_data->dis_handle, tsx205_video_list[device], NULL, &raw_state);
802         if (retval < 0) {
803                 dprintk(O_ERR "Failed to get video device (%d) state.\n", device);
804                 return retval;
805         }
806
807         /* Ugly, but better than nothing... */
808         switch (device) {
809         case 0:
810         case 4: /* LCD device */
811                 dprintk("get_display LCD (%d) raw_state: %x\n", device, raw_state);
812                 if (raw_state == TSX205_DSPY_DE) {
813                         *state |= DISPLAY_LCD_DET;
814                         *state |= DISPLAY_LCD_ON;
815                 } else
816                 if (raw_state == TSX205_DSPY_DN)
817                         *state |= DISPLAY_LCD_DET;
818                 else if (raw_state == TSX205_DSPY_NE)
819                         *state |= DISPLAY_LCD_ON;
820                 break;
821         case 1:
822         case 5: /* CRT device */
823                 dprintk("get_display CRT (%d) raw_state: %x\n", device, raw_state);
824                 if (raw_state == TSX205_DSPY_DE) {
825                         *state |= DISPLAY_CRT_DET;
826                         *state |= DISPLAY_CRT_ON;
827                 } else
828                 if (raw_state == TSX205_DSPY_DN)
829                         *state |= DISPLAY_CRT_DET;
830                 else if (raw_state == TSX205_DSPY_NE)
831                         *state |= DISPLAY_CRT_ON;
832                 break;
833         case 2:
834         case 6: /* TV-OUT device */
835                 dprintk("get_display TV-OUT (%d) raw_state: %x\n", device, raw_state);
836                 if (raw_state == TSX205_DSPY_DE) {
837                         *state |= DISPLAY_TVO_DET;
838                         *state |= DISPLAY_TVO_ON;
839                 } else
840                 if (raw_state == TSX205_DSPY_DN)
841                         *state |= DISPLAY_TVO_DET;
842                 else if (raw_state == TSX205_DSPY_NE)
843                         *state |= DISPLAY_TVO_ON;
844                 break;
845         case 3:
846         case 7: /* DVI device */
847                 dprintk("get_display DVI (%d) raw_state: %x\n", device, raw_state);
848                 if (raw_state == TSX205_DSPY_DE) {
849                         *state |= DISPLAY_DVI_DET;
850                         *state |= DISPLAY_DVI_ON;
851                 } else
852                 if (raw_state == TSX205_DSPY_DN)
853                         *state |= DISPLAY_DVI_DET;
854                 else if (raw_state == TSX205_DSPY_NE)
855                         *state |= DISPLAY_DVI_ON;
856                 break;
857         }
858
859         return retval;
860 }
861
862 static int omnibook_acpi_get_display(const struct omnibook_operation *io_op, unsigned int *state)
863 {
864         int retval = 0;
865         int raw_state = 0;
866         struct acpi_backend_data *priv_data = io_op->backend->data;
867         
868         if(!priv_data->has_doss_dosw)
869                 return -ENODEV;
870
871         if (omnibook_ectype & TSX205) {
872                 int i;
873
874                 /* Loop 'tru the different Video-Out devices */
875                 if (priv_data->has_sli)
876                         for (i = 4; i < ARRAY_SIZE(tsx205_video_list); i++)
877                                 retval = tsx205_get_display(priv_data, state, i);
878                 else
879                         for (i = 0; i < 4; i++)
880                                 retval = tsx205_get_display(priv_data, state, i);
881
882                 if (retval < 0)
883                         return -EIO;
884         
885                 goto vidout;
886         }
887
888         retval = omnibook_acpi_execute(priv_data->ec_handle, GET_DISPLAY_METHOD, NULL, &raw_state);
889         if (retval < 0)
890                 return retval;
891
892         dprintk("get_display raw_state: %x\n", raw_state);
893
894         /* Backend specific to backend-neutral conversion */
895         *state = (raw_state & LCD_CSTE) ? DISPLAY_LCD_ON : 0;
896         *state |= (raw_state & CRT_CSTE) ? DISPLAY_CRT_ON : 0;
897         *state |= (raw_state & TVO_CSTE) ? DISPLAY_TVO_ON : 0;
898         *state |= (raw_state & DVI_CSTE) ? DISPLAY_DVI_ON : 0;
899
900         *state |= (raw_state & LCD_CADL) ? DISPLAY_LCD_DET : 0;
901         *state |= (raw_state & CRT_CADL) ? DISPLAY_CRT_DET : 0;
902         *state |= (raw_state & TVO_CADL) ? DISPLAY_TVO_DET : 0;
903         *state |= (raw_state & DVI_CADL) ? DISPLAY_DVI_DET : 0;
904
905 vidout:
906         return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON | DISPLAY_DVI_ON
907             | DISPLAY_LCD_DET | DISPLAY_CRT_DET | DISPLAY_TVO_DET | DISPLAY_DVI_DET;
908 }
909
910 static const unsigned int acpi_display_mode_list[] = {
911         DISPLAY_LCD_ON,
912         DISPLAY_CRT_ON,
913         DISPLAY_LCD_ON | DISPLAY_CRT_ON,
914         DISPLAY_TVO_ON,
915         DISPLAY_LCD_ON | DISPLAY_TVO_ON,
916         DISPLAY_CRT_ON | DISPLAY_TVO_ON,
917         DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON,
918         DISPLAY_DVI_ON,
919         DISPLAY_LCD_ON | DISPLAY_DVI_ON,
920 };
921
922 static int omnibook_acpi_set_display(const struct omnibook_operation *io_op, unsigned int state)
923 {
924         int retval = 0;
925         int i; 
926         int matched = -1;
927         struct acpi_backend_data *priv_data = io_op->backend->data;
928
929         if(!priv_data->has_doss_dosw)
930                 return -ENODEV;
931
932         for (i = 0; i < ARRAY_SIZE(acpi_display_mode_list); i++) {
933                 if (acpi_display_mode_list[i] == state) {
934                         matched = i + 1;        /* raw state is array row number + 1 */
935                         break;
936                 }
937         }
938         if (matched == -1) {
939                 printk("Display mode %x is unsupported.\n", state);
940                 return -EINVAL;
941         }
942
943         dprintk("set_display raw_state: %x\n", matched);
944
945         if (omnibook_ectype & TSX205) {
946                 if (priv_data->has_sli)
947                         retval = omnibook_acpi_execute(priv_data->dis_handle, TSX205_SLI_DISPLAY_METHOD, &matched, NULL);
948                 else
949                         retval = omnibook_acpi_execute(priv_data->dis_handle, TSX205_SET_DISPLAY_METHOD, &matched, NULL);
950         } else
951                 retval = omnibook_acpi_execute(priv_data->ec_handle, SET_DISPLAY_METHOD, &matched, NULL);
952         if (retval < 0)
953                 return retval;
954
955         return DISPLAY_LCD_ON | DISPLAY_CRT_ON | DISPLAY_TVO_ON | DISPLAY_DVI_ON;
956 }
957
958 static int omnibook_acpi_get_throttle(const struct omnibook_operation *io_op, unsigned int *state)
959 {
960         int retval;
961         int thtl_en = 0, thtl_dty = 0;
962         int param;
963         struct acpi_backend_data *priv_data = io_op->backend->data;
964         
965         param = 0;
966         /* Read THEN aka THTL_EN in ICH6M datasheets */
967         retval = omnibook_acpi_execute(priv_data->ec_handle, GET_THROTTLE_METHOD, &param, &thtl_en); 
968         if ( thtl_en == 0 ) {
969                 *state = 0;
970                 return retval;
971         }
972         param = 1;
973         /* Read DUTY aka THTL_DTY in ICH6M datasheets */
974         retval = omnibook_acpi_execute(priv_data->ec_handle, GET_THROTTLE_METHOD, &param, &thtl_dty);
975         WARN_ON(thtl_dty > 7); /* We shouldn't encounter more than 7 throttling level */
976         *state = 8 - thtl_dty; /* THTL_DTY and ACPI T-state are reverse mapped */
977         return retval;
978 }
979
980 static int omnibook_acpi_set_throttle(const struct omnibook_operation *io_op, unsigned int state)
981 {
982         struct acpi_backend_data *priv_data = io_op->backend->data;
983         /* THTL_DTY and ACPI T-state are reverse mapped */
984         /* throttling.c already clamped state between 0 and 7 */
985         if (state) 
986                 state = 8 - state;
987
988         return omnibook_acpi_execute(priv_data->ec_handle, SET_THROTTLE_METHOD, &state, NULL);
989 }
990
991 /*
992  * Fn+foo hotkeys handling
993  */
994 static int omnibook_hci_get_hotkeys(const struct omnibook_operation *io_op, unsigned int *state)
995 {
996         u32 in[HCI_WORDS] = { HCI_GET, HCI_HOTKEY_EVENT, 0, 0, 0, 0 };
997         u32 out[HCI_WORDS];
998         acpi_status status = hci_raw(in, out);
999
1000         if (status != AE_OK)
1001                 return HCI_FAILURE;
1002
1003         dprintk("get_hotkeys raw_state: %x\n", out[2]);
1004
1005         *state = (out[2] & ACPI_FN_MASK) ? HKEY_FN : 0;
1006
1007         return 0;
1008 }
1009
1010 static int omnibook_hci_set_hotkeys(const struct omnibook_operation *io_op, unsigned int state)
1011 {
1012         u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
1013         u32 out[HCI_WORDS];
1014         acpi_status status;
1015         in[0] = HCI_SET;
1016         in[1] = HCI_HOTKEY_EVENT;
1017         in[2] = (state & HKEY_FN) ? 1 : 0;
1018
1019         status = hci_raw(in, out);
1020
1021         dprintk("set_hotkeys (Fn interface) raw_state: %x\n", in[2]);
1022
1023         return (status == AE_OK) ? out[0] : HCI_FAILURE;
1024 }
1025
1026 static int omnibook_acpi_get_events(unsigned int *state)
1027 {
1028         acpi_status status;
1029         struct acpi_backend_data *priv_data = acpi_backend.data;
1030   
1031         /* We need to call the NTFY method first so it can activate the TECF variable */
1032         status = omnibook_acpi_execute(priv_data->ec_handle, TSX205_NOTIFY_METHOD, NULL, NULL);
1033         if (status != AE_OK) {
1034                 dprintk(O_ERR "Failed to activate NTFY method.\n");
1035                 return -EIO;
1036         }
1037
1038         /* Now we can poll the INFO method to get last pressed hotkey */
1039         status = omnibook_acpi_execute(priv_data->hci_handle, TSX205_EVENTS_METHOD, NULL, state);
1040         if (status != AE_OK) {
1041                 dprintk(O_ERR "Failed to get Hotkey event.\n");
1042                 return -EIO;
1043         }
1044
1045         /* We only care about a key press, so just report the Fn key Press/Release */
1046         if ( ((*state & ~0x80) == 0x100) || ((*state & ~0x80) == 0x17f) )
1047                 *state &= ~0x80;
1048
1049         return status;
1050 }
1051
1052 /*
1053  * Adjust the lcd backlight level by delta.
1054  * Used for Fn+F6/F7 keypress
1055  */
1056 static int adjust_brighness(int delta)
1057 {
1058         struct omnibook_feature *lcd_feature = omnibook_find_feature("lcd");
1059         struct omnibook_operation *io_op;
1060         int retval = 0;
1061         u8 brgt;
1062
1063         if(!lcd_feature)
1064                 return -ENODEV;
1065
1066         io_op = lcd_feature->io_op;
1067
1068         mutex_lock(&io_op->backend->mutex);
1069
1070         if(( retval = __backend_byte_read(io_op, &brgt)))
1071                 goto out;       
1072
1073         dprintk("Fn-F6/F7 pressed: adjusting brightness.\n");
1074
1075         if (((int) brgt + delta) < 0)
1076                 brgt = 0;
1077         else if ((brgt + delta) > omnibook_max_brightness)
1078                 brgt = omnibook_max_brightness;
1079         else
1080                 brgt += delta;
1081
1082         retval = __backend_byte_write(io_op, brgt);
1083
1084         out:
1085         mutex_unlock(&io_op->backend->mutex);
1086         return retval;
1087 }
1088
1089 /*
1090  * Workqueue handler for Fn hotkeys
1091  */
1092 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
1093 static void omnibook_handle_fnkey(struct work_struct *work)
1094 #else
1095 static void omnibook_handle_fnkey(void* data)
1096 #endif
1097 {
1098         int i;
1099         u32 gen_scan;
1100         struct input_dev *input_dev;
1101         acpi_status status;
1102
1103         status = omnibook_acpi_get_events(&gen_scan);
1104         if (status != AE_OK)
1105                 return;
1106
1107         dprintk("detected scancode 0x%x.\n", gen_scan);
1108         switch(gen_scan) {
1109         case HCI_BRIGHTNESSDOWN:
1110                 adjust_brighness(-1);
1111                 break;
1112         case HCI_BRIGHTNESSUP:
1113                 adjust_brighness(+1);
1114                 break;
1115         }
1116
1117         for (i = 0 ; i < ARRAY_SIZE(acpi_scan_table); i++) {
1118                 if (gen_scan == acpi_scan_table[i].scancode) {
1119                         dprintk("generating keycode %i.\n", acpi_scan_table[i].keycode);
1120 #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19))
1121                         input_dev = container_of(work, struct acpi_backend_data, fnkey_work)->acpi_input_dev;
1122 #else
1123                         input_dev = ((struct acpi_backend_data *) data)->acpi_input_dev;
1124 #endif
1125                         omnibook_report_key(input_dev, acpi_scan_table[i].keycode);
1126                         break;
1127                 }
1128         }
1129 }
1130
1131 struct omnibook_backend acpi_backend = {
1132         .name = "acpi",
1133         .hotkeys_read_cap = HKEY_FN,
1134         .hotkeys_write_cap = HKEY_FN,
1135         .init = omnibook_acpi_init,
1136         .exit = omnibook_acpi_exit,
1137         .aerial_get = omnibook_acpi_get_wireless,
1138         .aerial_set = omnibook_acpi_set_wireless,
1139         .display_get = omnibook_acpi_get_display,
1140         .display_set = omnibook_acpi_set_display,
1141         .throttle_get = omnibook_acpi_get_throttle,
1142         .throttle_set = omnibook_acpi_set_throttle,
1143         .hotkeys_get = omnibook_hci_get_hotkeys,
1144         .hotkeys_set = omnibook_hci_set_hotkeys,
1145 };
1146
1147 #else                           /* CONFIG_ACPI */
1148
1149 /* dummy backend for non-ACPI systems */
1150 static int _fail_probe(const struct omnibook_operation *io_op)
1151 {
1152         return -ENODEV;
1153 }
1154
1155 struct omnibook_backend acpi_backend = {
1156         .name = "acpi",
1157         .init = _fail_probe,
1158 };
1159
1160 #endif                          /* CONFIG_ACPI */