1b4f9fe12SLen Brown /* 2b4f9fe12SLen Brown * toshiba_acpi.c - Toshiba Laptop ACPI Extras 3b4f9fe12SLen Brown * 4b4f9fe12SLen Brown * 5b4f9fe12SLen Brown * Copyright (C) 2002-2004 John Belmonte 6b4f9fe12SLen Brown * Copyright (C) 2008 Philip Langdale 76c3f6e6cSPierre Ducroquet * Copyright (C) 2010 Pierre Ducroquet 8b4f9fe12SLen Brown * 9b4f9fe12SLen Brown * This program is free software; you can redistribute it and/or modify 10b4f9fe12SLen Brown * it under the terms of the GNU General Public License as published by 11b4f9fe12SLen Brown * the Free Software Foundation; either version 2 of the License, or 12b4f9fe12SLen Brown * (at your option) any later version. 13b4f9fe12SLen Brown * 14b4f9fe12SLen Brown * This program is distributed in the hope that it will be useful, 15b4f9fe12SLen Brown * but WITHOUT ANY WARRANTY; without even the implied warranty of 16b4f9fe12SLen Brown * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17b4f9fe12SLen Brown * GNU General Public License for more details. 18b4f9fe12SLen Brown * 19b4f9fe12SLen Brown * You should have received a copy of the GNU General Public License 20b4f9fe12SLen Brown * along with this program; if not, write to the Free Software 21b4f9fe12SLen Brown * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22b4f9fe12SLen Brown * 23b4f9fe12SLen Brown * 24b4f9fe12SLen Brown * The devolpment page for this driver is located at 25b4f9fe12SLen Brown * http://memebeam.org/toys/ToshibaAcpiDriver. 26b4f9fe12SLen Brown * 27b4f9fe12SLen Brown * Credits: 28b4f9fe12SLen Brown * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse 29b4f9fe12SLen Brown * engineering the Windows drivers 30b4f9fe12SLen Brown * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 31b4f9fe12SLen Brown * Rob Miller - TV out and hotkeys help 32b4f9fe12SLen Brown * 33b4f9fe12SLen Brown * 34b4f9fe12SLen Brown * TODO 35b4f9fe12SLen Brown * 36b4f9fe12SLen Brown */ 37b4f9fe12SLen Brown 387e33460dSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 397e33460dSJoe Perches 40b4f9fe12SLen Brown #define TOSHIBA_ACPI_VERSION "0.19" 41b4f9fe12SLen Brown #define PROC_INTERFACE_VERSION 1 42b4f9fe12SLen Brown 43b4f9fe12SLen Brown #include <linux/kernel.h> 44b4f9fe12SLen Brown #include <linux/module.h> 45b4f9fe12SLen Brown #include <linux/init.h> 46b4f9fe12SLen Brown #include <linux/types.h> 47b4f9fe12SLen Brown #include <linux/proc_fs.h> 48936c8bcdSAlexey Dobriyan #include <linux/seq_file.h> 49b4f9fe12SLen Brown #include <linux/backlight.h> 50b4f9fe12SLen Brown #include <linux/rfkill.h> 516335e4d5SMatthew Garrett #include <linux/input.h> 52384a7cd9SDmitry Torokhov #include <linux/input/sparse-keymap.h> 536c3f6e6cSPierre Ducroquet #include <linux/leds.h> 545a0e3ad6STejun Heo #include <linux/slab.h> 55b4f9fe12SLen Brown 56b4f9fe12SLen Brown #include <asm/uaccess.h> 57b4f9fe12SLen Brown 58b4f9fe12SLen Brown #include <acpi/acpi_drivers.h> 59b4f9fe12SLen Brown 60b4f9fe12SLen Brown MODULE_AUTHOR("John Belmonte"); 61b4f9fe12SLen Brown MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); 62b4f9fe12SLen Brown MODULE_LICENSE("GPL"); 63b4f9fe12SLen Brown 64b4f9fe12SLen Brown /* Toshiba ACPI method paths */ 65b4f9fe12SLen Brown #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" 666335e4d5SMatthew Garrett #define TOSH_INTERFACE_1 "\\_SB_.VALD" 676335e4d5SMatthew Garrett #define TOSH_INTERFACE_2 "\\_SB_.VALZ" 68b4f9fe12SLen Brown #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" 696335e4d5SMatthew Garrett #define GHCI_METHOD ".GHCI" 70b4f9fe12SLen Brown 71b4f9fe12SLen Brown /* Toshiba HCI interface definitions 72b4f9fe12SLen Brown * 73b4f9fe12SLen Brown * HCI is Toshiba's "Hardware Control Interface" which is supposed to 74b4f9fe12SLen Brown * be uniform across all their models. Ideally we would just call 75b4f9fe12SLen Brown * dedicated ACPI methods instead of using this primitive interface. 76b4f9fe12SLen Brown * However the ACPI methods seem to be incomplete in some areas (for 77b4f9fe12SLen Brown * example they allow setting, but not reading, the LCD brightness value), 78b4f9fe12SLen Brown * so this is still useful. 79b4f9fe12SLen Brown */ 80b4f9fe12SLen Brown 81b4f9fe12SLen Brown #define HCI_WORDS 6 82b4f9fe12SLen Brown 83b4f9fe12SLen Brown /* operations */ 84b4f9fe12SLen Brown #define HCI_SET 0xff00 85b4f9fe12SLen Brown #define HCI_GET 0xfe00 86b4f9fe12SLen Brown 87b4f9fe12SLen Brown /* return codes */ 88b4f9fe12SLen Brown #define HCI_SUCCESS 0x0000 89b4f9fe12SLen Brown #define HCI_FAILURE 0x1000 90b4f9fe12SLen Brown #define HCI_NOT_SUPPORTED 0x8000 91b4f9fe12SLen Brown #define HCI_EMPTY 0x8c00 92b4f9fe12SLen Brown 93b4f9fe12SLen Brown /* registers */ 94b4f9fe12SLen Brown #define HCI_FAN 0x0004 95b4f9fe12SLen Brown #define HCI_SYSTEM_EVENT 0x0016 96b4f9fe12SLen Brown #define HCI_VIDEO_OUT 0x001c 97b4f9fe12SLen Brown #define HCI_HOTKEY_EVENT 0x001e 98b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS 0x002a 99b4f9fe12SLen Brown #define HCI_WIRELESS 0x0056 100b4f9fe12SLen Brown 101b4f9fe12SLen Brown /* field definitions */ 102b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_BITS 3 103b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) 104b4f9fe12SLen Brown #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) 105b4f9fe12SLen Brown #define HCI_VIDEO_OUT_LCD 0x1 106b4f9fe12SLen Brown #define HCI_VIDEO_OUT_CRT 0x2 107b4f9fe12SLen Brown #define HCI_VIDEO_OUT_TV 0x4 108b4f9fe12SLen Brown #define HCI_WIRELESS_KILL_SWITCH 0x01 109b4f9fe12SLen Brown #define HCI_WIRELESS_BT_PRESENT 0x0f 110b4f9fe12SLen Brown #define HCI_WIRELESS_BT_ATTACH 0x40 111b4f9fe12SLen Brown #define HCI_WIRELESS_BT_POWER 0x80 112b4f9fe12SLen Brown 113135740deSSeth Forshee struct toshiba_acpi_dev { 114135740deSSeth Forshee struct acpi_device *acpi_dev; 115135740deSSeth Forshee const char *method_hci; 116135740deSSeth Forshee struct rfkill *bt_rfk; 117135740deSSeth Forshee struct input_dev *hotkey_dev; 118135740deSSeth Forshee struct backlight_device *backlight_dev; 119135740deSSeth Forshee struct led_classdev led_dev; 120135740deSSeth Forshee int illumination_installed; 121135740deSSeth Forshee int force_fan; 122135740deSSeth Forshee int last_key_event; 123135740deSSeth Forshee int key_event_valid; 124135740deSSeth Forshee acpi_handle handle; 125135740deSSeth Forshee 126135740deSSeth Forshee struct mutex mutex; 127135740deSSeth Forshee }; 128135740deSSeth Forshee 129b4f9fe12SLen Brown static const struct acpi_device_id toshiba_device_ids[] = { 130b4f9fe12SLen Brown {"TOS6200", 0}, 131b4f9fe12SLen Brown {"TOS6208", 0}, 132b4f9fe12SLen Brown {"TOS1900", 0}, 133b4f9fe12SLen Brown {"", 0}, 134b4f9fe12SLen Brown }; 135b4f9fe12SLen Brown MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); 136b4f9fe12SLen Brown 137135740deSSeth Forshee static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { 138384a7cd9SDmitry Torokhov { KE_KEY, 0x101, { KEY_MUTE } }, 139384a7cd9SDmitry Torokhov { KE_KEY, 0x102, { KEY_ZOOMOUT } }, 140384a7cd9SDmitry Torokhov { KE_KEY, 0x103, { KEY_ZOOMIN } }, 141384a7cd9SDmitry Torokhov { KE_KEY, 0x13b, { KEY_COFFEE } }, 142384a7cd9SDmitry Torokhov { KE_KEY, 0x13c, { KEY_BATTERY } }, 143384a7cd9SDmitry Torokhov { KE_KEY, 0x13d, { KEY_SLEEP } }, 144384a7cd9SDmitry Torokhov { KE_KEY, 0x13e, { KEY_SUSPEND } }, 145384a7cd9SDmitry Torokhov { KE_KEY, 0x13f, { KEY_SWITCHVIDEOMODE } }, 146384a7cd9SDmitry Torokhov { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, 147384a7cd9SDmitry Torokhov { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, 148384a7cd9SDmitry Torokhov { KE_KEY, 0x142, { KEY_WLAN } }, 149384a7cd9SDmitry Torokhov { KE_KEY, 0x143, { KEY_PROG1 } }, 150a49010f5SJon Dowland { KE_KEY, 0x17f, { KEY_FN } }, 151384a7cd9SDmitry Torokhov { KE_KEY, 0xb05, { KEY_PROG2 } }, 152384a7cd9SDmitry Torokhov { KE_KEY, 0xb06, { KEY_WWW } }, 153384a7cd9SDmitry Torokhov { KE_KEY, 0xb07, { KEY_MAIL } }, 154384a7cd9SDmitry Torokhov { KE_KEY, 0xb30, { KEY_STOP } }, 155384a7cd9SDmitry Torokhov { KE_KEY, 0xb31, { KEY_PREVIOUSSONG } }, 156384a7cd9SDmitry Torokhov { KE_KEY, 0xb32, { KEY_NEXTSONG } }, 157384a7cd9SDmitry Torokhov { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, 158384a7cd9SDmitry Torokhov { KE_KEY, 0xb5a, { KEY_MEDIA } }, 159384a7cd9SDmitry Torokhov { KE_END, 0 }, 1606335e4d5SMatthew Garrett }; 1616335e4d5SMatthew Garrett 162b4f9fe12SLen Brown /* utility 163b4f9fe12SLen Brown */ 164b4f9fe12SLen Brown 165b4f9fe12SLen Brown static __inline__ void _set_bit(u32 * word, u32 mask, int value) 166b4f9fe12SLen Brown { 167b4f9fe12SLen Brown *word = (*word & ~mask) | (mask * value); 168b4f9fe12SLen Brown } 169b4f9fe12SLen Brown 170b4f9fe12SLen Brown /* acpi interface wrappers 171b4f9fe12SLen Brown */ 172b4f9fe12SLen Brown 173b4f9fe12SLen Brown static int is_valid_acpi_path(const char *methodName) 174b4f9fe12SLen Brown { 175b4f9fe12SLen Brown acpi_handle handle; 176b4f9fe12SLen Brown acpi_status status; 177b4f9fe12SLen Brown 178b4f9fe12SLen Brown status = acpi_get_handle(NULL, (char *)methodName, &handle); 179b4f9fe12SLen Brown return !ACPI_FAILURE(status); 180b4f9fe12SLen Brown } 181b4f9fe12SLen Brown 182b4f9fe12SLen Brown static int write_acpi_int(const char *methodName, int val) 183b4f9fe12SLen Brown { 184b4f9fe12SLen Brown struct acpi_object_list params; 185b4f9fe12SLen Brown union acpi_object in_objs[1]; 186b4f9fe12SLen Brown acpi_status status; 187b4f9fe12SLen Brown 188b4f9fe12SLen Brown params.count = ARRAY_SIZE(in_objs); 189b4f9fe12SLen Brown params.pointer = in_objs; 190b4f9fe12SLen Brown in_objs[0].type = ACPI_TYPE_INTEGER; 191b4f9fe12SLen Brown in_objs[0].integer.value = val; 192b4f9fe12SLen Brown 193b4f9fe12SLen Brown status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); 194*32bcd5cbSSeth Forshee return (status == AE_OK) ? 0 : -EIO; 195b4f9fe12SLen Brown } 196b4f9fe12SLen Brown 197b4f9fe12SLen Brown /* Perform a raw HCI call. Here we don't care about input or output buffer 198b4f9fe12SLen Brown * format. 199b4f9fe12SLen Brown */ 200135740deSSeth Forshee static acpi_status hci_raw(struct toshiba_acpi_dev *dev, 201135740deSSeth Forshee const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) 202b4f9fe12SLen Brown { 203b4f9fe12SLen Brown struct acpi_object_list params; 204b4f9fe12SLen Brown union acpi_object in_objs[HCI_WORDS]; 205b4f9fe12SLen Brown struct acpi_buffer results; 206b4f9fe12SLen Brown union acpi_object out_objs[HCI_WORDS + 1]; 207b4f9fe12SLen Brown acpi_status status; 208b4f9fe12SLen Brown int i; 209b4f9fe12SLen Brown 210b4f9fe12SLen Brown params.count = HCI_WORDS; 211b4f9fe12SLen Brown params.pointer = in_objs; 212b4f9fe12SLen Brown for (i = 0; i < HCI_WORDS; ++i) { 213b4f9fe12SLen Brown in_objs[i].type = ACPI_TYPE_INTEGER; 214b4f9fe12SLen Brown in_objs[i].integer.value = in[i]; 215b4f9fe12SLen Brown } 216b4f9fe12SLen Brown 217b4f9fe12SLen Brown results.length = sizeof(out_objs); 218b4f9fe12SLen Brown results.pointer = out_objs; 219b4f9fe12SLen Brown 220135740deSSeth Forshee status = acpi_evaluate_object(NULL, (char *)dev->method_hci, ¶ms, 221b4f9fe12SLen Brown &results); 222b4f9fe12SLen Brown if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { 223b4f9fe12SLen Brown for (i = 0; i < out_objs->package.count; ++i) { 224b4f9fe12SLen Brown out[i] = out_objs->package.elements[i].integer.value; 225b4f9fe12SLen Brown } 226b4f9fe12SLen Brown } 227b4f9fe12SLen Brown 228b4f9fe12SLen Brown return status; 229b4f9fe12SLen Brown } 230b4f9fe12SLen Brown 231b4f9fe12SLen Brown /* common hci tasks (get or set one or two value) 232b4f9fe12SLen Brown * 233b4f9fe12SLen Brown * In addition to the ACPI status, the HCI system returns a result which 234b4f9fe12SLen Brown * may be useful (such as "not supported"). 235b4f9fe12SLen Brown */ 236b4f9fe12SLen Brown 237135740deSSeth Forshee static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, 238135740deSSeth Forshee u32 in1, u32 *result) 239b4f9fe12SLen Brown { 240b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; 241b4f9fe12SLen Brown u32 out[HCI_WORDS]; 242135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 243b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 244b4f9fe12SLen Brown return status; 245b4f9fe12SLen Brown } 246b4f9fe12SLen Brown 247135740deSSeth Forshee static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, 248135740deSSeth Forshee u32 *out1, u32 *result) 249b4f9fe12SLen Brown { 250b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; 251b4f9fe12SLen Brown u32 out[HCI_WORDS]; 252135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 253b4f9fe12SLen Brown *out1 = out[2]; 254b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 255b4f9fe12SLen Brown return status; 256b4f9fe12SLen Brown } 257b4f9fe12SLen Brown 258135740deSSeth Forshee static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, 259135740deSSeth Forshee u32 in1, u32 in2, u32 *result) 260b4f9fe12SLen Brown { 261b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; 262b4f9fe12SLen Brown u32 out[HCI_WORDS]; 263135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 264b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 265b4f9fe12SLen Brown return status; 266b4f9fe12SLen Brown } 267b4f9fe12SLen Brown 268135740deSSeth Forshee static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, 269135740deSSeth Forshee u32 *out1, u32 *out2, u32 *result) 270b4f9fe12SLen Brown { 271b4f9fe12SLen Brown u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; 272b4f9fe12SLen Brown u32 out[HCI_WORDS]; 273135740deSSeth Forshee acpi_status status = hci_raw(dev, in, out); 274b4f9fe12SLen Brown *out1 = out[2]; 275b4f9fe12SLen Brown *out2 = out[3]; 276b4f9fe12SLen Brown *result = (status == AE_OK) ? out[0] : HCI_FAILURE; 277b4f9fe12SLen Brown return status; 278b4f9fe12SLen Brown } 279b4f9fe12SLen Brown 2806c3f6e6cSPierre Ducroquet /* Illumination support */ 281135740deSSeth Forshee static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) 2826c3f6e6cSPierre Ducroquet { 2836c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 2846c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 2856c3f6e6cSPierre Ducroquet acpi_status status; 2866c3f6e6cSPierre Ducroquet 2876c3f6e6cSPierre Ducroquet in[0] = 0xf100; 288135740deSSeth Forshee status = hci_raw(dev, in, out); 2896c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 2907e33460dSJoe Perches pr_info("Illumination device not available\n"); 2916c3f6e6cSPierre Ducroquet return 0; 2926c3f6e6cSPierre Ducroquet } 2936c3f6e6cSPierre Ducroquet in[0] = 0xf400; 294135740deSSeth Forshee status = hci_raw(dev, in, out); 2956c3f6e6cSPierre Ducroquet return 1; 2966c3f6e6cSPierre Ducroquet } 2976c3f6e6cSPierre Ducroquet 2986c3f6e6cSPierre Ducroquet static void toshiba_illumination_set(struct led_classdev *cdev, 2996c3f6e6cSPierre Ducroquet enum led_brightness brightness) 3006c3f6e6cSPierre Ducroquet { 301135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 302135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 3036c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 3046c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 3056c3f6e6cSPierre Ducroquet acpi_status status; 3066c3f6e6cSPierre Ducroquet 3076c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 3086c3f6e6cSPierre Ducroquet in[0] = 0xf100; 309135740deSSeth Forshee status = hci_raw(dev, in, out); 3106c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3117e33460dSJoe Perches pr_info("Illumination device not available\n"); 3126c3f6e6cSPierre Ducroquet return; 3136c3f6e6cSPierre Ducroquet } 3146c3f6e6cSPierre Ducroquet 3156c3f6e6cSPierre Ducroquet if (brightness) { 3166c3f6e6cSPierre Ducroquet /* Switch the illumination on */ 3176c3f6e6cSPierre Ducroquet in[0] = 0xf400; 3186c3f6e6cSPierre Ducroquet in[1] = 0x14e; 3196c3f6e6cSPierre Ducroquet in[2] = 1; 320135740deSSeth Forshee status = hci_raw(dev, in, out); 3216c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3227e33460dSJoe Perches pr_info("ACPI call for illumination failed\n"); 3236c3f6e6cSPierre Ducroquet return; 3246c3f6e6cSPierre Ducroquet } 3256c3f6e6cSPierre Ducroquet } else { 3266c3f6e6cSPierre Ducroquet /* Switch the illumination off */ 3276c3f6e6cSPierre Ducroquet in[0] = 0xf400; 3286c3f6e6cSPierre Ducroquet in[1] = 0x14e; 3296c3f6e6cSPierre Ducroquet in[2] = 0; 330135740deSSeth Forshee status = hci_raw(dev, in, out); 3316c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3327e33460dSJoe Perches pr_info("ACPI call for illumination failed.\n"); 3336c3f6e6cSPierre Ducroquet return; 3346c3f6e6cSPierre Ducroquet } 3356c3f6e6cSPierre Ducroquet } 3366c3f6e6cSPierre Ducroquet 3376c3f6e6cSPierre Ducroquet /* Last request : close communication. */ 3386c3f6e6cSPierre Ducroquet in[0] = 0xf200; 3396c3f6e6cSPierre Ducroquet in[1] = 0; 3406c3f6e6cSPierre Ducroquet in[2] = 0; 341135740deSSeth Forshee hci_raw(dev, in, out); 3426c3f6e6cSPierre Ducroquet } 3436c3f6e6cSPierre Ducroquet 3446c3f6e6cSPierre Ducroquet static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) 3456c3f6e6cSPierre Ducroquet { 346135740deSSeth Forshee struct toshiba_acpi_dev *dev = container_of(cdev, 347135740deSSeth Forshee struct toshiba_acpi_dev, led_dev); 3486c3f6e6cSPierre Ducroquet u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; 3496c3f6e6cSPierre Ducroquet u32 out[HCI_WORDS]; 3506c3f6e6cSPierre Ducroquet acpi_status status; 3516c3f6e6cSPierre Ducroquet enum led_brightness result; 3526c3f6e6cSPierre Ducroquet 3536c3f6e6cSPierre Ducroquet /* First request : initialize communication. */ 3546c3f6e6cSPierre Ducroquet in[0] = 0xf100; 355135740deSSeth Forshee status = hci_raw(dev, in, out); 3566c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3577e33460dSJoe Perches pr_info("Illumination device not available\n"); 3586c3f6e6cSPierre Ducroquet return LED_OFF; 3596c3f6e6cSPierre Ducroquet } 3606c3f6e6cSPierre Ducroquet 3616c3f6e6cSPierre Ducroquet /* Check the illumination */ 3626c3f6e6cSPierre Ducroquet in[0] = 0xf300; 3636c3f6e6cSPierre Ducroquet in[1] = 0x14e; 364135740deSSeth Forshee status = hci_raw(dev, in, out); 3656c3f6e6cSPierre Ducroquet if (ACPI_FAILURE(status)) { 3667e33460dSJoe Perches pr_info("ACPI call for illumination failed.\n"); 3676c3f6e6cSPierre Ducroquet return LED_OFF; 3686c3f6e6cSPierre Ducroquet } 3696c3f6e6cSPierre Ducroquet 3706c3f6e6cSPierre Ducroquet result = out[2] ? LED_FULL : LED_OFF; 3716c3f6e6cSPierre Ducroquet 3726c3f6e6cSPierre Ducroquet /* Last request : close communication. */ 3736c3f6e6cSPierre Ducroquet in[0] = 0xf200; 3746c3f6e6cSPierre Ducroquet in[1] = 0; 3756c3f6e6cSPierre Ducroquet in[2] = 0; 376135740deSSeth Forshee hci_raw(dev, in, out); 3776c3f6e6cSPierre Ducroquet 3786c3f6e6cSPierre Ducroquet return result; 3796c3f6e6cSPierre Ducroquet } 3806c3f6e6cSPierre Ducroquet 381b4f9fe12SLen Brown /* Bluetooth rfkill handlers */ 382b4f9fe12SLen Brown 383135740deSSeth Forshee static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) 384b4f9fe12SLen Brown { 385b4f9fe12SLen Brown u32 hci_result; 386b4f9fe12SLen Brown u32 value, value2; 387b4f9fe12SLen Brown 388b4f9fe12SLen Brown value = 0; 389b4f9fe12SLen Brown value2 = 0; 390135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 391b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) 392b4f9fe12SLen Brown *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; 393b4f9fe12SLen Brown 394b4f9fe12SLen Brown return hci_result; 395b4f9fe12SLen Brown } 396b4f9fe12SLen Brown 397135740deSSeth Forshee static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) 398b4f9fe12SLen Brown { 399b4f9fe12SLen Brown u32 hci_result; 400b4f9fe12SLen Brown u32 value, value2; 401b4f9fe12SLen Brown 402b4f9fe12SLen Brown value = 0; 403b4f9fe12SLen Brown value2 = 0x0001; 404135740deSSeth Forshee hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); 405b4f9fe12SLen Brown 406b4f9fe12SLen Brown *radio_state = value & HCI_WIRELESS_KILL_SWITCH; 407b4f9fe12SLen Brown return hci_result; 408b4f9fe12SLen Brown } 409b4f9fe12SLen Brown 41019d337dfSJohannes Berg static int bt_rfkill_set_block(void *data, bool blocked) 411b4f9fe12SLen Brown { 41219d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 413b4f9fe12SLen Brown u32 result1, result2; 414b4f9fe12SLen Brown u32 value; 41519d337dfSJohannes Berg int err; 416b4f9fe12SLen Brown bool radio_state; 417b4f9fe12SLen Brown 41819d337dfSJohannes Berg value = (blocked == false); 419b4f9fe12SLen Brown 420b4f9fe12SLen Brown mutex_lock(&dev->mutex); 421135740deSSeth Forshee if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { 422*32bcd5cbSSeth Forshee err = -EIO; 42319d337dfSJohannes Berg goto out; 424b4f9fe12SLen Brown } 425b4f9fe12SLen Brown 42619d337dfSJohannes Berg if (!radio_state) { 42719d337dfSJohannes Berg err = 0; 42819d337dfSJohannes Berg goto out; 42919d337dfSJohannes Berg } 43019d337dfSJohannes Berg 431135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); 432135740deSSeth Forshee hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); 43319d337dfSJohannes Berg 43419d337dfSJohannes Berg if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) 435*32bcd5cbSSeth Forshee err = -EIO; 43619d337dfSJohannes Berg else 43719d337dfSJohannes Berg err = 0; 43819d337dfSJohannes Berg out: 43919d337dfSJohannes Berg mutex_unlock(&dev->mutex); 44019d337dfSJohannes Berg return err; 44119d337dfSJohannes Berg } 44219d337dfSJohannes Berg 44319d337dfSJohannes Berg static void bt_rfkill_poll(struct rfkill *rfkill, void *data) 444b4f9fe12SLen Brown { 445b4f9fe12SLen Brown bool new_rfk_state; 446b4f9fe12SLen Brown bool value; 447b4f9fe12SLen Brown u32 hci_result; 44819d337dfSJohannes Berg struct toshiba_acpi_dev *dev = data; 44919d337dfSJohannes Berg 45019d337dfSJohannes Berg mutex_lock(&dev->mutex); 451b4f9fe12SLen Brown 452135740deSSeth Forshee hci_result = hci_get_radio_state(dev, &value); 45319d337dfSJohannes Berg if (hci_result != HCI_SUCCESS) { 45419d337dfSJohannes Berg /* Can't do anything useful */ 45519d337dfSJohannes Berg mutex_unlock(&dev->mutex); 45682e7784fSJiri Slaby return; 45719d337dfSJohannes Berg } 458b4f9fe12SLen Brown 459b4f9fe12SLen Brown new_rfk_state = value; 460b4f9fe12SLen Brown 461b4f9fe12SLen Brown mutex_unlock(&dev->mutex); 462b4f9fe12SLen Brown 46319d337dfSJohannes Berg if (rfkill_set_hw_state(rfkill, !new_rfk_state)) 46419d337dfSJohannes Berg bt_rfkill_set_block(data, true); 465b4f9fe12SLen Brown } 46619d337dfSJohannes Berg 46719d337dfSJohannes Berg static const struct rfkill_ops toshiba_rfk_ops = { 46819d337dfSJohannes Berg .set_block = bt_rfkill_set_block, 46919d337dfSJohannes Berg .poll = bt_rfkill_poll, 47019d337dfSJohannes Berg }; 471b4f9fe12SLen Brown 472b4f9fe12SLen Brown static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; 473b4f9fe12SLen Brown 474b4f9fe12SLen Brown static int get_lcd(struct backlight_device *bd) 475b4f9fe12SLen Brown { 476135740deSSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 477b4f9fe12SLen Brown u32 hci_result; 478b4f9fe12SLen Brown u32 value; 479b4f9fe12SLen Brown 480135740deSSeth Forshee hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); 481*32bcd5cbSSeth Forshee if (hci_result == HCI_SUCCESS) 482b4f9fe12SLen Brown return (value >> HCI_LCD_BRIGHTNESS_SHIFT); 483*32bcd5cbSSeth Forshee 484*32bcd5cbSSeth Forshee return -EIO; 485b4f9fe12SLen Brown } 486b4f9fe12SLen Brown 487936c8bcdSAlexey Dobriyan static int lcd_proc_show(struct seq_file *m, void *v) 488b4f9fe12SLen Brown { 489135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 490135740deSSeth Forshee int value; 491b4f9fe12SLen Brown 492135740deSSeth Forshee if (!dev->backlight_dev) 493135740deSSeth Forshee return -ENODEV; 494135740deSSeth Forshee 495135740deSSeth Forshee value = get_lcd(dev->backlight_dev); 496b4f9fe12SLen Brown if (value >= 0) { 497936c8bcdSAlexey Dobriyan seq_printf(m, "brightness: %d\n", value); 498936c8bcdSAlexey Dobriyan seq_printf(m, "brightness_levels: %d\n", 499b4f9fe12SLen Brown HCI_LCD_BRIGHTNESS_LEVELS); 500*32bcd5cbSSeth Forshee return 0; 501b4f9fe12SLen Brown } 502b4f9fe12SLen Brown 503*32bcd5cbSSeth Forshee pr_err("Error reading LCD brightness\n"); 504*32bcd5cbSSeth Forshee return -EIO; 505936c8bcdSAlexey Dobriyan } 506936c8bcdSAlexey Dobriyan 507936c8bcdSAlexey Dobriyan static int lcd_proc_open(struct inode *inode, struct file *file) 508936c8bcdSAlexey Dobriyan { 509135740deSSeth Forshee return single_open(file, lcd_proc_show, PDE(inode)->data); 510b4f9fe12SLen Brown } 511b4f9fe12SLen Brown 512135740deSSeth Forshee static int set_lcd(struct toshiba_acpi_dev *dev, int value) 513b4f9fe12SLen Brown { 514b4f9fe12SLen Brown u32 hci_result; 515b4f9fe12SLen Brown 516b4f9fe12SLen Brown value = value << HCI_LCD_BRIGHTNESS_SHIFT; 517135740deSSeth Forshee hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); 518*32bcd5cbSSeth Forshee return hci_result == HCI_SUCCESS ? 0 : -EIO; 519b4f9fe12SLen Brown } 520b4f9fe12SLen Brown 521b4f9fe12SLen Brown static int set_lcd_status(struct backlight_device *bd) 522b4f9fe12SLen Brown { 523135740deSSeth Forshee struct toshiba_acpi_dev *dev = bl_get_data(bd); 524135740deSSeth Forshee return set_lcd(dev, bd->props.brightness); 525b4f9fe12SLen Brown } 526b4f9fe12SLen Brown 527936c8bcdSAlexey Dobriyan static ssize_t lcd_proc_write(struct file *file, const char __user *buf, 528936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 529b4f9fe12SLen Brown { 530135740deSSeth Forshee struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; 531936c8bcdSAlexey Dobriyan char cmd[42]; 532936c8bcdSAlexey Dobriyan size_t len; 533b4f9fe12SLen Brown int value; 534b4f9fe12SLen Brown int ret; 535b4f9fe12SLen Brown 536936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 537936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 538936c8bcdSAlexey Dobriyan return -EFAULT; 539936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 540936c8bcdSAlexey Dobriyan 541936c8bcdSAlexey Dobriyan if (sscanf(cmd, " brightness : %i", &value) == 1 && 542b4f9fe12SLen Brown value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { 543135740deSSeth Forshee ret = set_lcd(dev, value); 544b4f9fe12SLen Brown if (ret == 0) 545b4f9fe12SLen Brown ret = count; 546b4f9fe12SLen Brown } else { 547b4f9fe12SLen Brown ret = -EINVAL; 548b4f9fe12SLen Brown } 549b4f9fe12SLen Brown return ret; 550b4f9fe12SLen Brown } 551b4f9fe12SLen Brown 552936c8bcdSAlexey Dobriyan static const struct file_operations lcd_proc_fops = { 553936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 554936c8bcdSAlexey Dobriyan .open = lcd_proc_open, 555936c8bcdSAlexey Dobriyan .read = seq_read, 556936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 557936c8bcdSAlexey Dobriyan .release = single_release, 558936c8bcdSAlexey Dobriyan .write = lcd_proc_write, 559936c8bcdSAlexey Dobriyan }; 560936c8bcdSAlexey Dobriyan 561936c8bcdSAlexey Dobriyan static int video_proc_show(struct seq_file *m, void *v) 562b4f9fe12SLen Brown { 563135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 564b4f9fe12SLen Brown u32 hci_result; 565b4f9fe12SLen Brown u32 value; 566b4f9fe12SLen Brown 567135740deSSeth Forshee hci_read1(dev, HCI_VIDEO_OUT, &value, &hci_result); 568b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 569b4f9fe12SLen Brown int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; 570b4f9fe12SLen Brown int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; 571b4f9fe12SLen Brown int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; 572936c8bcdSAlexey Dobriyan seq_printf(m, "lcd_out: %d\n", is_lcd); 573936c8bcdSAlexey Dobriyan seq_printf(m, "crt_out: %d\n", is_crt); 574936c8bcdSAlexey Dobriyan seq_printf(m, "tv_out: %d\n", is_tv); 575*32bcd5cbSSeth Forshee return 0; 576b4f9fe12SLen Brown } 577b4f9fe12SLen Brown 578*32bcd5cbSSeth Forshee return -EIO; 579b4f9fe12SLen Brown } 580b4f9fe12SLen Brown 581936c8bcdSAlexey Dobriyan static int video_proc_open(struct inode *inode, struct file *file) 582b4f9fe12SLen Brown { 583135740deSSeth Forshee return single_open(file, video_proc_show, PDE(inode)->data); 584936c8bcdSAlexey Dobriyan } 585936c8bcdSAlexey Dobriyan 586936c8bcdSAlexey Dobriyan static ssize_t video_proc_write(struct file *file, const char __user *buf, 587936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 588936c8bcdSAlexey Dobriyan { 589135740deSSeth Forshee struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; 590936c8bcdSAlexey Dobriyan char *cmd, *buffer; 591*32bcd5cbSSeth Forshee int ret = 0; 592b4f9fe12SLen Brown int value; 593b4f9fe12SLen Brown int remain = count; 594b4f9fe12SLen Brown int lcd_out = -1; 595b4f9fe12SLen Brown int crt_out = -1; 596b4f9fe12SLen Brown int tv_out = -1; 597b4f9fe12SLen Brown u32 hci_result; 598b4f9fe12SLen Brown u32 video_out; 599b4f9fe12SLen Brown 600936c8bcdSAlexey Dobriyan cmd = kmalloc(count + 1, GFP_KERNEL); 601936c8bcdSAlexey Dobriyan if (!cmd) 602936c8bcdSAlexey Dobriyan return -ENOMEM; 603936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, count)) { 604936c8bcdSAlexey Dobriyan kfree(cmd); 605936c8bcdSAlexey Dobriyan return -EFAULT; 606936c8bcdSAlexey Dobriyan } 607936c8bcdSAlexey Dobriyan cmd[count] = '\0'; 608936c8bcdSAlexey Dobriyan 609936c8bcdSAlexey Dobriyan buffer = cmd; 610936c8bcdSAlexey Dobriyan 611b4f9fe12SLen Brown /* scan expression. Multiple expressions may be delimited with ; 612b4f9fe12SLen Brown * 613b4f9fe12SLen Brown * NOTE: to keep scanning simple, invalid fields are ignored 614b4f9fe12SLen Brown */ 615b4f9fe12SLen Brown while (remain) { 616b4f9fe12SLen Brown if (sscanf(buffer, " lcd_out : %i", &value) == 1) 617b4f9fe12SLen Brown lcd_out = value & 1; 618b4f9fe12SLen Brown else if (sscanf(buffer, " crt_out : %i", &value) == 1) 619b4f9fe12SLen Brown crt_out = value & 1; 620b4f9fe12SLen Brown else if (sscanf(buffer, " tv_out : %i", &value) == 1) 621b4f9fe12SLen Brown tv_out = value & 1; 622b4f9fe12SLen Brown /* advance to one character past the next ; */ 623b4f9fe12SLen Brown do { 624b4f9fe12SLen Brown ++buffer; 625b4f9fe12SLen Brown --remain; 626b4f9fe12SLen Brown } 627b4f9fe12SLen Brown while (remain && *(buffer - 1) != ';'); 628b4f9fe12SLen Brown } 629b4f9fe12SLen Brown 630936c8bcdSAlexey Dobriyan kfree(cmd); 631936c8bcdSAlexey Dobriyan 632135740deSSeth Forshee hci_read1(dev, HCI_VIDEO_OUT, &video_out, &hci_result); 633b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 634b4f9fe12SLen Brown unsigned int new_video_out = video_out; 635b4f9fe12SLen Brown if (lcd_out != -1) 636b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); 637b4f9fe12SLen Brown if (crt_out != -1) 638b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); 639b4f9fe12SLen Brown if (tv_out != -1) 640b4f9fe12SLen Brown _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); 641b4f9fe12SLen Brown /* To avoid unnecessary video disruption, only write the new 642b4f9fe12SLen Brown * video setting if something changed. */ 643b4f9fe12SLen Brown if (new_video_out != video_out) 644*32bcd5cbSSeth Forshee ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); 645b4f9fe12SLen Brown } else { 646*32bcd5cbSSeth Forshee ret = -EIO; 647b4f9fe12SLen Brown } 648b4f9fe12SLen Brown 649*32bcd5cbSSeth Forshee return ret ? ret : count; 650b4f9fe12SLen Brown } 651b4f9fe12SLen Brown 652936c8bcdSAlexey Dobriyan static const struct file_operations video_proc_fops = { 653936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 654936c8bcdSAlexey Dobriyan .open = video_proc_open, 655936c8bcdSAlexey Dobriyan .read = seq_read, 656936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 657936c8bcdSAlexey Dobriyan .release = single_release, 658936c8bcdSAlexey Dobriyan .write = video_proc_write, 659936c8bcdSAlexey Dobriyan }; 660936c8bcdSAlexey Dobriyan 661936c8bcdSAlexey Dobriyan static int fan_proc_show(struct seq_file *m, void *v) 662b4f9fe12SLen Brown { 663135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 664b4f9fe12SLen Brown u32 hci_result; 665b4f9fe12SLen Brown u32 value; 666b4f9fe12SLen Brown 667135740deSSeth Forshee hci_read1(dev, HCI_FAN, &value, &hci_result); 668b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 669936c8bcdSAlexey Dobriyan seq_printf(m, "running: %d\n", (value > 0)); 670135740deSSeth Forshee seq_printf(m, "force_on: %d\n", dev->force_fan); 671*32bcd5cbSSeth Forshee return 0; 672b4f9fe12SLen Brown } 673b4f9fe12SLen Brown 674*32bcd5cbSSeth Forshee return -EIO; 675b4f9fe12SLen Brown } 676b4f9fe12SLen Brown 677936c8bcdSAlexey Dobriyan static int fan_proc_open(struct inode *inode, struct file *file) 678b4f9fe12SLen Brown { 679135740deSSeth Forshee return single_open(file, fan_proc_show, PDE(inode)->data); 680936c8bcdSAlexey Dobriyan } 681936c8bcdSAlexey Dobriyan 682936c8bcdSAlexey Dobriyan static ssize_t fan_proc_write(struct file *file, const char __user *buf, 683936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 684936c8bcdSAlexey Dobriyan { 685135740deSSeth Forshee struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; 686936c8bcdSAlexey Dobriyan char cmd[42]; 687936c8bcdSAlexey Dobriyan size_t len; 688b4f9fe12SLen Brown int value; 689b4f9fe12SLen Brown u32 hci_result; 690b4f9fe12SLen Brown 691936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 692936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 693936c8bcdSAlexey Dobriyan return -EFAULT; 694936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 695936c8bcdSAlexey Dobriyan 696936c8bcdSAlexey Dobriyan if (sscanf(cmd, " force_on : %i", &value) == 1 && 697b4f9fe12SLen Brown value >= 0 && value <= 1) { 698135740deSSeth Forshee hci_write1(dev, HCI_FAN, value, &hci_result); 699b4f9fe12SLen Brown if (hci_result != HCI_SUCCESS) 700*32bcd5cbSSeth Forshee return -EIO; 701b4f9fe12SLen Brown else 702135740deSSeth Forshee dev->force_fan = value; 703b4f9fe12SLen Brown } else { 704b4f9fe12SLen Brown return -EINVAL; 705b4f9fe12SLen Brown } 706b4f9fe12SLen Brown 707b4f9fe12SLen Brown return count; 708b4f9fe12SLen Brown } 709b4f9fe12SLen Brown 710936c8bcdSAlexey Dobriyan static const struct file_operations fan_proc_fops = { 711936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 712936c8bcdSAlexey Dobriyan .open = fan_proc_open, 713936c8bcdSAlexey Dobriyan .read = seq_read, 714936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 715936c8bcdSAlexey Dobriyan .release = single_release, 716936c8bcdSAlexey Dobriyan .write = fan_proc_write, 717936c8bcdSAlexey Dobriyan }; 718936c8bcdSAlexey Dobriyan 719936c8bcdSAlexey Dobriyan static int keys_proc_show(struct seq_file *m, void *v) 720b4f9fe12SLen Brown { 721135740deSSeth Forshee struct toshiba_acpi_dev *dev = m->private; 722b4f9fe12SLen Brown u32 hci_result; 723b4f9fe12SLen Brown u32 value; 724b4f9fe12SLen Brown 725135740deSSeth Forshee if (!dev->key_event_valid) { 726135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 727b4f9fe12SLen Brown if (hci_result == HCI_SUCCESS) { 728135740deSSeth Forshee dev->key_event_valid = 1; 729135740deSSeth Forshee dev->last_key_event = value; 730b4f9fe12SLen Brown } else if (hci_result == HCI_EMPTY) { 731b4f9fe12SLen Brown /* better luck next time */ 732b4f9fe12SLen Brown } else if (hci_result == HCI_NOT_SUPPORTED) { 733b4f9fe12SLen Brown /* This is a workaround for an unresolved issue on 734b4f9fe12SLen Brown * some machines where system events sporadically 735b4f9fe12SLen Brown * become disabled. */ 736135740deSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 7377e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 738b4f9fe12SLen Brown } else { 7397e33460dSJoe Perches pr_err("Error reading hotkey status\n"); 740*32bcd5cbSSeth Forshee return -EIO; 741b4f9fe12SLen Brown } 742b4f9fe12SLen Brown } 743b4f9fe12SLen Brown 744135740deSSeth Forshee seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); 745135740deSSeth Forshee seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); 746936c8bcdSAlexey Dobriyan return 0; 747b4f9fe12SLen Brown } 748b4f9fe12SLen Brown 749936c8bcdSAlexey Dobriyan static int keys_proc_open(struct inode *inode, struct file *file) 750b4f9fe12SLen Brown { 751135740deSSeth Forshee return single_open(file, keys_proc_show, PDE(inode)->data); 752936c8bcdSAlexey Dobriyan } 753936c8bcdSAlexey Dobriyan 754936c8bcdSAlexey Dobriyan static ssize_t keys_proc_write(struct file *file, const char __user *buf, 755936c8bcdSAlexey Dobriyan size_t count, loff_t *pos) 756936c8bcdSAlexey Dobriyan { 757135740deSSeth Forshee struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; 758936c8bcdSAlexey Dobriyan char cmd[42]; 759936c8bcdSAlexey Dobriyan size_t len; 760b4f9fe12SLen Brown int value; 761b4f9fe12SLen Brown 762936c8bcdSAlexey Dobriyan len = min(count, sizeof(cmd) - 1); 763936c8bcdSAlexey Dobriyan if (copy_from_user(cmd, buf, len)) 764936c8bcdSAlexey Dobriyan return -EFAULT; 765936c8bcdSAlexey Dobriyan cmd[len] = '\0'; 766936c8bcdSAlexey Dobriyan 767936c8bcdSAlexey Dobriyan if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { 768135740deSSeth Forshee dev->key_event_valid = 0; 769b4f9fe12SLen Brown } else { 770b4f9fe12SLen Brown return -EINVAL; 771b4f9fe12SLen Brown } 772b4f9fe12SLen Brown 773b4f9fe12SLen Brown return count; 774b4f9fe12SLen Brown } 775b4f9fe12SLen Brown 776936c8bcdSAlexey Dobriyan static const struct file_operations keys_proc_fops = { 777936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 778936c8bcdSAlexey Dobriyan .open = keys_proc_open, 779936c8bcdSAlexey Dobriyan .read = seq_read, 780936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 781936c8bcdSAlexey Dobriyan .release = single_release, 782936c8bcdSAlexey Dobriyan .write = keys_proc_write, 783936c8bcdSAlexey Dobriyan }; 784936c8bcdSAlexey Dobriyan 785936c8bcdSAlexey Dobriyan static int version_proc_show(struct seq_file *m, void *v) 786b4f9fe12SLen Brown { 787936c8bcdSAlexey Dobriyan seq_printf(m, "driver: %s\n", TOSHIBA_ACPI_VERSION); 788936c8bcdSAlexey Dobriyan seq_printf(m, "proc_interface: %d\n", PROC_INTERFACE_VERSION); 789936c8bcdSAlexey Dobriyan return 0; 790b4f9fe12SLen Brown } 791b4f9fe12SLen Brown 792936c8bcdSAlexey Dobriyan static int version_proc_open(struct inode *inode, struct file *file) 793936c8bcdSAlexey Dobriyan { 794936c8bcdSAlexey Dobriyan return single_open(file, version_proc_show, PDE(inode)->data); 795936c8bcdSAlexey Dobriyan } 796936c8bcdSAlexey Dobriyan 797936c8bcdSAlexey Dobriyan static const struct file_operations version_proc_fops = { 798936c8bcdSAlexey Dobriyan .owner = THIS_MODULE, 799936c8bcdSAlexey Dobriyan .open = version_proc_open, 800936c8bcdSAlexey Dobriyan .read = seq_read, 801936c8bcdSAlexey Dobriyan .llseek = seq_lseek, 802936c8bcdSAlexey Dobriyan .release = single_release, 803936c8bcdSAlexey Dobriyan }; 804936c8bcdSAlexey Dobriyan 805b4f9fe12SLen Brown /* proc and module init 806b4f9fe12SLen Brown */ 807b4f9fe12SLen Brown 808b4f9fe12SLen Brown #define PROC_TOSHIBA "toshiba" 809b4f9fe12SLen Brown 810135740deSSeth Forshee static void __devinit 811135740deSSeth Forshee create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) 812b4f9fe12SLen Brown { 813135740deSSeth Forshee proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, 814135740deSSeth Forshee &lcd_proc_fops, dev); 815135740deSSeth Forshee proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, 816135740deSSeth Forshee &video_proc_fops, dev); 817135740deSSeth Forshee proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, 818135740deSSeth Forshee &fan_proc_fops, dev); 819135740deSSeth Forshee proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, 820135740deSSeth Forshee &keys_proc_fops, dev); 821135740deSSeth Forshee proc_create_data("version", S_IRUGO, toshiba_proc_dir, 822135740deSSeth Forshee &version_proc_fops, dev); 823b4f9fe12SLen Brown } 824b4f9fe12SLen Brown 825f8ef3aecSAxel Lin static void remove_toshiba_proc_entries(void) 826b4f9fe12SLen Brown { 827936c8bcdSAlexey Dobriyan remove_proc_entry("lcd", toshiba_proc_dir); 828936c8bcdSAlexey Dobriyan remove_proc_entry("video", toshiba_proc_dir); 829936c8bcdSAlexey Dobriyan remove_proc_entry("fan", toshiba_proc_dir); 830936c8bcdSAlexey Dobriyan remove_proc_entry("keys", toshiba_proc_dir); 831936c8bcdSAlexey Dobriyan remove_proc_entry("version", toshiba_proc_dir); 832b4f9fe12SLen Brown } 833b4f9fe12SLen Brown 834acc2472eSLionel Debroux static const struct backlight_ops toshiba_backlight_data = { 835b4f9fe12SLen Brown .get_brightness = get_lcd, 836b4f9fe12SLen Brown .update_status = set_lcd_status, 837b4f9fe12SLen Brown }; 838b4f9fe12SLen Brown 839135740deSSeth Forshee static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev, 840135740deSSeth Forshee char *device_path) 8416335e4d5SMatthew Garrett { 842135740deSSeth Forshee acpi_status status; 843135740deSSeth Forshee int error; 844135740deSSeth Forshee 845135740deSSeth Forshee status = acpi_get_handle(NULL, device_path, &dev->handle); 846135740deSSeth Forshee if (ACPI_FAILURE(status)) { 847135740deSSeth Forshee pr_info("Unable to get notification device\n"); 848135740deSSeth Forshee return -ENODEV; 849135740deSSeth Forshee } 850135740deSSeth Forshee 851135740deSSeth Forshee dev->hotkey_dev = input_allocate_device(); 852135740deSSeth Forshee if (!dev->hotkey_dev) { 853135740deSSeth Forshee pr_info("Unable to register input device\n"); 854135740deSSeth Forshee return -ENOMEM; 855135740deSSeth Forshee } 856135740deSSeth Forshee 857135740deSSeth Forshee dev->hotkey_dev->name = "Toshiba input device"; 858135740deSSeth Forshee dev->hotkey_dev->phys = device_path; 859135740deSSeth Forshee dev->hotkey_dev->id.bustype = BUS_HOST; 860135740deSSeth Forshee 861135740deSSeth Forshee error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); 862135740deSSeth Forshee if (error) 863135740deSSeth Forshee goto err_free_dev; 864135740deSSeth Forshee 865135740deSSeth Forshee status = acpi_evaluate_object(dev->handle, "ENAB", NULL, NULL); 866135740deSSeth Forshee if (ACPI_FAILURE(status)) { 867135740deSSeth Forshee pr_info("Unable to enable hotkeys\n"); 868135740deSSeth Forshee error = -ENODEV; 869135740deSSeth Forshee goto err_free_keymap; 870135740deSSeth Forshee } 871135740deSSeth Forshee 872135740deSSeth Forshee error = input_register_device(dev->hotkey_dev); 873135740deSSeth Forshee if (error) { 874135740deSSeth Forshee pr_info("Unable to register input device\n"); 875135740deSSeth Forshee goto err_free_keymap; 876135740deSSeth Forshee } 877135740deSSeth Forshee 878135740deSSeth Forshee return 0; 879135740deSSeth Forshee 880135740deSSeth Forshee err_free_keymap: 881135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 882135740deSSeth Forshee err_free_dev: 883135740deSSeth Forshee input_free_device(dev->hotkey_dev); 884135740deSSeth Forshee dev->hotkey_dev = NULL; 885135740deSSeth Forshee return error; 886135740deSSeth Forshee } 887135740deSSeth Forshee 888135740deSSeth Forshee static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) 889135740deSSeth Forshee { 890135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 891135740deSSeth Forshee 892135740deSSeth Forshee remove_toshiba_proc_entries(); 893135740deSSeth Forshee 894135740deSSeth Forshee if (dev->hotkey_dev) { 895135740deSSeth Forshee input_unregister_device(dev->hotkey_dev); 896135740deSSeth Forshee sparse_keymap_free(dev->hotkey_dev); 897135740deSSeth Forshee } 898135740deSSeth Forshee 899135740deSSeth Forshee if (dev->bt_rfk) { 900135740deSSeth Forshee rfkill_unregister(dev->bt_rfk); 901135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 902135740deSSeth Forshee } 903135740deSSeth Forshee 904135740deSSeth Forshee if (dev->backlight_dev) 905135740deSSeth Forshee backlight_device_unregister(dev->backlight_dev); 906135740deSSeth Forshee 907135740deSSeth Forshee if (dev->illumination_installed) 908135740deSSeth Forshee led_classdev_unregister(&dev->led_dev); 909135740deSSeth Forshee 910135740deSSeth Forshee kfree(dev); 911135740deSSeth Forshee 912135740deSSeth Forshee return 0; 913135740deSSeth Forshee } 914135740deSSeth Forshee 915135740deSSeth Forshee static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) 916135740deSSeth Forshee { 917135740deSSeth Forshee struct toshiba_acpi_dev *dev; 918135740deSSeth Forshee u32 hci_result; 919135740deSSeth Forshee bool bt_present; 920135740deSSeth Forshee int ret = 0; 921135740deSSeth Forshee struct backlight_properties props; 922135740deSSeth Forshee 923135740deSSeth Forshee pr_info("Toshiba Laptop ACPI Extras version %s\n", 924135740deSSeth Forshee TOSHIBA_ACPI_VERSION); 925135740deSSeth Forshee 926135740deSSeth Forshee dev = kzalloc(sizeof(*dev), GFP_KERNEL); 927135740deSSeth Forshee if (!dev) 928135740deSSeth Forshee return -ENOMEM; 929135740deSSeth Forshee dev->acpi_dev = acpi_dev; 930135740deSSeth Forshee acpi_dev->driver_data = dev; 931135740deSSeth Forshee 932135740deSSeth Forshee /* simple device detection: look for HCI method */ 933135740deSSeth Forshee if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { 934135740deSSeth Forshee dev->method_hci = TOSH_INTERFACE_1 GHCI_METHOD; 935135740deSSeth Forshee if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_1)) 936135740deSSeth Forshee pr_info("Unable to activate hotkeys\n"); 937135740deSSeth Forshee } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { 938135740deSSeth Forshee dev->method_hci = TOSH_INTERFACE_2 GHCI_METHOD; 939135740deSSeth Forshee if (toshiba_acpi_setup_keyboard(dev, TOSH_INTERFACE_2)) 940135740deSSeth Forshee pr_info("Unable to activate hotkeys\n"); 941135740deSSeth Forshee } else { 942135740deSSeth Forshee ret = -ENODEV; 943135740deSSeth Forshee goto error; 944135740deSSeth Forshee } 945135740deSSeth Forshee 946135740deSSeth Forshee pr_info("HCI method: %s\n", dev->method_hci); 947135740deSSeth Forshee 948135740deSSeth Forshee mutex_init(&dev->mutex); 949135740deSSeth Forshee 950135740deSSeth Forshee /* enable event fifo */ 951135740deSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 952135740deSSeth Forshee 953135740deSSeth Forshee create_toshiba_proc_entries(dev); 954135740deSSeth Forshee 955135740deSSeth Forshee props.type = BACKLIGHT_PLATFORM; 956135740deSSeth Forshee props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; 957135740deSSeth Forshee dev->backlight_dev = backlight_device_register("toshiba", 958135740deSSeth Forshee &acpi_dev->dev, 959135740deSSeth Forshee dev, 960135740deSSeth Forshee &toshiba_backlight_data, 961135740deSSeth Forshee &props); 962135740deSSeth Forshee if (IS_ERR(dev->backlight_dev)) { 963135740deSSeth Forshee ret = PTR_ERR(dev->backlight_dev); 964135740deSSeth Forshee 965135740deSSeth Forshee pr_err("Could not register toshiba backlight device\n"); 966135740deSSeth Forshee dev->backlight_dev = NULL; 967135740deSSeth Forshee goto error; 968135740deSSeth Forshee } 969135740deSSeth Forshee 970135740deSSeth Forshee /* Register rfkill switch for Bluetooth */ 971135740deSSeth Forshee if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { 972135740deSSeth Forshee dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", 973135740deSSeth Forshee &acpi_dev->dev, 974135740deSSeth Forshee RFKILL_TYPE_BLUETOOTH, 975135740deSSeth Forshee &toshiba_rfk_ops, 976135740deSSeth Forshee dev); 977135740deSSeth Forshee if (!dev->bt_rfk) { 978135740deSSeth Forshee pr_err("unable to allocate rfkill device\n"); 979135740deSSeth Forshee ret = -ENOMEM; 980135740deSSeth Forshee goto error; 981135740deSSeth Forshee } 982135740deSSeth Forshee 983135740deSSeth Forshee ret = rfkill_register(dev->bt_rfk); 984135740deSSeth Forshee if (ret) { 985135740deSSeth Forshee pr_err("unable to register rfkill device\n"); 986135740deSSeth Forshee rfkill_destroy(dev->bt_rfk); 987135740deSSeth Forshee goto error; 988135740deSSeth Forshee } 989135740deSSeth Forshee } 990135740deSSeth Forshee 991135740deSSeth Forshee if (toshiba_illumination_available(dev)) { 992135740deSSeth Forshee dev->led_dev.name = "toshiba::illumination"; 993135740deSSeth Forshee dev->led_dev.max_brightness = 1; 994135740deSSeth Forshee dev->led_dev.brightness_set = toshiba_illumination_set; 995135740deSSeth Forshee dev->led_dev.brightness_get = toshiba_illumination_get; 996135740deSSeth Forshee if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) 997135740deSSeth Forshee dev->illumination_installed = 1; 998135740deSSeth Forshee } 999135740deSSeth Forshee 1000135740deSSeth Forshee return 0; 1001135740deSSeth Forshee 1002135740deSSeth Forshee error: 1003135740deSSeth Forshee toshiba_acpi_remove(acpi_dev, 0); 1004135740deSSeth Forshee return ret; 1005135740deSSeth Forshee } 1006135740deSSeth Forshee 1007135740deSSeth Forshee static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) 1008135740deSSeth Forshee { 1009135740deSSeth Forshee struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); 10106335e4d5SMatthew Garrett u32 hci_result, value; 10116335e4d5SMatthew Garrett 10126335e4d5SMatthew Garrett if (event != 0x80) 10136335e4d5SMatthew Garrett return; 10146335e4d5SMatthew Garrett do { 1015135740deSSeth Forshee hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); 10166335e4d5SMatthew Garrett if (hci_result == HCI_SUCCESS) { 10176335e4d5SMatthew Garrett if (value == 0x100) 10186335e4d5SMatthew Garrett continue; 1019b466301bSFrans Pop /* act on key press; ignore key release */ 1020b466301bSFrans Pop if (value & 0x80) 1021b466301bSFrans Pop continue; 1022b466301bSFrans Pop 1023135740deSSeth Forshee if (!sparse_keymap_report_event(dev->hotkey_dev, 1024384a7cd9SDmitry Torokhov value, 1, true)) { 10257e33460dSJoe Perches pr_info("Unknown key %x\n", 1026b466301bSFrans Pop value); 10276335e4d5SMatthew Garrett } 10286335e4d5SMatthew Garrett } else if (hci_result == HCI_NOT_SUPPORTED) { 10296335e4d5SMatthew Garrett /* This is a workaround for an unresolved issue on 10306335e4d5SMatthew Garrett * some machines where system events sporadically 10316335e4d5SMatthew Garrett * become disabled. */ 1032135740deSSeth Forshee hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); 10337e33460dSJoe Perches pr_notice("Re-enabled hotkeys\n"); 10346335e4d5SMatthew Garrett } 10356335e4d5SMatthew Garrett } while (hci_result != HCI_EMPTY); 10366335e4d5SMatthew Garrett } 10376335e4d5SMatthew Garrett 10386335e4d5SMatthew Garrett 1039135740deSSeth Forshee static struct acpi_driver toshiba_acpi_driver = { 1040135740deSSeth Forshee .name = "Toshiba ACPI driver", 1041135740deSSeth Forshee .owner = THIS_MODULE, 1042135740deSSeth Forshee .ids = toshiba_device_ids, 1043135740deSSeth Forshee .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, 1044135740deSSeth Forshee .ops = { 1045135740deSSeth Forshee .add = toshiba_acpi_add, 1046135740deSSeth Forshee .remove = toshiba_acpi_remove, 1047135740deSSeth Forshee .notify = toshiba_acpi_notify, 1048135740deSSeth Forshee }, 1049135740deSSeth Forshee }; 1050b4f9fe12SLen Brown 1051b4f9fe12SLen Brown static int __init toshiba_acpi_init(void) 1052b4f9fe12SLen Brown { 1053135740deSSeth Forshee int ret; 1054b4f9fe12SLen Brown 1055b4f9fe12SLen Brown toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); 1056b4f9fe12SLen Brown if (!toshiba_proc_dir) { 1057135740deSSeth Forshee pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); 1058b4f9fe12SLen Brown return -ENODEV; 1059b4f9fe12SLen Brown } 1060b4f9fe12SLen Brown 1061135740deSSeth Forshee ret = acpi_bus_register_driver(&toshiba_acpi_driver); 1062b4f9fe12SLen Brown if (ret) { 1063135740deSSeth Forshee pr_err("Failed to register ACPI driver: %d\n", ret); 1064135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1065135740deSSeth Forshee } 1066135740deSSeth Forshee 1067b4f9fe12SLen Brown return ret; 1068b4f9fe12SLen Brown } 1069b4f9fe12SLen Brown 1070135740deSSeth Forshee static void __exit toshiba_acpi_exit(void) 1071135740deSSeth Forshee { 1072135740deSSeth Forshee acpi_bus_unregister_driver(&toshiba_acpi_driver); 1073135740deSSeth Forshee if (toshiba_proc_dir) 1074135740deSSeth Forshee remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); 1075b4f9fe12SLen Brown } 1076b4f9fe12SLen Brown 1077b4f9fe12SLen Brown module_init(toshiba_acpi_init); 1078b4f9fe12SLen Brown module_exit(toshiba_acpi_exit); 1079