15fc14680SDmitry Torokhov /* 25fc14680SDmitry Torokhov * Wistron laptop button driver 35fc14680SDmitry Torokhov * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz> 484b256a6SBernhard Rosenkraenzer * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org> 5a5b0cc80SDmitry Torokhov * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru> 65fc14680SDmitry Torokhov * 75fc14680SDmitry Torokhov * You can redistribute and/or modify this program under the terms of the 85fc14680SDmitry Torokhov * GNU General Public License version 2 as published by the Free Software 95fc14680SDmitry Torokhov * Foundation. 105fc14680SDmitry Torokhov * 115fc14680SDmitry Torokhov * This program is distributed in the hope that it will be useful, but 125fc14680SDmitry Torokhov * WITHOUT ANY WARRANTY; without even the implied warranty of 135fc14680SDmitry Torokhov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 145fc14680SDmitry Torokhov * Public License for more details. 155fc14680SDmitry Torokhov * 165fc14680SDmitry Torokhov * You should have received a copy of the GNU General Public License along 175fc14680SDmitry Torokhov * with this program; if not, write to the Free Software Foundation, Inc., 185fc14680SDmitry Torokhov * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. 195fc14680SDmitry Torokhov */ 205fc14680SDmitry Torokhov #include <asm/io.h> 215fc14680SDmitry Torokhov #include <linux/dmi.h> 225fc14680SDmitry Torokhov #include <linux/init.h> 235fc14680SDmitry Torokhov #include <linux/input.h> 245fc14680SDmitry Torokhov #include <linux/interrupt.h> 255fc14680SDmitry Torokhov #include <linux/kernel.h> 265fc14680SDmitry Torokhov #include <linux/mc146818rtc.h> 275fc14680SDmitry Torokhov #include <linux/module.h> 285fc14680SDmitry Torokhov #include <linux/preempt.h> 295fc14680SDmitry Torokhov #include <linux/string.h> 305fc14680SDmitry Torokhov #include <linux/timer.h> 315fc14680SDmitry Torokhov #include <linux/types.h> 32a5b0cc80SDmitry Torokhov #include <linux/platform_device.h> 335fc14680SDmitry Torokhov 345fc14680SDmitry Torokhov /* 355fc14680SDmitry Torokhov * Number of attempts to read data from queue per poll; 365fc14680SDmitry Torokhov * the queue can hold up to 31 entries 375fc14680SDmitry Torokhov */ 385fc14680SDmitry Torokhov #define MAX_POLL_ITERATIONS 64 395fc14680SDmitry Torokhov 405fc14680SDmitry Torokhov #define POLL_FREQUENCY 10 /* Number of polls per second */ 415fc14680SDmitry Torokhov 425fc14680SDmitry Torokhov #if POLL_FREQUENCY > HZ 435fc14680SDmitry Torokhov #error "POLL_FREQUENCY too high" 445fc14680SDmitry Torokhov #endif 455fc14680SDmitry Torokhov 4684b256a6SBernhard Rosenkraenzer /* BIOS subsystem IDs */ 4784b256a6SBernhard Rosenkraenzer #define WIFI 0x35 4884b256a6SBernhard Rosenkraenzer #define BLUETOOTH 0x34 4984b256a6SBernhard Rosenkraenzer 505fc14680SDmitry Torokhov MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>"); 515fc14680SDmitry Torokhov MODULE_DESCRIPTION("Wistron laptop button driver"); 525fc14680SDmitry Torokhov MODULE_LICENSE("GPL v2"); 535fc14680SDmitry Torokhov MODULE_VERSION("0.1"); 545fc14680SDmitry Torokhov 555fc14680SDmitry Torokhov static int force; /* = 0; */ 565fc14680SDmitry Torokhov module_param(force, bool, 0); 575fc14680SDmitry Torokhov MODULE_PARM_DESC(force, "Load even if computer is not in database"); 585fc14680SDmitry Torokhov 595fc14680SDmitry Torokhov static char *keymap_name; /* = NULL; */ 605fc14680SDmitry Torokhov module_param_named(keymap, keymap_name, charp, 0); 615fc14680SDmitry Torokhov MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected"); 625fc14680SDmitry Torokhov 63a5b0cc80SDmitry Torokhov static struct platform_device *wistron_device; 64a5b0cc80SDmitry Torokhov 655fc14680SDmitry Torokhov /* BIOS interface implementation */ 665fc14680SDmitry Torokhov 675fc14680SDmitry Torokhov static void __iomem *bios_entry_point; /* BIOS routine entry point */ 685fc14680SDmitry Torokhov static void __iomem *bios_code_map_base; 695fc14680SDmitry Torokhov static void __iomem *bios_data_map_base; 705fc14680SDmitry Torokhov 715fc14680SDmitry Torokhov static u8 cmos_address; 725fc14680SDmitry Torokhov 735fc14680SDmitry Torokhov struct regs { 745fc14680SDmitry Torokhov u32 eax, ebx, ecx; 755fc14680SDmitry Torokhov }; 765fc14680SDmitry Torokhov 775fc14680SDmitry Torokhov static void call_bios(struct regs *regs) 785fc14680SDmitry Torokhov { 795fc14680SDmitry Torokhov unsigned long flags; 805fc14680SDmitry Torokhov 815fc14680SDmitry Torokhov preempt_disable(); 825fc14680SDmitry Torokhov local_irq_save(flags); 835fc14680SDmitry Torokhov asm volatile ("pushl %%ebp;" 845fc14680SDmitry Torokhov "movl %7, %%ebp;" 855fc14680SDmitry Torokhov "call *%6;" 865fc14680SDmitry Torokhov "popl %%ebp" 875fc14680SDmitry Torokhov : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx) 885fc14680SDmitry Torokhov : "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx), 895fc14680SDmitry Torokhov "m" (bios_entry_point), "m" (bios_data_map_base) 905fc14680SDmitry Torokhov : "edx", "edi", "esi", "memory"); 915fc14680SDmitry Torokhov local_irq_restore(flags); 925fc14680SDmitry Torokhov preempt_enable(); 935fc14680SDmitry Torokhov } 945fc14680SDmitry Torokhov 955fc14680SDmitry Torokhov static size_t __init locate_wistron_bios(void __iomem *base) 965fc14680SDmitry Torokhov { 975fc14680SDmitry Torokhov static const unsigned char __initdata signature[] = 985fc14680SDmitry Torokhov { 0x42, 0x21, 0x55, 0x30 }; 995fc14680SDmitry Torokhov size_t offset; 1005fc14680SDmitry Torokhov 1015fc14680SDmitry Torokhov for (offset = 0; offset < 0x10000; offset += 0x10) { 1025fc14680SDmitry Torokhov if (check_signature(base + offset, signature, 1035fc14680SDmitry Torokhov sizeof(signature)) != 0) 1045fc14680SDmitry Torokhov return offset; 1055fc14680SDmitry Torokhov } 1065fc14680SDmitry Torokhov return -1; 1075fc14680SDmitry Torokhov } 1085fc14680SDmitry Torokhov 1095fc14680SDmitry Torokhov static int __init map_bios(void) 1105fc14680SDmitry Torokhov { 1115fc14680SDmitry Torokhov void __iomem *base; 1125fc14680SDmitry Torokhov size_t offset; 1135fc14680SDmitry Torokhov u32 entry_point; 1145fc14680SDmitry Torokhov 1155fc14680SDmitry Torokhov base = ioremap(0xF0000, 0x10000); /* Can't fail */ 1165fc14680SDmitry Torokhov offset = locate_wistron_bios(base); 1175fc14680SDmitry Torokhov if (offset < 0) { 1185fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: BIOS entry point not found\n"); 1195fc14680SDmitry Torokhov iounmap(base); 1205fc14680SDmitry Torokhov return -ENODEV; 1215fc14680SDmitry Torokhov } 1225fc14680SDmitry Torokhov 1235fc14680SDmitry Torokhov entry_point = readl(base + offset + 5); 1245fc14680SDmitry Torokhov printk(KERN_DEBUG 1255fc14680SDmitry Torokhov "wistron_btns: BIOS signature found at %p, entry point %08X\n", 1265fc14680SDmitry Torokhov base + offset, entry_point); 1275fc14680SDmitry Torokhov 1285fc14680SDmitry Torokhov if (entry_point >= 0xF0000) { 1295fc14680SDmitry Torokhov bios_code_map_base = base; 1305fc14680SDmitry Torokhov bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF); 1315fc14680SDmitry Torokhov } else { 1325fc14680SDmitry Torokhov iounmap(base); 1335fc14680SDmitry Torokhov bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000); 1345fc14680SDmitry Torokhov if (bios_code_map_base == NULL) { 1355fc14680SDmitry Torokhov printk(KERN_ERR 1365fc14680SDmitry Torokhov "wistron_btns: Can't map BIOS code at %08X\n", 1375fc14680SDmitry Torokhov entry_point & ~0x3FFF); 1385fc14680SDmitry Torokhov goto err; 1395fc14680SDmitry Torokhov } 1405fc14680SDmitry Torokhov bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF); 1415fc14680SDmitry Torokhov } 1425fc14680SDmitry Torokhov /* The Windows driver maps 0x10000 bytes, we keep only one page... */ 1435fc14680SDmitry Torokhov bios_data_map_base = ioremap(0x400, 0xc00); 1445fc14680SDmitry Torokhov if (bios_data_map_base == NULL) { 1455fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: Can't map BIOS data\n"); 1465fc14680SDmitry Torokhov goto err_code; 1475fc14680SDmitry Torokhov } 1485fc14680SDmitry Torokhov return 0; 1495fc14680SDmitry Torokhov 1505fc14680SDmitry Torokhov err_code: 1515fc14680SDmitry Torokhov iounmap(bios_code_map_base); 1525fc14680SDmitry Torokhov err: 1535fc14680SDmitry Torokhov return -ENOMEM; 1545fc14680SDmitry Torokhov } 1555fc14680SDmitry Torokhov 15622a397e2SDmitry Torokhov static inline void unmap_bios(void) 1575fc14680SDmitry Torokhov { 1585fc14680SDmitry Torokhov iounmap(bios_code_map_base); 1595fc14680SDmitry Torokhov iounmap(bios_data_map_base); 1605fc14680SDmitry Torokhov } 1615fc14680SDmitry Torokhov 1625fc14680SDmitry Torokhov /* BIOS calls */ 1635fc14680SDmitry Torokhov 1645fc14680SDmitry Torokhov static u16 bios_pop_queue(void) 1655fc14680SDmitry Torokhov { 1665fc14680SDmitry Torokhov struct regs regs; 1675fc14680SDmitry Torokhov 1685fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 1695fc14680SDmitry Torokhov regs.eax = 0x9610; 1705fc14680SDmitry Torokhov regs.ebx = 0x061C; 1715fc14680SDmitry Torokhov regs.ecx = 0x0000; 1725fc14680SDmitry Torokhov call_bios(®s); 1735fc14680SDmitry Torokhov 1745fc14680SDmitry Torokhov return regs.eax; 1755fc14680SDmitry Torokhov } 1765fc14680SDmitry Torokhov 1775fc14680SDmitry Torokhov static void __init bios_attach(void) 1785fc14680SDmitry Torokhov { 1795fc14680SDmitry Torokhov struct regs regs; 1805fc14680SDmitry Torokhov 1815fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 1825fc14680SDmitry Torokhov regs.eax = 0x9610; 1835fc14680SDmitry Torokhov regs.ebx = 0x012E; 1845fc14680SDmitry Torokhov call_bios(®s); 1855fc14680SDmitry Torokhov } 1865fc14680SDmitry Torokhov 18722a397e2SDmitry Torokhov static void bios_detach(void) 1885fc14680SDmitry Torokhov { 1895fc14680SDmitry Torokhov struct regs regs; 1905fc14680SDmitry Torokhov 1915fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 1925fc14680SDmitry Torokhov regs.eax = 0x9610; 1935fc14680SDmitry Torokhov regs.ebx = 0x002E; 1945fc14680SDmitry Torokhov call_bios(®s); 1955fc14680SDmitry Torokhov } 1965fc14680SDmitry Torokhov 1975fc14680SDmitry Torokhov static u8 __init bios_get_cmos_address(void) 1985fc14680SDmitry Torokhov { 1995fc14680SDmitry Torokhov struct regs regs; 2005fc14680SDmitry Torokhov 2015fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 2025fc14680SDmitry Torokhov regs.eax = 0x9610; 2035fc14680SDmitry Torokhov regs.ebx = 0x051C; 2045fc14680SDmitry Torokhov call_bios(®s); 2055fc14680SDmitry Torokhov 2065fc14680SDmitry Torokhov return regs.ecx; 2075fc14680SDmitry Torokhov } 2085fc14680SDmitry Torokhov 20984b256a6SBernhard Rosenkraenzer static u16 __init bios_get_default_setting(u8 subsys) 2105fc14680SDmitry Torokhov { 2115fc14680SDmitry Torokhov struct regs regs; 2125fc14680SDmitry Torokhov 2135fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 2145fc14680SDmitry Torokhov regs.eax = 0x9610; 21584b256a6SBernhard Rosenkraenzer regs.ebx = 0x0200 | subsys; 2165fc14680SDmitry Torokhov call_bios(®s); 2175fc14680SDmitry Torokhov 2185fc14680SDmitry Torokhov return regs.eax; 2195fc14680SDmitry Torokhov } 2205fc14680SDmitry Torokhov 22184b256a6SBernhard Rosenkraenzer static void bios_set_state(u8 subsys, int enable) 2225fc14680SDmitry Torokhov { 2235fc14680SDmitry Torokhov struct regs regs; 2245fc14680SDmitry Torokhov 2255fc14680SDmitry Torokhov memset(®s, 0, sizeof (regs)); 2265fc14680SDmitry Torokhov regs.eax = 0x9610; 22784b256a6SBernhard Rosenkraenzer regs.ebx = (enable ? 0x0100 : 0x0000) | subsys; 2285fc14680SDmitry Torokhov call_bios(®s); 2295fc14680SDmitry Torokhov } 2305fc14680SDmitry Torokhov 2315fc14680SDmitry Torokhov /* Hardware database */ 2325fc14680SDmitry Torokhov 2335fc14680SDmitry Torokhov struct key_entry { 2345fc14680SDmitry Torokhov char type; /* See KE_* below */ 2355fc14680SDmitry Torokhov u8 code; 2365fc14680SDmitry Torokhov unsigned keycode; /* For KE_KEY */ 2375fc14680SDmitry Torokhov }; 2385fc14680SDmitry Torokhov 23984b256a6SBernhard Rosenkraenzer enum { KE_END, KE_KEY, KE_WIFI, KE_BLUETOOTH }; 2405fc14680SDmitry Torokhov 2415fc14680SDmitry Torokhov static const struct key_entry *keymap; /* = NULL; Current key map */ 2425fc14680SDmitry Torokhov static int have_wifi; 24384b256a6SBernhard Rosenkraenzer static int have_bluetooth; 2445fc14680SDmitry Torokhov 2455fc14680SDmitry Torokhov static int __init dmi_matched(struct dmi_system_id *dmi) 2465fc14680SDmitry Torokhov { 2475fc14680SDmitry Torokhov const struct key_entry *key; 2485fc14680SDmitry Torokhov 2495fc14680SDmitry Torokhov keymap = dmi->driver_data; 2505fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 2515fc14680SDmitry Torokhov if (key->type == KE_WIFI) { 2525fc14680SDmitry Torokhov have_wifi = 1; 2535fc14680SDmitry Torokhov break; 25484b256a6SBernhard Rosenkraenzer } else if (key->type == KE_BLUETOOTH) { 25584b256a6SBernhard Rosenkraenzer have_bluetooth = 1; 25684b256a6SBernhard Rosenkraenzer break; 2575fc14680SDmitry Torokhov } 2585fc14680SDmitry Torokhov } 2595fc14680SDmitry Torokhov return 1; 2605fc14680SDmitry Torokhov } 2615fc14680SDmitry Torokhov 2625fc14680SDmitry Torokhov static struct key_entry keymap_empty[] = { 2635fc14680SDmitry Torokhov { KE_END, 0 } 2645fc14680SDmitry Torokhov }; 2655fc14680SDmitry Torokhov 2665fc14680SDmitry Torokhov static struct key_entry keymap_fs_amilo_pro_v2000[] = { 2675fc14680SDmitry Torokhov { KE_KEY, 0x01, KEY_HELP }, 2685fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2695fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 2705fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 2715fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 2725fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 2735fc14680SDmitry Torokhov { KE_END, 0 } 2745fc14680SDmitry Torokhov }; 2755fc14680SDmitry Torokhov 2765fc14680SDmitry Torokhov static struct key_entry keymap_wistron_ms2141[] = { 2775fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2785fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 2795fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 2805fc14680SDmitry Torokhov { KE_KEY, 0x22, KEY_REWIND }, 2815fc14680SDmitry Torokhov { KE_KEY, 0x23, KEY_FORWARD }, 2825fc14680SDmitry Torokhov { KE_KEY, 0x24, KEY_PLAYPAUSE }, 2835fc14680SDmitry Torokhov { KE_KEY, 0x25, KEY_STOPCD }, 2845fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 2855fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 2865fc14680SDmitry Torokhov { KE_END, 0 } 2875fc14680SDmitry Torokhov }; 2885fc14680SDmitry Torokhov 28984b256a6SBernhard Rosenkraenzer static struct key_entry keymap_acer_aspire_1500[] = { 29084b256a6SBernhard Rosenkraenzer { KE_KEY, 0x11, KEY_PROG1 }, 29184b256a6SBernhard Rosenkraenzer { KE_KEY, 0x12, KEY_PROG2 }, 29284b256a6SBernhard Rosenkraenzer { KE_WIFI, 0x30, 0 }, 29384b256a6SBernhard Rosenkraenzer { KE_KEY, 0x31, KEY_MAIL }, 29484b256a6SBernhard Rosenkraenzer { KE_KEY, 0x36, KEY_WWW }, 29584b256a6SBernhard Rosenkraenzer { KE_BLUETOOTH, 0x44, 0 }, 29684b256a6SBernhard Rosenkraenzer { KE_END, 0 } 29784b256a6SBernhard Rosenkraenzer }; 29884b256a6SBernhard Rosenkraenzer 2995fc14680SDmitry Torokhov /* 3005fc14680SDmitry Torokhov * If your machine is not here (which is currently rather likely), please send 3015fc14680SDmitry Torokhov * a list of buttons and their key codes (reported when loading this module 3025fc14680SDmitry Torokhov * with force=1) and the output of dmidecode to $MODULE_AUTHOR. 3035fc14680SDmitry Torokhov */ 3045fc14680SDmitry Torokhov static struct dmi_system_id dmi_ids[] = { 3055fc14680SDmitry Torokhov { 3065fc14680SDmitry Torokhov .callback = dmi_matched, 3075fc14680SDmitry Torokhov .ident = "Fujitsu-Siemens Amilo Pro V2000", 3085fc14680SDmitry Torokhov .matches = { 3095fc14680SDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3105fc14680SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), 3115fc14680SDmitry Torokhov }, 3125fc14680SDmitry Torokhov .driver_data = keymap_fs_amilo_pro_v2000 3135fc14680SDmitry Torokhov }, 31484b256a6SBernhard Rosenkraenzer { 31584b256a6SBernhard Rosenkraenzer .callback = dmi_matched, 31684b256a6SBernhard Rosenkraenzer .ident = "Acer Aspire 1500", 31784b256a6SBernhard Rosenkraenzer .matches = { 31884b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 31984b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), 32084b256a6SBernhard Rosenkraenzer }, 32184b256a6SBernhard Rosenkraenzer .driver_data = keymap_acer_aspire_1500 32284b256a6SBernhard Rosenkraenzer }, 3235fc14680SDmitry Torokhov { 0, } 3245fc14680SDmitry Torokhov }; 3255fc14680SDmitry Torokhov 3265fc14680SDmitry Torokhov static int __init select_keymap(void) 3275fc14680SDmitry Torokhov { 3285fc14680SDmitry Torokhov if (keymap_name != NULL) { 3295fc14680SDmitry Torokhov if (strcmp (keymap_name, "1557/MS2141") == 0) 3305fc14680SDmitry Torokhov keymap = keymap_wistron_ms2141; 3315fc14680SDmitry Torokhov else { 3325fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: Keymap unknown\n"); 3335fc14680SDmitry Torokhov return -EINVAL; 3345fc14680SDmitry Torokhov } 3355fc14680SDmitry Torokhov } 3365fc14680SDmitry Torokhov dmi_check_system(dmi_ids); 3375fc14680SDmitry Torokhov if (keymap == NULL) { 3385fc14680SDmitry Torokhov if (!force) { 3395fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: System unknown\n"); 3405fc14680SDmitry Torokhov return -ENODEV; 3415fc14680SDmitry Torokhov } 3425fc14680SDmitry Torokhov keymap = keymap_empty; 3435fc14680SDmitry Torokhov } 3445fc14680SDmitry Torokhov return 0; 3455fc14680SDmitry Torokhov } 3465fc14680SDmitry Torokhov 3475fc14680SDmitry Torokhov /* Input layer interface */ 3485fc14680SDmitry Torokhov 34922a397e2SDmitry Torokhov static struct input_dev *input_dev; 3505fc14680SDmitry Torokhov 35122a397e2SDmitry Torokhov static int __init setup_input_dev(void) 3525fc14680SDmitry Torokhov { 3535fc14680SDmitry Torokhov const struct key_entry *key; 35422a397e2SDmitry Torokhov int error; 35522a397e2SDmitry Torokhov 35622a397e2SDmitry Torokhov input_dev = input_allocate_device(); 35722a397e2SDmitry Torokhov if (!input_dev) 35822a397e2SDmitry Torokhov return -ENOMEM; 35922a397e2SDmitry Torokhov 36022a397e2SDmitry Torokhov input_dev->name = "Wistron laptop buttons"; 36122a397e2SDmitry Torokhov input_dev->phys = "wistron/input0"; 36222a397e2SDmitry Torokhov input_dev->id.bustype = BUS_HOST; 363a5b0cc80SDmitry Torokhov input_dev->cdev.dev = &wistron_device->dev; 3645fc14680SDmitry Torokhov 3655fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 3665fc14680SDmitry Torokhov if (key->type == KE_KEY) { 36722a397e2SDmitry Torokhov input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); 36822a397e2SDmitry Torokhov set_bit(key->keycode, input_dev->keybit); 3695fc14680SDmitry Torokhov } 3705fc14680SDmitry Torokhov } 3715fc14680SDmitry Torokhov 37222a397e2SDmitry Torokhov error = input_register_device(input_dev); 37322a397e2SDmitry Torokhov if (error) { 37422a397e2SDmitry Torokhov input_free_device(input_dev); 37522a397e2SDmitry Torokhov return error; 37622a397e2SDmitry Torokhov } 37722a397e2SDmitry Torokhov 37822a397e2SDmitry Torokhov return 0; 3795fc14680SDmitry Torokhov } 3805fc14680SDmitry Torokhov 3815fc14680SDmitry Torokhov static void report_key(unsigned keycode) 3825fc14680SDmitry Torokhov { 38322a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 1); 38422a397e2SDmitry Torokhov input_sync(input_dev); 38522a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 0); 38622a397e2SDmitry Torokhov input_sync(input_dev); 3875fc14680SDmitry Torokhov } 3885fc14680SDmitry Torokhov 3895fc14680SDmitry Torokhov /* Driver core */ 3905fc14680SDmitry Torokhov 3915fc14680SDmitry Torokhov static int wifi_enabled; 39284b256a6SBernhard Rosenkraenzer static int bluetooth_enabled; 3935fc14680SDmitry Torokhov 3945fc14680SDmitry Torokhov static void poll_bios(unsigned long); 3955fc14680SDmitry Torokhov 3965fc14680SDmitry Torokhov static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); 3975fc14680SDmitry Torokhov 3985fc14680SDmitry Torokhov static void handle_key(u8 code) 3995fc14680SDmitry Torokhov { 4005fc14680SDmitry Torokhov const struct key_entry *key; 4015fc14680SDmitry Torokhov 4025fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 4035fc14680SDmitry Torokhov if (code == key->code) { 4045fc14680SDmitry Torokhov switch (key->type) { 4055fc14680SDmitry Torokhov case KE_KEY: 4065fc14680SDmitry Torokhov report_key(key->keycode); 4075fc14680SDmitry Torokhov break; 4085fc14680SDmitry Torokhov 4095fc14680SDmitry Torokhov case KE_WIFI: 4105fc14680SDmitry Torokhov if (have_wifi) { 4115fc14680SDmitry Torokhov wifi_enabled = !wifi_enabled; 41284b256a6SBernhard Rosenkraenzer bios_set_state(WIFI, wifi_enabled); 41384b256a6SBernhard Rosenkraenzer } 41484b256a6SBernhard Rosenkraenzer break; 41584b256a6SBernhard Rosenkraenzer 41684b256a6SBernhard Rosenkraenzer case KE_BLUETOOTH: 41784b256a6SBernhard Rosenkraenzer if (have_bluetooth) { 41884b256a6SBernhard Rosenkraenzer bluetooth_enabled = !bluetooth_enabled; 41984b256a6SBernhard Rosenkraenzer bios_set_state(BLUETOOTH, bluetooth_enabled); 4205fc14680SDmitry Torokhov } 4215fc14680SDmitry Torokhov break; 4225fc14680SDmitry Torokhov 4235fc14680SDmitry Torokhov case KE_END: 4245fc14680SDmitry Torokhov default: 4255fc14680SDmitry Torokhov BUG(); 4265fc14680SDmitry Torokhov } 4275fc14680SDmitry Torokhov return; 4285fc14680SDmitry Torokhov } 4295fc14680SDmitry Torokhov } 4305fc14680SDmitry Torokhov printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); 4315fc14680SDmitry Torokhov } 4325fc14680SDmitry Torokhov 4335fc14680SDmitry Torokhov static void poll_bios(unsigned long discard) 4345fc14680SDmitry Torokhov { 4355fc14680SDmitry Torokhov u8 qlen; 4365fc14680SDmitry Torokhov u16 val; 4375fc14680SDmitry Torokhov 4385fc14680SDmitry Torokhov for (;;) { 4395fc14680SDmitry Torokhov qlen = CMOS_READ(cmos_address); 4405fc14680SDmitry Torokhov if (qlen == 0) 4415fc14680SDmitry Torokhov break; 4425fc14680SDmitry Torokhov val = bios_pop_queue(); 4435fc14680SDmitry Torokhov if (val != 0 && !discard) 4445fc14680SDmitry Torokhov handle_key((u8)val); 4455fc14680SDmitry Torokhov } 4465fc14680SDmitry Torokhov 4475fc14680SDmitry Torokhov mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); 4485fc14680SDmitry Torokhov } 4495fc14680SDmitry Torokhov 450a5b0cc80SDmitry Torokhov static int wistron_suspend(struct platform_device *dev, pm_message_t state) 451a5b0cc80SDmitry Torokhov { 452a5b0cc80SDmitry Torokhov del_timer_sync(&poll_timer); 453a5b0cc80SDmitry Torokhov 454a5b0cc80SDmitry Torokhov return 0; 455a5b0cc80SDmitry Torokhov } 456a5b0cc80SDmitry Torokhov 457a5b0cc80SDmitry Torokhov static int wistron_resume(struct platform_device *dev) 458a5b0cc80SDmitry Torokhov { 459a5b0cc80SDmitry Torokhov if (have_wifi) 460a5b0cc80SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 461a5b0cc80SDmitry Torokhov 462a5b0cc80SDmitry Torokhov if (have_bluetooth) 463a5b0cc80SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 464a5b0cc80SDmitry Torokhov 465a5b0cc80SDmitry Torokhov poll_bios(1); 466a5b0cc80SDmitry Torokhov 467a5b0cc80SDmitry Torokhov return 0; 468a5b0cc80SDmitry Torokhov } 469a5b0cc80SDmitry Torokhov 470a5b0cc80SDmitry Torokhov static struct platform_driver wistron_driver = { 471a5b0cc80SDmitry Torokhov .suspend = wistron_suspend, 472a5b0cc80SDmitry Torokhov .resume = wistron_resume, 473a5b0cc80SDmitry Torokhov .driver = { 474a5b0cc80SDmitry Torokhov .name = "wistron-bios", 475a5b0cc80SDmitry Torokhov }, 476a5b0cc80SDmitry Torokhov }; 477a5b0cc80SDmitry Torokhov 4785fc14680SDmitry Torokhov static int __init wb_module_init(void) 4795fc14680SDmitry Torokhov { 4805fc14680SDmitry Torokhov int err; 4815fc14680SDmitry Torokhov 4825fc14680SDmitry Torokhov err = select_keymap(); 4835fc14680SDmitry Torokhov if (err) 4845fc14680SDmitry Torokhov return err; 48522a397e2SDmitry Torokhov 4865fc14680SDmitry Torokhov err = map_bios(); 4875fc14680SDmitry Torokhov if (err) 4885fc14680SDmitry Torokhov return err; 48922a397e2SDmitry Torokhov 4905fc14680SDmitry Torokhov bios_attach(); 4915fc14680SDmitry Torokhov cmos_address = bios_get_cmos_address(); 49222a397e2SDmitry Torokhov 493a5b0cc80SDmitry Torokhov err = platform_driver_register(&wistron_driver); 494a5b0cc80SDmitry Torokhov if (err) 495a5b0cc80SDmitry Torokhov goto err_detach_bios; 496a5b0cc80SDmitry Torokhov 497a5b0cc80SDmitry Torokhov wistron_device = platform_device_register_simple("wistron-bios", -1, NULL, 0); 498a5b0cc80SDmitry Torokhov if (IS_ERR(wistron_device)) { 499a5b0cc80SDmitry Torokhov err = PTR_ERR(wistron_device); 500a5b0cc80SDmitry Torokhov goto err_unregister_driver; 501a5b0cc80SDmitry Torokhov } 502a5b0cc80SDmitry Torokhov 5035fc14680SDmitry Torokhov if (have_wifi) { 50484b256a6SBernhard Rosenkraenzer u16 wifi = bios_get_default_setting(WIFI); 50584b256a6SBernhard Rosenkraenzer if (wifi & 1) 50684b256a6SBernhard Rosenkraenzer wifi_enabled = (wifi & 2) ? 1 : 0; 50784b256a6SBernhard Rosenkraenzer else 5085fc14680SDmitry Torokhov have_wifi = 0; 50984b256a6SBernhard Rosenkraenzer 5105fc14680SDmitry Torokhov if (have_wifi) 51184b256a6SBernhard Rosenkraenzer bios_set_state(WIFI, wifi_enabled); 51284b256a6SBernhard Rosenkraenzer } 51322a397e2SDmitry Torokhov 51484b256a6SBernhard Rosenkraenzer if (have_bluetooth) { 51584b256a6SBernhard Rosenkraenzer u16 bt = bios_get_default_setting(BLUETOOTH); 51684b256a6SBernhard Rosenkraenzer if (bt & 1) 51784b256a6SBernhard Rosenkraenzer bluetooth_enabled = (bt & 2) ? 1 : 0; 51884b256a6SBernhard Rosenkraenzer else 51984b256a6SBernhard Rosenkraenzer have_bluetooth = 0; 52084b256a6SBernhard Rosenkraenzer 52184b256a6SBernhard Rosenkraenzer if (have_bluetooth) 52284b256a6SBernhard Rosenkraenzer bios_set_state(BLUETOOTH, bluetooth_enabled); 5235fc14680SDmitry Torokhov } 5245fc14680SDmitry Torokhov 52522a397e2SDmitry Torokhov err = setup_input_dev(); 526a5b0cc80SDmitry Torokhov if (err) 527a5b0cc80SDmitry Torokhov goto err_unregister_device; 5285fc14680SDmitry Torokhov 5295fc14680SDmitry Torokhov poll_bios(1); /* Flush stale event queue and arm timer */ 5305fc14680SDmitry Torokhov 5315fc14680SDmitry Torokhov return 0; 532a5b0cc80SDmitry Torokhov 533a5b0cc80SDmitry Torokhov err_unregister_device: 534a5b0cc80SDmitry Torokhov platform_device_unregister(wistron_device); 535a5b0cc80SDmitry Torokhov err_unregister_driver: 536a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 537a5b0cc80SDmitry Torokhov err_detach_bios: 538a5b0cc80SDmitry Torokhov bios_detach(); 539a5b0cc80SDmitry Torokhov unmap_bios(); 540a5b0cc80SDmitry Torokhov 541a5b0cc80SDmitry Torokhov return err; 5425fc14680SDmitry Torokhov } 5435fc14680SDmitry Torokhov 5445fc14680SDmitry Torokhov static void __exit wb_module_exit(void) 5455fc14680SDmitry Torokhov { 5465fc14680SDmitry Torokhov del_timer_sync(&poll_timer); 54722a397e2SDmitry Torokhov input_unregister_device(input_dev); 548a5b0cc80SDmitry Torokhov platform_device_unregister(wistron_device); 549a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 5505fc14680SDmitry Torokhov bios_detach(); 5515fc14680SDmitry Torokhov unmap_bios(); 5525fc14680SDmitry Torokhov } 5535fc14680SDmitry Torokhov 5545fc14680SDmitry Torokhov module_init(wb_module_init); 5555fc14680SDmitry Torokhov module_exit(wb_module_exit); 556