1 /* 2 * Asus Notebooks WMI hotkey driver 3 * 4 * Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 */ 20 21 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 22 23 #include <linux/kernel.h> 24 #include <linux/module.h> 25 #include <linux/init.h> 26 #include <linux/input.h> 27 #include <linux/input/sparse-keymap.h> 28 #include <linux/fb.h> 29 #include <linux/dmi.h> 30 #include <linux/i8042.h> 31 32 #include "asus-wmi.h" 33 34 #define ASUS_NB_WMI_FILE "asus-nb-wmi" 35 36 MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>"); 37 MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver"); 38 MODULE_LICENSE("GPL"); 39 40 #define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" 41 42 MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID); 43 44 /* 45 * WAPF defines the behavior of the Fn+Fx wlan key 46 * The significance of values is yet to be found, but 47 * most of the time: 48 * Bit | Bluetooth | WLAN 49 * 0 | Hardware | Hardware 50 * 1 | Hardware | Software 51 * 4 | Software | Software 52 */ 53 static int wapf = -1; 54 module_param(wapf, uint, 0444); 55 MODULE_PARM_DESC(wapf, "WAPF value"); 56 57 static struct quirk_entry *quirks; 58 59 static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str, 60 struct serio *port) 61 { 62 static bool extended; 63 bool ret = false; 64 65 if (str & I8042_STR_AUXDATA) 66 return false; 67 68 if (unlikely(data == 0xe1)) { 69 extended = true; 70 ret = true; 71 } else if (unlikely(extended)) { 72 extended = false; 73 ret = true; 74 } 75 76 return ret; 77 } 78 79 static struct quirk_entry quirk_asus_unknown = { 80 .wapf = 0, 81 }; 82 83 static struct quirk_entry quirk_asus_q500a = { 84 .i8042_filter = asus_q500a_i8042_filter, 85 }; 86 87 /* 88 * For those machines that need software to control bt/wifi status 89 * and can't adjust brightness through ACPI interface 90 * and have duplicate events(ACPI and WMI) for display toggle 91 */ 92 static struct quirk_entry quirk_asus_x55u = { 93 .wapf = 4, 94 .wmi_backlight_power = true, 95 .no_display_toggle = true, 96 }; 97 98 static struct quirk_entry quirk_asus_wapf4 = { 99 .wapf = 4, 100 }; 101 102 static struct quirk_entry quirk_asus_x200ca = { 103 .wapf = 2, 104 }; 105 106 static struct quirk_entry quirk_no_rfkill = { 107 .no_rfkill = true, 108 }; 109 110 static struct quirk_entry quirk_no_rfkill_wapf4 = { 111 .wapf = 4, 112 .no_rfkill = true, 113 }; 114 115 static struct quirk_entry quirk_asus_ux303ub = { 116 .wmi_backlight_native = true, 117 }; 118 119 static struct quirk_entry quirk_asus_x550lb = { 120 .xusb2pr = 0x01D9, 121 }; 122 123 static int dmi_matched(const struct dmi_system_id *dmi) 124 { 125 pr_info("Identified laptop model '%s'\n", dmi->ident); 126 quirks = dmi->driver_data; 127 return 1; 128 } 129 130 static const struct dmi_system_id asus_quirks[] = { 131 { 132 .callback = dmi_matched, 133 .ident = "ASUSTeK COMPUTER INC. Q500A", 134 .matches = { 135 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 136 DMI_MATCH(DMI_PRODUCT_NAME, "Q500A"), 137 }, 138 .driver_data = &quirk_asus_q500a, 139 }, 140 { 141 .callback = dmi_matched, 142 .ident = "ASUSTeK COMPUTER INC. U32U", 143 .matches = { 144 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), 145 DMI_MATCH(DMI_PRODUCT_NAME, "U32U"), 146 }, 147 /* 148 * Note this machine has a Brazos APU, and most Brazos Asus 149 * machines need quirk_asus_x55u / wmi_backlight_power but 150 * here acpi-video seems to work fine for backlight control. 151 */ 152 .driver_data = &quirk_asus_wapf4, 153 }, 154 { 155 .callback = dmi_matched, 156 .ident = "ASUSTeK COMPUTER INC. X401U", 157 .matches = { 158 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 159 DMI_MATCH(DMI_PRODUCT_NAME, "X401U"), 160 }, 161 .driver_data = &quirk_asus_x55u, 162 }, 163 { 164 .callback = dmi_matched, 165 .ident = "ASUSTeK COMPUTER INC. X401A", 166 .matches = { 167 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 168 DMI_MATCH(DMI_PRODUCT_NAME, "X401A"), 169 }, 170 .driver_data = &quirk_asus_wapf4, 171 }, 172 { 173 .callback = dmi_matched, 174 .ident = "ASUSTeK COMPUTER INC. X401A1", 175 .matches = { 176 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 177 DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"), 178 }, 179 .driver_data = &quirk_asus_wapf4, 180 }, 181 { 182 .callback = dmi_matched, 183 .ident = "ASUSTeK COMPUTER INC. X45U", 184 .matches = { 185 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 186 DMI_MATCH(DMI_PRODUCT_NAME, "X45U"), 187 }, 188 .driver_data = &quirk_asus_wapf4, 189 }, 190 { 191 .callback = dmi_matched, 192 .ident = "ASUSTeK COMPUTER INC. X456UA", 193 .matches = { 194 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 195 DMI_MATCH(DMI_PRODUCT_NAME, "X456UA"), 196 }, 197 .driver_data = &quirk_no_rfkill_wapf4, 198 }, 199 { 200 .callback = dmi_matched, 201 .ident = "ASUSTeK COMPUTER INC. X456UF", 202 .matches = { 203 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 204 DMI_MATCH(DMI_PRODUCT_NAME, "X456UF"), 205 }, 206 .driver_data = &quirk_no_rfkill_wapf4, 207 }, 208 { 209 .callback = dmi_matched, 210 .ident = "ASUSTeK COMPUTER INC. X501U", 211 .matches = { 212 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 213 DMI_MATCH(DMI_PRODUCT_NAME, "X501U"), 214 }, 215 .driver_data = &quirk_asus_x55u, 216 }, 217 { 218 .callback = dmi_matched, 219 .ident = "ASUSTeK COMPUTER INC. X501A", 220 .matches = { 221 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 222 DMI_MATCH(DMI_PRODUCT_NAME, "X501A"), 223 }, 224 .driver_data = &quirk_asus_wapf4, 225 }, 226 { 227 .callback = dmi_matched, 228 .ident = "ASUSTeK COMPUTER INC. X501A1", 229 .matches = { 230 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 231 DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"), 232 }, 233 .driver_data = &quirk_asus_wapf4, 234 }, 235 { 236 .callback = dmi_matched, 237 .ident = "ASUSTeK COMPUTER INC. X550CA", 238 .matches = { 239 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 240 DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"), 241 }, 242 .driver_data = &quirk_asus_wapf4, 243 }, 244 { 245 .callback = dmi_matched, 246 .ident = "ASUSTeK COMPUTER INC. X550CC", 247 .matches = { 248 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 249 DMI_MATCH(DMI_PRODUCT_NAME, "X550CC"), 250 }, 251 .driver_data = &quirk_asus_wapf4, 252 }, 253 { 254 .callback = dmi_matched, 255 .ident = "ASUSTeK COMPUTER INC. X550CL", 256 .matches = { 257 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 258 DMI_MATCH(DMI_PRODUCT_NAME, "X550CL"), 259 }, 260 .driver_data = &quirk_asus_wapf4, 261 }, 262 { 263 .callback = dmi_matched, 264 .ident = "ASUSTeK COMPUTER INC. X550VB", 265 .matches = { 266 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 267 DMI_MATCH(DMI_PRODUCT_NAME, "X550VB"), 268 }, 269 .driver_data = &quirk_asus_wapf4, 270 }, 271 { 272 .callback = dmi_matched, 273 .ident = "ASUSTeK COMPUTER INC. X551CA", 274 .matches = { 275 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 276 DMI_MATCH(DMI_PRODUCT_NAME, "X551CA"), 277 }, 278 .driver_data = &quirk_asus_wapf4, 279 }, 280 { 281 .callback = dmi_matched, 282 .ident = "ASUSTeK COMPUTER INC. X55A", 283 .matches = { 284 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 285 DMI_MATCH(DMI_PRODUCT_NAME, "X55A"), 286 }, 287 .driver_data = &quirk_asus_wapf4, 288 }, 289 { 290 .callback = dmi_matched, 291 .ident = "ASUSTeK COMPUTER INC. X55C", 292 .matches = { 293 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 294 DMI_MATCH(DMI_PRODUCT_NAME, "X55C"), 295 }, 296 .driver_data = &quirk_asus_wapf4, 297 }, 298 { 299 .callback = dmi_matched, 300 .ident = "ASUSTeK COMPUTER INC. X55U", 301 .matches = { 302 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 303 DMI_MATCH(DMI_PRODUCT_NAME, "X55U"), 304 }, 305 .driver_data = &quirk_asus_x55u, 306 }, 307 { 308 .callback = dmi_matched, 309 .ident = "ASUSTeK COMPUTER INC. X55VD", 310 .matches = { 311 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 312 DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"), 313 }, 314 .driver_data = &quirk_asus_wapf4, 315 }, 316 { 317 .callback = dmi_matched, 318 .ident = "ASUSTeK COMPUTER INC. X75A", 319 .matches = { 320 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 321 DMI_MATCH(DMI_PRODUCT_NAME, "X75A"), 322 }, 323 .driver_data = &quirk_asus_wapf4, 324 }, 325 { 326 .callback = dmi_matched, 327 .ident = "ASUSTeK COMPUTER INC. X75VBP", 328 .matches = { 329 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 330 DMI_MATCH(DMI_PRODUCT_NAME, "X75VBP"), 331 }, 332 .driver_data = &quirk_asus_wapf4, 333 }, 334 { 335 .callback = dmi_matched, 336 .ident = "ASUSTeK COMPUTER INC. X75VD", 337 .matches = { 338 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 339 DMI_MATCH(DMI_PRODUCT_NAME, "X75VD"), 340 }, 341 .driver_data = &quirk_asus_wapf4, 342 }, 343 { 344 .callback = dmi_matched, 345 .ident = "ASUSTeK COMPUTER INC. 1015E", 346 .matches = { 347 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 348 DMI_MATCH(DMI_PRODUCT_NAME, "1015E"), 349 }, 350 .driver_data = &quirk_asus_wapf4, 351 }, 352 { 353 .callback = dmi_matched, 354 .ident = "ASUSTeK COMPUTER INC. 1015U", 355 .matches = { 356 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 357 DMI_MATCH(DMI_PRODUCT_NAME, "1015U"), 358 }, 359 .driver_data = &quirk_asus_wapf4, 360 }, 361 { 362 .callback = dmi_matched, 363 .ident = "ASUSTeK COMPUTER INC. X200CA", 364 .matches = { 365 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 366 DMI_MATCH(DMI_PRODUCT_NAME, "X200CA"), 367 }, 368 .driver_data = &quirk_asus_x200ca, 369 }, 370 { 371 .callback = dmi_matched, 372 .ident = "ASUSTeK COMPUTER INC. X555UB", 373 .matches = { 374 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 375 DMI_MATCH(DMI_PRODUCT_NAME, "X555UB"), 376 }, 377 .driver_data = &quirk_no_rfkill, 378 }, 379 { 380 .callback = dmi_matched, 381 .ident = "ASUSTeK COMPUTER INC. N552VW", 382 .matches = { 383 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 384 DMI_MATCH(DMI_PRODUCT_NAME, "N552VW"), 385 }, 386 .driver_data = &quirk_no_rfkill, 387 }, 388 { 389 .callback = dmi_matched, 390 .ident = "ASUSTeK COMPUTER INC. U303LB", 391 .matches = { 392 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 393 DMI_MATCH(DMI_PRODUCT_NAME, "U303LB"), 394 }, 395 .driver_data = &quirk_no_rfkill, 396 }, 397 { 398 .callback = dmi_matched, 399 .ident = "ASUSTeK COMPUTER INC. Z550MA", 400 .matches = { 401 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 402 DMI_MATCH(DMI_PRODUCT_NAME, "Z550MA"), 403 }, 404 .driver_data = &quirk_no_rfkill, 405 }, 406 { 407 .callback = dmi_matched, 408 .ident = "ASUSTeK COMPUTER INC. UX303UB", 409 .matches = { 410 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 411 DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"), 412 }, 413 .driver_data = &quirk_asus_ux303ub, 414 }, 415 { 416 .callback = dmi_matched, 417 .ident = "ASUSTeK COMPUTER INC. X550LB", 418 .matches = { 419 DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 420 DMI_MATCH(DMI_PRODUCT_NAME, "X550LB"), 421 }, 422 .driver_data = &quirk_asus_x550lb, 423 }, 424 {}, 425 }; 426 427 static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) 428 { 429 int ret; 430 431 quirks = &quirk_asus_unknown; 432 dmi_check_system(asus_quirks); 433 434 driver->quirks = quirks; 435 driver->panel_power = FB_BLANK_UNBLANK; 436 437 /* overwrite the wapf setting if the wapf paramater is specified */ 438 if (wapf != -1) 439 quirks->wapf = wapf; 440 else 441 wapf = quirks->wapf; 442 443 if (quirks->i8042_filter) { 444 ret = i8042_install_filter(quirks->i8042_filter); 445 if (ret) { 446 pr_warn("Unable to install key filter\n"); 447 return; 448 } 449 pr_info("Using i8042 filter function for receiving events\n"); 450 } 451 } 452 453 static const struct key_entry asus_nb_wmi_keymap[] = { 454 { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } }, 455 { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } }, 456 { KE_KEY, 0x30, { KEY_VOLUMEUP } }, 457 { KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, 458 { KE_KEY, 0x32, { KEY_MUTE } }, 459 { KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ 460 { KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */ 461 { KE_KEY, 0x40, { KEY_PREVIOUSSONG } }, 462 { KE_KEY, 0x41, { KEY_NEXTSONG } }, 463 { KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */ 464 { KE_KEY, 0x45, { KEY_PLAYPAUSE } }, 465 { KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */ 466 { KE_KEY, 0x50, { KEY_EMAIL } }, 467 { KE_KEY, 0x51, { KEY_WWW } }, 468 { KE_KEY, 0x55, { KEY_CALC } }, 469 { KE_IGNORE, 0x57, }, /* Battery mode */ 470 { KE_IGNORE, 0x58, }, /* AC mode */ 471 { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ 472 { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */ 473 { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */ 474 { KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */ 475 { KE_KEY, 0x60, { KEY_TOUCHPAD_ON } }, 476 { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */ 477 { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */ 478 { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */ 479 { KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */ 480 { KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */ 481 { KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */ 482 { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ 483 { KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, 484 { KE_IGNORE, 0x6E, }, /* Low Battery notification */ 485 { KE_KEY, 0x7a, { KEY_ALS_TOGGLE } }, /* Ambient Light Sensor Toggle */ 486 { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ 487 { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */ 488 { KE_KEY, 0x82, { KEY_CAMERA } }, 489 { KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */ 490 { KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */ 491 { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ 492 { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ 493 { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ 494 { KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */ 495 { KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */ 496 { KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */ 497 { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ 498 { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */ 499 { KE_KEY, 0x95, { KEY_MEDIA } }, 500 { KE_KEY, 0x99, { KEY_PHONE } }, 501 { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ 502 { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ 503 { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ 504 { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ 505 { KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */ 506 { KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */ 507 { KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */ 508 { KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */ 509 { KE_KEY, 0xB5, { KEY_CALC } }, 510 { KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, 511 { KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } }, 512 { KE_IGNORE, 0xC6, }, /* Ambient Light Sensor notification */ 513 { KE_END, 0}, 514 }; 515 516 static struct asus_wmi_driver asus_nb_wmi_driver = { 517 .name = ASUS_NB_WMI_FILE, 518 .owner = THIS_MODULE, 519 .event_guid = ASUS_NB_WMI_EVENT_GUID, 520 .keymap = asus_nb_wmi_keymap, 521 .input_name = "Asus WMI hotkeys", 522 .input_phys = ASUS_NB_WMI_FILE "/input0", 523 .detect_quirks = asus_nb_wmi_quirks, 524 }; 525 526 527 static int __init asus_nb_wmi_init(void) 528 { 529 return asus_wmi_register_driver(&asus_nb_wmi_driver); 530 } 531 532 static void __exit asus_nb_wmi_exit(void) 533 { 534 asus_wmi_unregister_driver(&asus_nb_wmi_driver); 535 } 536 537 module_init(asus_nb_wmi_init); 538 module_exit(asus_nb_wmi_exit); 539