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 95c28c3583SMiloslav Trmac static ssize_t __init locate_wistron_bios(void __iomem *base) 965fc14680SDmitry Torokhov { 97c7948989SAndrew Morton static unsigned char __initdata signature[] = 985fc14680SDmitry Torokhov { 0x42, 0x21, 0x55, 0x30 }; 99c28c3583SMiloslav Trmac ssize_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; 112c28c3583SMiloslav Trmac ssize_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 177e7c3aad5SDmitry Torokhov static void __devinit 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 197e7c3aad5SDmitry Torokhov static u8 __devinit 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 209e7c3aad5SDmitry Torokhov static u16 __devinit 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 262c7948989SAndrew Morton static struct key_entry keymap_empty[] __initdata = { 2635fc14680SDmitry Torokhov { KE_END, 0 } 2645fc14680SDmitry Torokhov }; 2655fc14680SDmitry Torokhov 266c7948989SAndrew Morton static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = { 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 276c7948989SAndrew Morton static struct key_entry keymap_fujitsu_n3510[] __initdata = { 277e2aa507aSJohn Reed Riley { KE_KEY, 0x11, KEY_PROG1 }, 278e2aa507aSJohn Reed Riley { KE_KEY, 0x12, KEY_PROG2 }, 279e2aa507aSJohn Reed Riley { KE_KEY, 0x36, KEY_WWW }, 280e2aa507aSJohn Reed Riley { KE_KEY, 0x31, KEY_MAIL }, 281e2aa507aSJohn Reed Riley { KE_KEY, 0x71, KEY_STOPCD }, 282e2aa507aSJohn Reed Riley { KE_KEY, 0x72, KEY_PLAYPAUSE }, 283e2aa507aSJohn Reed Riley { KE_KEY, 0x74, KEY_REWIND }, 284e2aa507aSJohn Reed Riley { KE_KEY, 0x78, KEY_FORWARD }, 285e2aa507aSJohn Reed Riley { KE_END, 0 } 286e2aa507aSJohn Reed Riley }; 287e2aa507aSJohn Reed Riley 288c7948989SAndrew Morton static struct key_entry keymap_wistron_ms2111[] __initdata = { 2899000195bSFrank de Lange { KE_KEY, 0x11, KEY_PROG1 }, 2909000195bSFrank de Lange { KE_KEY, 0x12, KEY_PROG2 }, 2919000195bSFrank de Lange { KE_KEY, 0x13, KEY_PROG3 }, 2929000195bSFrank de Lange { KE_KEY, 0x31, KEY_MAIL }, 2939000195bSFrank de Lange { KE_KEY, 0x36, KEY_WWW }, 2949000195bSFrank de Lange { KE_END, 0 } 2959000195bSFrank de Lange }; 2969000195bSFrank de Lange 297c7948989SAndrew Morton static struct key_entry keymap_wistron_ms2141[] __initdata = { 2985fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2995fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 3005fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 3015fc14680SDmitry Torokhov { KE_KEY, 0x22, KEY_REWIND }, 3025fc14680SDmitry Torokhov { KE_KEY, 0x23, KEY_FORWARD }, 3035fc14680SDmitry Torokhov { KE_KEY, 0x24, KEY_PLAYPAUSE }, 3045fc14680SDmitry Torokhov { KE_KEY, 0x25, KEY_STOPCD }, 3055fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 3065fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 3075fc14680SDmitry Torokhov { KE_END, 0 } 3085fc14680SDmitry Torokhov }; 3095fc14680SDmitry Torokhov 310c7948989SAndrew Morton static struct key_entry keymap_acer_aspire_1500[] __initdata = { 31184b256a6SBernhard Rosenkraenzer { KE_KEY, 0x11, KEY_PROG1 }, 31284b256a6SBernhard Rosenkraenzer { KE_KEY, 0x12, KEY_PROG2 }, 31384b256a6SBernhard Rosenkraenzer { KE_WIFI, 0x30, 0 }, 31484b256a6SBernhard Rosenkraenzer { KE_KEY, 0x31, KEY_MAIL }, 31584b256a6SBernhard Rosenkraenzer { KE_KEY, 0x36, KEY_WWW }, 31684b256a6SBernhard Rosenkraenzer { KE_BLUETOOTH, 0x44, 0 }, 31784b256a6SBernhard Rosenkraenzer { KE_END, 0 } 31884b256a6SBernhard Rosenkraenzer }; 31984b256a6SBernhard Rosenkraenzer 320c7948989SAndrew Morton static struct key_entry keymap_acer_travelmate_240[] __initdata = { 32174a89c96SAshutosh Naik { KE_KEY, 0x31, KEY_MAIL }, 32274a89c96SAshutosh Naik { KE_KEY, 0x36, KEY_WWW }, 32374a89c96SAshutosh Naik { KE_KEY, 0x11, KEY_PROG1 }, 32474a89c96SAshutosh Naik { KE_KEY, 0x12, KEY_PROG2 }, 32574a89c96SAshutosh Naik { KE_BLUETOOTH, 0x44, 0 }, 32674a89c96SAshutosh Naik { KE_WIFI, 0x30, 0 }, 32774a89c96SAshutosh Naik { KE_END, 0 } 32874a89c96SAshutosh Naik }; 32974a89c96SAshutosh Naik 330c7948989SAndrew Morton static struct key_entry keymap_aopen_1559as[] __initdata = { 331e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x01, KEY_HELP }, 332e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x06, KEY_PROG3 }, 333e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x11, KEY_PROG1 }, 334e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x12, KEY_PROG2 }, 335e107b8eeSmasc@theaterzentrum.at { KE_WIFI, 0x30, 0 }, 336e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x31, KEY_MAIL }, 337e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x36, KEY_WWW }, 3389000195bSFrank de Lange { KE_END, 0 }, 339e107b8eeSmasc@theaterzentrum.at }; 340e107b8eeSmasc@theaterzentrum.at 3415fc14680SDmitry Torokhov /* 3425fc14680SDmitry Torokhov * If your machine is not here (which is currently rather likely), please send 3435fc14680SDmitry Torokhov * a list of buttons and their key codes (reported when loading this module 3445fc14680SDmitry Torokhov * with force=1) and the output of dmidecode to $MODULE_AUTHOR. 3455fc14680SDmitry Torokhov */ 346c7948989SAndrew Morton static struct dmi_system_id dmi_ids[] __initdata = { 3475fc14680SDmitry Torokhov { 3485fc14680SDmitry Torokhov .callback = dmi_matched, 3495fc14680SDmitry Torokhov .ident = "Fujitsu-Siemens Amilo Pro V2000", 3505fc14680SDmitry Torokhov .matches = { 3515fc14680SDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3525fc14680SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), 3535fc14680SDmitry Torokhov }, 3545fc14680SDmitry Torokhov .driver_data = keymap_fs_amilo_pro_v2000 3555fc14680SDmitry Torokhov }, 35684b256a6SBernhard Rosenkraenzer { 35784b256a6SBernhard Rosenkraenzer .callback = dmi_matched, 3588a1b1708SStefan Rompf .ident = "Fujitsu-Siemens Amilo M7400", 3598a1b1708SStefan Rompf .matches = { 3608a1b1708SStefan Rompf DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3618a1b1708SStefan Rompf DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), 3628a1b1708SStefan Rompf }, 3638a1b1708SStefan Rompf .driver_data = keymap_fs_amilo_pro_v2000 3648a1b1708SStefan Rompf }, 3658a1b1708SStefan Rompf { 3668a1b1708SStefan Rompf .callback = dmi_matched, 367e2aa507aSJohn Reed Riley .ident = "Fujitsu N3510", 368e2aa507aSJohn Reed Riley .matches = { 369e2aa507aSJohn Reed Riley DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 370e2aa507aSJohn Reed Riley DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), 371e2aa507aSJohn Reed Riley }, 372e2aa507aSJohn Reed Riley .driver_data = keymap_fujitsu_n3510 373e2aa507aSJohn Reed Riley }, 374e2aa507aSJohn Reed Riley { 375e2aa507aSJohn Reed Riley .callback = dmi_matched, 37684b256a6SBernhard Rosenkraenzer .ident = "Acer Aspire 1500", 37784b256a6SBernhard Rosenkraenzer .matches = { 37884b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 37984b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), 38084b256a6SBernhard Rosenkraenzer }, 38184b256a6SBernhard Rosenkraenzer .driver_data = keymap_acer_aspire_1500 38284b256a6SBernhard Rosenkraenzer }, 38374a89c96SAshutosh Naik { 38474a89c96SAshutosh Naik .callback = dmi_matched, 38574a89c96SAshutosh Naik .ident = "Acer TravelMate 240", 38674a89c96SAshutosh Naik .matches = { 38774a89c96SAshutosh Naik DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 38874a89c96SAshutosh Naik DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), 38974a89c96SAshutosh Naik }, 39074a89c96SAshutosh Naik .driver_data = keymap_acer_travelmate_240 39174a89c96SAshutosh Naik }, 392e107b8eeSmasc@theaterzentrum.at { 393e107b8eeSmasc@theaterzentrum.at .callback = dmi_matched, 394e107b8eeSmasc@theaterzentrum.at .ident = "AOpen 1559AS", 395e107b8eeSmasc@theaterzentrum.at .matches = { 396e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), 397e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_BOARD_NAME, "E2U"), 398e107b8eeSmasc@theaterzentrum.at }, 399e107b8eeSmasc@theaterzentrum.at .driver_data = keymap_aopen_1559as 400e107b8eeSmasc@theaterzentrum.at }, 4019000195bSFrank de Lange { 4029000195bSFrank de Lange .callback = dmi_matched, 4039000195bSFrank de Lange .ident = "Medion MD 9783", 4049000195bSFrank de Lange .matches = { 4059000195bSFrank de Lange DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), 4069000195bSFrank de Lange DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"), 4079000195bSFrank de Lange }, 4089000195bSFrank de Lange .driver_data = keymap_wistron_ms2111 4099000195bSFrank de Lange }, 41081f0a91eSAl Viro { NULL, } 4115fc14680SDmitry Torokhov }; 4125fc14680SDmitry Torokhov 4135fc14680SDmitry Torokhov static int __init select_keymap(void) 4145fc14680SDmitry Torokhov { 4155fc14680SDmitry Torokhov if (keymap_name != NULL) { 4165fc14680SDmitry Torokhov if (strcmp (keymap_name, "1557/MS2141") == 0) 4175fc14680SDmitry Torokhov keymap = keymap_wistron_ms2141; 4185fc14680SDmitry Torokhov else { 4195fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: Keymap unknown\n"); 4205fc14680SDmitry Torokhov return -EINVAL; 4215fc14680SDmitry Torokhov } 4225fc14680SDmitry Torokhov } 4235fc14680SDmitry Torokhov dmi_check_system(dmi_ids); 4245fc14680SDmitry Torokhov if (keymap == NULL) { 4255fc14680SDmitry Torokhov if (!force) { 4265fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: System unknown\n"); 4275fc14680SDmitry Torokhov return -ENODEV; 4285fc14680SDmitry Torokhov } 4295fc14680SDmitry Torokhov keymap = keymap_empty; 4305fc14680SDmitry Torokhov } 4315fc14680SDmitry Torokhov return 0; 4325fc14680SDmitry Torokhov } 4335fc14680SDmitry Torokhov 4345fc14680SDmitry Torokhov /* Input layer interface */ 4355fc14680SDmitry Torokhov 43622a397e2SDmitry Torokhov static struct input_dev *input_dev; 4375fc14680SDmitry Torokhov 438e7c3aad5SDmitry Torokhov static int __devinit setup_input_dev(void) 4395fc14680SDmitry Torokhov { 4405fc14680SDmitry Torokhov const struct key_entry *key; 44122a397e2SDmitry Torokhov int error; 44222a397e2SDmitry Torokhov 44322a397e2SDmitry Torokhov input_dev = input_allocate_device(); 44422a397e2SDmitry Torokhov if (!input_dev) 44522a397e2SDmitry Torokhov return -ENOMEM; 44622a397e2SDmitry Torokhov 44722a397e2SDmitry Torokhov input_dev->name = "Wistron laptop buttons"; 44822a397e2SDmitry Torokhov input_dev->phys = "wistron/input0"; 44922a397e2SDmitry Torokhov input_dev->id.bustype = BUS_HOST; 450a5b0cc80SDmitry Torokhov input_dev->cdev.dev = &wistron_device->dev; 4515fc14680SDmitry Torokhov 4525fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 4535fc14680SDmitry Torokhov if (key->type == KE_KEY) { 45422a397e2SDmitry Torokhov input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); 45522a397e2SDmitry Torokhov set_bit(key->keycode, input_dev->keybit); 4565fc14680SDmitry Torokhov } 4575fc14680SDmitry Torokhov } 4585fc14680SDmitry Torokhov 45922a397e2SDmitry Torokhov error = input_register_device(input_dev); 46022a397e2SDmitry Torokhov if (error) { 46122a397e2SDmitry Torokhov input_free_device(input_dev); 46222a397e2SDmitry Torokhov return error; 46322a397e2SDmitry Torokhov } 46422a397e2SDmitry Torokhov 46522a397e2SDmitry Torokhov return 0; 4665fc14680SDmitry Torokhov } 4675fc14680SDmitry Torokhov 4685fc14680SDmitry Torokhov static void report_key(unsigned keycode) 4695fc14680SDmitry Torokhov { 47022a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 1); 47122a397e2SDmitry Torokhov input_sync(input_dev); 47222a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 0); 47322a397e2SDmitry Torokhov input_sync(input_dev); 4745fc14680SDmitry Torokhov } 4755fc14680SDmitry Torokhov 4765fc14680SDmitry Torokhov /* Driver core */ 4775fc14680SDmitry Torokhov 4785fc14680SDmitry Torokhov static int wifi_enabled; 47984b256a6SBernhard Rosenkraenzer static int bluetooth_enabled; 4805fc14680SDmitry Torokhov 4815fc14680SDmitry Torokhov static void poll_bios(unsigned long); 4825fc14680SDmitry Torokhov 4835fc14680SDmitry Torokhov static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); 4845fc14680SDmitry Torokhov 4855fc14680SDmitry Torokhov static void handle_key(u8 code) 4865fc14680SDmitry Torokhov { 4875fc14680SDmitry Torokhov const struct key_entry *key; 4885fc14680SDmitry Torokhov 4895fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 4905fc14680SDmitry Torokhov if (code == key->code) { 4915fc14680SDmitry Torokhov switch (key->type) { 4925fc14680SDmitry Torokhov case KE_KEY: 4935fc14680SDmitry Torokhov report_key(key->keycode); 4945fc14680SDmitry Torokhov break; 4955fc14680SDmitry Torokhov 4965fc14680SDmitry Torokhov case KE_WIFI: 4975fc14680SDmitry Torokhov if (have_wifi) { 4985fc14680SDmitry Torokhov wifi_enabled = !wifi_enabled; 49984b256a6SBernhard Rosenkraenzer bios_set_state(WIFI, wifi_enabled); 50084b256a6SBernhard Rosenkraenzer } 50184b256a6SBernhard Rosenkraenzer break; 50284b256a6SBernhard Rosenkraenzer 50384b256a6SBernhard Rosenkraenzer case KE_BLUETOOTH: 50484b256a6SBernhard Rosenkraenzer if (have_bluetooth) { 50584b256a6SBernhard Rosenkraenzer bluetooth_enabled = !bluetooth_enabled; 50684b256a6SBernhard Rosenkraenzer bios_set_state(BLUETOOTH, bluetooth_enabled); 5075fc14680SDmitry Torokhov } 5085fc14680SDmitry Torokhov break; 5095fc14680SDmitry Torokhov 5105fc14680SDmitry Torokhov case KE_END: 5115fc14680SDmitry Torokhov default: 5125fc14680SDmitry Torokhov BUG(); 5135fc14680SDmitry Torokhov } 5145fc14680SDmitry Torokhov return; 5155fc14680SDmitry Torokhov } 5165fc14680SDmitry Torokhov } 5175fc14680SDmitry Torokhov printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); 5185fc14680SDmitry Torokhov } 5195fc14680SDmitry Torokhov 5205fc14680SDmitry Torokhov static void poll_bios(unsigned long discard) 5215fc14680SDmitry Torokhov { 5225fc14680SDmitry Torokhov u8 qlen; 5235fc14680SDmitry Torokhov u16 val; 5245fc14680SDmitry Torokhov 5255fc14680SDmitry Torokhov for (;;) { 5265fc14680SDmitry Torokhov qlen = CMOS_READ(cmos_address); 5275fc14680SDmitry Torokhov if (qlen == 0) 5285fc14680SDmitry Torokhov break; 5295fc14680SDmitry Torokhov val = bios_pop_queue(); 5305fc14680SDmitry Torokhov if (val != 0 && !discard) 5315fc14680SDmitry Torokhov handle_key((u8)val); 5325fc14680SDmitry Torokhov } 5335fc14680SDmitry Torokhov 5345fc14680SDmitry Torokhov mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); 5355fc14680SDmitry Torokhov } 5365fc14680SDmitry Torokhov 537e7c3aad5SDmitry Torokhov static int __devinit wistron_probe(struct platform_device *dev) 538e7c3aad5SDmitry Torokhov { 539e7c3aad5SDmitry Torokhov int err = setup_input_dev(); 540e7c3aad5SDmitry Torokhov if (err) 541e7c3aad5SDmitry Torokhov return err; 542e7c3aad5SDmitry Torokhov 543e7c3aad5SDmitry Torokhov bios_attach(); 544e7c3aad5SDmitry Torokhov cmos_address = bios_get_cmos_address(); 545e7c3aad5SDmitry Torokhov 546e7c3aad5SDmitry Torokhov if (have_wifi) { 547e7c3aad5SDmitry Torokhov u16 wifi = bios_get_default_setting(WIFI); 548e7c3aad5SDmitry Torokhov if (wifi & 1) 549e7c3aad5SDmitry Torokhov wifi_enabled = (wifi & 2) ? 1 : 0; 550e7c3aad5SDmitry Torokhov else 551e7c3aad5SDmitry Torokhov have_wifi = 0; 552e7c3aad5SDmitry Torokhov 553e7c3aad5SDmitry Torokhov if (have_wifi) 554e7c3aad5SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 555e7c3aad5SDmitry Torokhov } 556e7c3aad5SDmitry Torokhov 557e7c3aad5SDmitry Torokhov if (have_bluetooth) { 558e7c3aad5SDmitry Torokhov u16 bt = bios_get_default_setting(BLUETOOTH); 559e7c3aad5SDmitry Torokhov if (bt & 1) 560e7c3aad5SDmitry Torokhov bluetooth_enabled = (bt & 2) ? 1 : 0; 561e7c3aad5SDmitry Torokhov else 562e7c3aad5SDmitry Torokhov have_bluetooth = 0; 563e7c3aad5SDmitry Torokhov 564e7c3aad5SDmitry Torokhov if (have_bluetooth) 565e7c3aad5SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 566e7c3aad5SDmitry Torokhov } 567e7c3aad5SDmitry Torokhov 568e7c3aad5SDmitry Torokhov poll_bios(1); /* Flush stale event queue and arm timer */ 569e7c3aad5SDmitry Torokhov 570e7c3aad5SDmitry Torokhov return 0; 571e7c3aad5SDmitry Torokhov } 572e7c3aad5SDmitry Torokhov 573e7c3aad5SDmitry Torokhov static int __devexit wistron_remove(struct platform_device *dev) 574e7c3aad5SDmitry Torokhov { 575e7c3aad5SDmitry Torokhov del_timer_sync(&poll_timer); 576e7c3aad5SDmitry Torokhov input_unregister_device(input_dev); 577e7c3aad5SDmitry Torokhov bios_detach(); 578e7c3aad5SDmitry Torokhov 579e7c3aad5SDmitry Torokhov return 0; 580e7c3aad5SDmitry Torokhov } 581e7c3aad5SDmitry Torokhov 582e7c3aad5SDmitry Torokhov #ifdef CONFIG_PM 583a5b0cc80SDmitry Torokhov static int wistron_suspend(struct platform_device *dev, pm_message_t state) 584a5b0cc80SDmitry Torokhov { 585a5b0cc80SDmitry Torokhov del_timer_sync(&poll_timer); 586a5b0cc80SDmitry Torokhov 587e753b650SMiloslav Trmac if (have_wifi) 588e753b650SMiloslav Trmac bios_set_state(WIFI, 0); 589e753b650SMiloslav Trmac 590e753b650SMiloslav Trmac if (have_bluetooth) 591e753b650SMiloslav Trmac bios_set_state(BLUETOOTH, 0); 592e753b650SMiloslav Trmac 593a5b0cc80SDmitry Torokhov return 0; 594a5b0cc80SDmitry Torokhov } 595a5b0cc80SDmitry Torokhov 596a5b0cc80SDmitry Torokhov static int wistron_resume(struct platform_device *dev) 597a5b0cc80SDmitry Torokhov { 598a5b0cc80SDmitry Torokhov if (have_wifi) 599a5b0cc80SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 600a5b0cc80SDmitry Torokhov 601a5b0cc80SDmitry Torokhov if (have_bluetooth) 602a5b0cc80SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 603a5b0cc80SDmitry Torokhov 604a5b0cc80SDmitry Torokhov poll_bios(1); 605a5b0cc80SDmitry Torokhov 606a5b0cc80SDmitry Torokhov return 0; 607a5b0cc80SDmitry Torokhov } 608e7c3aad5SDmitry Torokhov #else 609e7c3aad5SDmitry Torokhov #define wistron_suspend NULL 610e7c3aad5SDmitry Torokhov #define wistron_resume NULL 611e7c3aad5SDmitry Torokhov #endif 612a5b0cc80SDmitry Torokhov 613a5b0cc80SDmitry Torokhov static struct platform_driver wistron_driver = { 614a5b0cc80SDmitry Torokhov .driver = { 615a5b0cc80SDmitry Torokhov .name = "wistron-bios", 616e7c3aad5SDmitry Torokhov .owner = THIS_MODULE, 617a5b0cc80SDmitry Torokhov }, 618e7c3aad5SDmitry Torokhov .probe = wistron_probe, 619e7c3aad5SDmitry Torokhov .remove = __devexit_p(wistron_remove), 620e7c3aad5SDmitry Torokhov .suspend = wistron_suspend, 621e7c3aad5SDmitry Torokhov .resume = wistron_resume, 622a5b0cc80SDmitry Torokhov }; 623a5b0cc80SDmitry Torokhov 6245fc14680SDmitry Torokhov static int __init wb_module_init(void) 6255fc14680SDmitry Torokhov { 6265fc14680SDmitry Torokhov int err; 6275fc14680SDmitry Torokhov 6285fc14680SDmitry Torokhov err = select_keymap(); 6295fc14680SDmitry Torokhov if (err) 6305fc14680SDmitry Torokhov return err; 63122a397e2SDmitry Torokhov 6325fc14680SDmitry Torokhov err = map_bios(); 6335fc14680SDmitry Torokhov if (err) 6345fc14680SDmitry Torokhov return err; 63522a397e2SDmitry Torokhov 636a5b0cc80SDmitry Torokhov err = platform_driver_register(&wistron_driver); 637a5b0cc80SDmitry Torokhov if (err) 638e7c3aad5SDmitry Torokhov goto err_unmap_bios; 639a5b0cc80SDmitry Torokhov 640e7c3aad5SDmitry Torokhov wistron_device = platform_device_alloc("wistron-bios", -1); 641e7c3aad5SDmitry Torokhov if (!wistron_device) { 642e7c3aad5SDmitry Torokhov err = -ENOMEM; 643a5b0cc80SDmitry Torokhov goto err_unregister_driver; 644a5b0cc80SDmitry Torokhov } 645a5b0cc80SDmitry Torokhov 646e7c3aad5SDmitry Torokhov err = platform_device_add(wistron_device); 647a5b0cc80SDmitry Torokhov if (err) 648e7c3aad5SDmitry Torokhov goto err_free_device; 6495fc14680SDmitry Torokhov 6505fc14680SDmitry Torokhov return 0; 651a5b0cc80SDmitry Torokhov 652e7c3aad5SDmitry Torokhov err_free_device: 653e7c3aad5SDmitry Torokhov platform_device_put(wistron_device); 654a5b0cc80SDmitry Torokhov err_unregister_driver: 655a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 656e7c3aad5SDmitry Torokhov err_unmap_bios: 657a5b0cc80SDmitry Torokhov unmap_bios(); 658a5b0cc80SDmitry Torokhov 659a5b0cc80SDmitry Torokhov return err; 6605fc14680SDmitry Torokhov } 6615fc14680SDmitry Torokhov 6625fc14680SDmitry Torokhov static void __exit wb_module_exit(void) 6635fc14680SDmitry Torokhov { 664a5b0cc80SDmitry Torokhov platform_device_unregister(wistron_device); 665a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 6665fc14680SDmitry Torokhov unmap_bios(); 6675fc14680SDmitry Torokhov } 6685fc14680SDmitry Torokhov 6695fc14680SDmitry Torokhov module_init(wb_module_init); 6705fc14680SDmitry Torokhov module_exit(wb_module_exit); 671