1 /* 2 * ACPI driver for Topstar notebooks (hotkeys support only) 3 * 4 * Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br> 5 * 6 * Implementation inspired by existing x86 platform drivers, in special 7 * asus/eepc/fujitsu-laptop, thanks to their authors 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/init.h> 19 #include <linux/acpi.h> 20 #include <linux/input.h> 21 22 #define ACPI_TOPSTAR_CLASS "topstar" 23 24 struct topstar_hkey { 25 struct input_dev *inputdev; 26 }; 27 28 struct tps_key_entry { 29 u8 code; 30 u16 keycode; 31 }; 32 33 static struct tps_key_entry topstar_keymap[] = { 34 { 0x80, KEY_BRIGHTNESSUP }, 35 { 0x81, KEY_BRIGHTNESSDOWN }, 36 { 0x83, KEY_VOLUMEUP }, 37 { 0x84, KEY_VOLUMEDOWN }, 38 { 0x85, KEY_MUTE }, 39 { 0x86, KEY_SWITCHVIDEOMODE }, 40 { 0x87, KEY_F13 }, /* touchpad enable/disable key */ 41 { 0x88, KEY_WLAN }, 42 { 0x8a, KEY_WWW }, 43 { 0x8b, KEY_MAIL }, 44 { 0x8c, KEY_MEDIA }, 45 { 0x96, KEY_F14 }, /* G key? */ 46 { } 47 }; 48 49 static struct tps_key_entry *tps_get_key_by_scancode(int code) 50 { 51 struct tps_key_entry *key; 52 53 for (key = topstar_keymap; key->code; key++) 54 if (code == key->code) 55 return key; 56 57 return NULL; 58 } 59 60 static struct tps_key_entry *tps_get_key_by_keycode(int code) 61 { 62 struct tps_key_entry *key; 63 64 for (key = topstar_keymap; key->code; key++) 65 if (code == key->keycode) 66 return key; 67 68 return NULL; 69 } 70 71 static void acpi_topstar_notify(struct acpi_device *device, u32 event) 72 { 73 struct tps_key_entry *key; 74 static bool dup_evnt[2]; 75 bool *dup; 76 struct topstar_hkey *hkey = acpi_driver_data(device); 77 78 /* 0x83 and 0x84 key events comes duplicated... */ 79 if (event == 0x83 || event == 0x84) { 80 dup = &dup_evnt[event - 0x83]; 81 if (*dup) { 82 *dup = false; 83 return; 84 } 85 *dup = true; 86 } 87 88 /* 89 * 'G key' generate two event codes, convert to only 90 * one event/key code for now (3G switch?) 91 */ 92 if (event == 0x97) 93 event = 0x96; 94 95 key = tps_get_key_by_scancode(event); 96 if (key) { 97 input_report_key(hkey->inputdev, key->keycode, 1); 98 input_sync(hkey->inputdev); 99 input_report_key(hkey->inputdev, key->keycode, 0); 100 input_sync(hkey->inputdev); 101 return; 102 } 103 104 /* Known non hotkey events don't handled or that we don't care yet */ 105 if (event == 0x8e || event == 0x8f || event == 0x90) 106 return; 107 108 pr_info("unknown event = 0x%02x\n", event); 109 } 110 111 static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state) 112 { 113 acpi_status status; 114 union acpi_object fncx_params[1] = { 115 { .type = ACPI_TYPE_INTEGER } 116 }; 117 struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] }; 118 119 fncx_params[0].integer.value = state ? 0x86 : 0x87; 120 status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL); 121 if (ACPI_FAILURE(status)) { 122 pr_err("Unable to switch FNCX notifications\n"); 123 return -ENODEV; 124 } 125 126 return 0; 127 } 128 129 static int topstar_getkeycode(struct input_dev *dev, int scancode, int *keycode) 130 { 131 struct tps_key_entry *key = tps_get_key_by_scancode(scancode); 132 133 if (!key) 134 return -EINVAL; 135 136 *keycode = key->keycode; 137 return 0; 138 } 139 140 static int topstar_setkeycode(struct input_dev *dev, int scancode, int keycode) 141 { 142 struct tps_key_entry *key; 143 int old_keycode; 144 145 if (keycode < 0 || keycode > KEY_MAX) 146 return -EINVAL; 147 148 key = tps_get_key_by_scancode(scancode); 149 150 if (!key) 151 return -EINVAL; 152 153 old_keycode = key->keycode; 154 key->keycode = keycode; 155 set_bit(keycode, dev->keybit); 156 if (!tps_get_key_by_keycode(old_keycode)) 157 clear_bit(old_keycode, dev->keybit); 158 return 0; 159 } 160 161 static int acpi_topstar_init_hkey(struct topstar_hkey *hkey) 162 { 163 struct tps_key_entry *key; 164 165 hkey->inputdev = input_allocate_device(); 166 if (!hkey->inputdev) { 167 pr_err("Unable to allocate input device\n"); 168 return -ENODEV; 169 } 170 hkey->inputdev->name = "Topstar Laptop extra buttons"; 171 hkey->inputdev->phys = "topstar/input0"; 172 hkey->inputdev->id.bustype = BUS_HOST; 173 hkey->inputdev->getkeycode = topstar_getkeycode; 174 hkey->inputdev->setkeycode = topstar_setkeycode; 175 for (key = topstar_keymap; key->code; key++) { 176 set_bit(EV_KEY, hkey->inputdev->evbit); 177 set_bit(key->keycode, hkey->inputdev->keybit); 178 } 179 if (input_register_device(hkey->inputdev)) { 180 pr_err("Unable to register input device\n"); 181 input_free_device(hkey->inputdev); 182 return -ENODEV; 183 } 184 185 return 0; 186 } 187 188 static int acpi_topstar_add(struct acpi_device *device) 189 { 190 struct topstar_hkey *tps_hkey; 191 192 tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL); 193 if (!tps_hkey) 194 return -ENOMEM; 195 196 strcpy(acpi_device_name(device), "Topstar TPSACPI"); 197 strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS); 198 199 if (acpi_topstar_fncx_switch(device, true)) 200 goto add_err; 201 202 if (acpi_topstar_init_hkey(tps_hkey)) 203 goto add_err; 204 205 device->driver_data = tps_hkey; 206 return 0; 207 208 add_err: 209 kfree(tps_hkey); 210 return -ENODEV; 211 } 212 213 static int acpi_topstar_remove(struct acpi_device *device, int type) 214 { 215 struct topstar_hkey *tps_hkey = acpi_driver_data(device); 216 217 acpi_topstar_fncx_switch(device, false); 218 219 input_unregister_device(tps_hkey->inputdev); 220 kfree(tps_hkey); 221 222 return 0; 223 } 224 225 static const struct acpi_device_id topstar_device_ids[] = { 226 { "TPSACPI01", 0 }, 227 { "", 0 }, 228 }; 229 MODULE_DEVICE_TABLE(acpi, topstar_device_ids); 230 231 static struct acpi_driver acpi_topstar_driver = { 232 .name = "Topstar laptop ACPI driver", 233 .class = ACPI_TOPSTAR_CLASS, 234 .ids = topstar_device_ids, 235 .ops = { 236 .add = acpi_topstar_add, 237 .remove = acpi_topstar_remove, 238 .notify = acpi_topstar_notify, 239 }, 240 }; 241 242 static int __init topstar_laptop_init(void) 243 { 244 int ret; 245 246 ret = acpi_bus_register_driver(&acpi_topstar_driver); 247 if (ret < 0) 248 return ret; 249 250 printk(KERN_INFO "Topstar Laptop ACPI extras driver loaded\n"); 251 252 return 0; 253 } 254 255 static void __exit topstar_laptop_exit(void) 256 { 257 acpi_bus_unregister_driver(&acpi_topstar_driver); 258 } 259 260 module_init(topstar_laptop_init); 261 module_exit(topstar_laptop_exit); 262 263 MODULE_AUTHOR("Herton Ronaldo Krzesinski"); 264 MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver"); 265 MODULE_LICENSE("GPL"); 266