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 */ 2053d5ed62SMatthew Wilcox #include <linux/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++) { 251cde45f19SReiner Herrmann if (key->type == KE_WIFI) 2525fc14680SDmitry Torokhov have_wifi = 1; 253cde45f19SReiner Herrmann else if (key->type == KE_BLUETOOTH) 25484b256a6SBernhard Rosenkraenzer have_bluetooth = 1; 2555fc14680SDmitry Torokhov } 2565fc14680SDmitry Torokhov return 1; 2575fc14680SDmitry Torokhov } 2585fc14680SDmitry Torokhov 25972a623beSDmitry Torokhov static struct key_entry keymap_empty[] = { 2605fc14680SDmitry Torokhov { KE_END, 0 } 2615fc14680SDmitry Torokhov }; 2625fc14680SDmitry Torokhov 26372a623beSDmitry Torokhov static struct key_entry keymap_fs_amilo_pro_v2000[] = { 2645fc14680SDmitry Torokhov { KE_KEY, 0x01, KEY_HELP }, 2655fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2665fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 2675fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 2685fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 2695fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 2705fc14680SDmitry Torokhov { KE_END, 0 } 2715fc14680SDmitry Torokhov }; 2725fc14680SDmitry Torokhov 27372a623beSDmitry Torokhov static struct key_entry keymap_fujitsu_n3510[] = { 274e2aa507aSJohn Reed Riley { KE_KEY, 0x11, KEY_PROG1 }, 275e2aa507aSJohn Reed Riley { KE_KEY, 0x12, KEY_PROG2 }, 276e2aa507aSJohn Reed Riley { KE_KEY, 0x36, KEY_WWW }, 277e2aa507aSJohn Reed Riley { KE_KEY, 0x31, KEY_MAIL }, 278e2aa507aSJohn Reed Riley { KE_KEY, 0x71, KEY_STOPCD }, 279e2aa507aSJohn Reed Riley { KE_KEY, 0x72, KEY_PLAYPAUSE }, 280e2aa507aSJohn Reed Riley { KE_KEY, 0x74, KEY_REWIND }, 281e2aa507aSJohn Reed Riley { KE_KEY, 0x78, KEY_FORWARD }, 282e2aa507aSJohn Reed Riley { KE_END, 0 } 283e2aa507aSJohn Reed Riley }; 284e2aa507aSJohn Reed Riley 28572a623beSDmitry Torokhov static struct key_entry keymap_wistron_ms2111[] = { 2869000195bSFrank de Lange { KE_KEY, 0x11, KEY_PROG1 }, 2879000195bSFrank de Lange { KE_KEY, 0x12, KEY_PROG2 }, 2889000195bSFrank de Lange { KE_KEY, 0x13, KEY_PROG3 }, 2899000195bSFrank de Lange { KE_KEY, 0x31, KEY_MAIL }, 2909000195bSFrank de Lange { KE_KEY, 0x36, KEY_WWW }, 2919000195bSFrank de Lange { KE_END, 0 } 2929000195bSFrank de Lange }; 2939000195bSFrank de Lange 29472a623beSDmitry Torokhov static struct key_entry keymap_wistron_ms2141[] = { 2955fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2965fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 2975fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 2985fc14680SDmitry Torokhov { KE_KEY, 0x22, KEY_REWIND }, 2995fc14680SDmitry Torokhov { KE_KEY, 0x23, KEY_FORWARD }, 3005fc14680SDmitry Torokhov { KE_KEY, 0x24, KEY_PLAYPAUSE }, 3015fc14680SDmitry Torokhov { KE_KEY, 0x25, KEY_STOPCD }, 3025fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 3035fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 3045fc14680SDmitry Torokhov { KE_END, 0 } 3055fc14680SDmitry Torokhov }; 3065fc14680SDmitry Torokhov 30772a623beSDmitry Torokhov static struct key_entry keymap_acer_aspire_1500[] = { 30884b256a6SBernhard Rosenkraenzer { KE_KEY, 0x11, KEY_PROG1 }, 30984b256a6SBernhard Rosenkraenzer { KE_KEY, 0x12, KEY_PROG2 }, 31084b256a6SBernhard Rosenkraenzer { KE_WIFI, 0x30, 0 }, 31184b256a6SBernhard Rosenkraenzer { KE_KEY, 0x31, KEY_MAIL }, 31284b256a6SBernhard Rosenkraenzer { KE_KEY, 0x36, KEY_WWW }, 31384b256a6SBernhard Rosenkraenzer { KE_BLUETOOTH, 0x44, 0 }, 31484b256a6SBernhard Rosenkraenzer { KE_END, 0 } 31584b256a6SBernhard Rosenkraenzer }; 31684b256a6SBernhard Rosenkraenzer 31772a623beSDmitry Torokhov static struct key_entry keymap_acer_travelmate_240[] = { 31874a89c96SAshutosh Naik { KE_KEY, 0x31, KEY_MAIL }, 31974a89c96SAshutosh Naik { KE_KEY, 0x36, KEY_WWW }, 32074a89c96SAshutosh Naik { KE_KEY, 0x11, KEY_PROG1 }, 32174a89c96SAshutosh Naik { KE_KEY, 0x12, KEY_PROG2 }, 32274a89c96SAshutosh Naik { KE_BLUETOOTH, 0x44, 0 }, 32374a89c96SAshutosh Naik { KE_WIFI, 0x30, 0 }, 32474a89c96SAshutosh Naik { KE_END, 0 } 32574a89c96SAshutosh Naik }; 32674a89c96SAshutosh Naik 327bc413c95SEric Piel /* Wifi subsystem only activates the led. Therefore we need to pass 328bc413c95SEric Piel * wifi event as a normal key, then userspace can really change the wifi state. 329bc413c95SEric Piel * TODO we need to export led state to userspace (wifi and mail) */ 330bc413c95SEric Piel static struct key_entry keymap_acer_travelmate_610[] = { 331bc413c95SEric Piel { KE_KEY, 0x01, KEY_HELP }, 332bc413c95SEric Piel { KE_KEY, 0x02, KEY_CONFIG }, 333bc413c95SEric Piel { KE_KEY, 0x11, KEY_PROG1 }, 334bc413c95SEric Piel { KE_KEY, 0x12, KEY_PROG2 }, 335bc413c95SEric Piel { KE_KEY, 0x13, KEY_PROG3 }, 336bc413c95SEric Piel { KE_KEY, 0x14, KEY_MAIL }, 337bc413c95SEric Piel { KE_KEY, 0x15, KEY_WWW }, 338bc413c95SEric Piel { KE_KEY, 0x40, KEY_WLAN }, /* Wifi */ 339bc413c95SEric Piel { KE_END, 0 } 340bc413c95SEric Piel }; 341bc413c95SEric Piel 34272a623beSDmitry Torokhov static struct key_entry keymap_aopen_1559as[] = { 343e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x01, KEY_HELP }, 344e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x06, KEY_PROG3 }, 345e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x11, KEY_PROG1 }, 346e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x12, KEY_PROG2 }, 347e107b8eeSmasc@theaterzentrum.at { KE_WIFI, 0x30, 0 }, 348e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x31, KEY_MAIL }, 349e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x36, KEY_WWW }, 3509000195bSFrank de Lange { KE_END, 0 }, 351e107b8eeSmasc@theaterzentrum.at }; 352e107b8eeSmasc@theaterzentrum.at 3535809d537SMichael Leun static struct key_entry keymap_fs_amilo_d88x0[] = { 3545809d537SMichael Leun { KE_KEY, 0x01, KEY_HELP }, 3555809d537SMichael Leun { KE_KEY, 0x08, KEY_MUTE }, 3565809d537SMichael Leun { KE_KEY, 0x31, KEY_MAIL }, 3575809d537SMichael Leun { KE_KEY, 0x36, KEY_WWW }, 3585809d537SMichael Leun { KE_KEY, 0x11, KEY_PROG1 }, 3595809d537SMichael Leun { KE_KEY, 0x12, KEY_PROG2 }, 3605809d537SMichael Leun { KE_KEY, 0x13, KEY_PROG3 }, 3615809d537SMichael Leun { KE_END, 0 } 3625809d537SMichael Leun }; 3635809d537SMichael Leun 3645fc14680SDmitry Torokhov /* 3655fc14680SDmitry Torokhov * If your machine is not here (which is currently rather likely), please send 3665fc14680SDmitry Torokhov * a list of buttons and their key codes (reported when loading this module 3675fc14680SDmitry Torokhov * with force=1) and the output of dmidecode to $MODULE_AUTHOR. 3685fc14680SDmitry Torokhov */ 369c7948989SAndrew Morton static struct dmi_system_id dmi_ids[] __initdata = { 3705fc14680SDmitry Torokhov { 3715fc14680SDmitry Torokhov .callback = dmi_matched, 3725fc14680SDmitry Torokhov .ident = "Fujitsu-Siemens Amilo Pro V2000", 3735fc14680SDmitry Torokhov .matches = { 3745fc14680SDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3755fc14680SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), 3765fc14680SDmitry Torokhov }, 3775fc14680SDmitry Torokhov .driver_data = keymap_fs_amilo_pro_v2000 3785fc14680SDmitry Torokhov }, 37984b256a6SBernhard Rosenkraenzer { 38084b256a6SBernhard Rosenkraenzer .callback = dmi_matched, 3818a1b1708SStefan Rompf .ident = "Fujitsu-Siemens Amilo M7400", 3828a1b1708SStefan Rompf .matches = { 3838a1b1708SStefan Rompf DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3848a1b1708SStefan Rompf DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), 3858a1b1708SStefan Rompf }, 3868a1b1708SStefan Rompf .driver_data = keymap_fs_amilo_pro_v2000 3878a1b1708SStefan Rompf }, 3888a1b1708SStefan Rompf { 3898a1b1708SStefan Rompf .callback = dmi_matched, 390e2aa507aSJohn Reed Riley .ident = "Fujitsu N3510", 391e2aa507aSJohn Reed Riley .matches = { 392e2aa507aSJohn Reed Riley DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 393e2aa507aSJohn Reed Riley DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), 394e2aa507aSJohn Reed Riley }, 395e2aa507aSJohn Reed Riley .driver_data = keymap_fujitsu_n3510 396e2aa507aSJohn Reed Riley }, 397e2aa507aSJohn Reed Riley { 398e2aa507aSJohn Reed Riley .callback = dmi_matched, 39984b256a6SBernhard Rosenkraenzer .ident = "Acer Aspire 1500", 40084b256a6SBernhard Rosenkraenzer .matches = { 40184b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 40284b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), 40384b256a6SBernhard Rosenkraenzer }, 40484b256a6SBernhard Rosenkraenzer .driver_data = keymap_acer_aspire_1500 40584b256a6SBernhard Rosenkraenzer }, 40674a89c96SAshutosh Naik { 40774a89c96SAshutosh Naik .callback = dmi_matched, 40874a89c96SAshutosh Naik .ident = "Acer TravelMate 240", 40974a89c96SAshutosh Naik .matches = { 41074a89c96SAshutosh Naik DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 41174a89c96SAshutosh Naik DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), 41274a89c96SAshutosh Naik }, 41374a89c96SAshutosh Naik .driver_data = keymap_acer_travelmate_240 41474a89c96SAshutosh Naik }, 415e107b8eeSmasc@theaterzentrum.at { 416bb088590SAshutosh Naik .callback = dmi_matched, 417bb088590SAshutosh Naik .ident = "Acer TravelMate 2424NWXCi", 418bb088590SAshutosh Naik .matches = { 419bb088590SAshutosh Naik DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 420bb088590SAshutosh Naik DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"), 421bb088590SAshutosh Naik }, 422bb088590SAshutosh Naik .driver_data = keymap_acer_travelmate_240 423bb088590SAshutosh Naik }, 424bb088590SAshutosh Naik { 425e107b8eeSmasc@theaterzentrum.at .callback = dmi_matched, 426bc413c95SEric Piel .ident = "Acer TravelMate 610", 427bc413c95SEric Piel .matches = { 428bc413c95SEric Piel DMI_MATCH(DMI_SYS_VENDOR, "ACER"), 429bc413c95SEric Piel DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"), 430bc413c95SEric Piel }, 431bc413c95SEric Piel .driver_data = keymap_acer_travelmate_610 432bc413c95SEric Piel }, 433bc413c95SEric Piel { 434bc413c95SEric Piel .callback = dmi_matched, 435e107b8eeSmasc@theaterzentrum.at .ident = "AOpen 1559AS", 436e107b8eeSmasc@theaterzentrum.at .matches = { 437e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), 438e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_BOARD_NAME, "E2U"), 439e107b8eeSmasc@theaterzentrum.at }, 440e107b8eeSmasc@theaterzentrum.at .driver_data = keymap_aopen_1559as 441e107b8eeSmasc@theaterzentrum.at }, 4429000195bSFrank de Lange { 4439000195bSFrank de Lange .callback = dmi_matched, 4449000195bSFrank de Lange .ident = "Medion MD 9783", 4459000195bSFrank de Lange .matches = { 4469000195bSFrank de Lange DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"), 4479000195bSFrank de Lange DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"), 4489000195bSFrank de Lange }, 4499000195bSFrank de Lange .driver_data = keymap_wistron_ms2111 4509000195bSFrank de Lange }, 4515809d537SMichael Leun { 4525809d537SMichael Leun .callback = dmi_matched, 4535809d537SMichael Leun .ident = "Fujitsu Siemens Amilo D88x0", 4545809d537SMichael Leun .matches = { 4555809d537SMichael Leun DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 4565809d537SMichael Leun DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"), 4575809d537SMichael Leun }, 4585809d537SMichael Leun .driver_data = keymap_fs_amilo_d88x0 4595809d537SMichael Leun }, 46081f0a91eSAl Viro { NULL, } 4615fc14680SDmitry Torokhov }; 4625fc14680SDmitry Torokhov 4635fc14680SDmitry Torokhov static int __init select_keymap(void) 4645fc14680SDmitry Torokhov { 4655fc14680SDmitry Torokhov if (keymap_name != NULL) { 4665fc14680SDmitry Torokhov if (strcmp (keymap_name, "1557/MS2141") == 0) 4675fc14680SDmitry Torokhov keymap = keymap_wistron_ms2141; 4685fc14680SDmitry Torokhov else { 4695fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: Keymap unknown\n"); 4705fc14680SDmitry Torokhov return -EINVAL; 4715fc14680SDmitry Torokhov } 4725fc14680SDmitry Torokhov } 4735fc14680SDmitry Torokhov dmi_check_system(dmi_ids); 4745fc14680SDmitry Torokhov if (keymap == NULL) { 4755fc14680SDmitry Torokhov if (!force) { 4765fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: System unknown\n"); 4775fc14680SDmitry Torokhov return -ENODEV; 4785fc14680SDmitry Torokhov } 4795fc14680SDmitry Torokhov keymap = keymap_empty; 4805fc14680SDmitry Torokhov } 4815fc14680SDmitry Torokhov return 0; 4825fc14680SDmitry Torokhov } 4835fc14680SDmitry Torokhov 4845fc14680SDmitry Torokhov /* Input layer interface */ 4855fc14680SDmitry Torokhov 48622a397e2SDmitry Torokhov static struct input_dev *input_dev; 4875fc14680SDmitry Torokhov 488e7c3aad5SDmitry Torokhov static int __devinit setup_input_dev(void) 4895fc14680SDmitry Torokhov { 4905fc14680SDmitry Torokhov const struct key_entry *key; 49122a397e2SDmitry Torokhov int error; 49222a397e2SDmitry Torokhov 49322a397e2SDmitry Torokhov input_dev = input_allocate_device(); 49422a397e2SDmitry Torokhov if (!input_dev) 49522a397e2SDmitry Torokhov return -ENOMEM; 49622a397e2SDmitry Torokhov 49722a397e2SDmitry Torokhov input_dev->name = "Wistron laptop buttons"; 49822a397e2SDmitry Torokhov input_dev->phys = "wistron/input0"; 49922a397e2SDmitry Torokhov input_dev->id.bustype = BUS_HOST; 500a5b0cc80SDmitry Torokhov input_dev->cdev.dev = &wistron_device->dev; 5015fc14680SDmitry Torokhov 5025fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 5035fc14680SDmitry Torokhov if (key->type == KE_KEY) { 50422a397e2SDmitry Torokhov input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); 50522a397e2SDmitry Torokhov set_bit(key->keycode, input_dev->keybit); 5065fc14680SDmitry Torokhov } 5075fc14680SDmitry Torokhov } 5085fc14680SDmitry Torokhov 50922a397e2SDmitry Torokhov error = input_register_device(input_dev); 51022a397e2SDmitry Torokhov if (error) { 51122a397e2SDmitry Torokhov input_free_device(input_dev); 51222a397e2SDmitry Torokhov return error; 51322a397e2SDmitry Torokhov } 51422a397e2SDmitry Torokhov 51522a397e2SDmitry Torokhov return 0; 5165fc14680SDmitry Torokhov } 5175fc14680SDmitry Torokhov 5185fc14680SDmitry Torokhov static void report_key(unsigned keycode) 5195fc14680SDmitry Torokhov { 52022a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 1); 52122a397e2SDmitry Torokhov input_sync(input_dev); 52222a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 0); 52322a397e2SDmitry Torokhov input_sync(input_dev); 5245fc14680SDmitry Torokhov } 5255fc14680SDmitry Torokhov 5265fc14680SDmitry Torokhov /* Driver core */ 5275fc14680SDmitry Torokhov 5285fc14680SDmitry Torokhov static int wifi_enabled; 52984b256a6SBernhard Rosenkraenzer static int bluetooth_enabled; 5305fc14680SDmitry Torokhov 5315fc14680SDmitry Torokhov static void poll_bios(unsigned long); 5325fc14680SDmitry Torokhov 5335fc14680SDmitry Torokhov static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); 5345fc14680SDmitry Torokhov 5355fc14680SDmitry Torokhov static void handle_key(u8 code) 5365fc14680SDmitry Torokhov { 5375fc14680SDmitry Torokhov const struct key_entry *key; 5385fc14680SDmitry Torokhov 5395fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 5405fc14680SDmitry Torokhov if (code == key->code) { 5415fc14680SDmitry Torokhov switch (key->type) { 5425fc14680SDmitry Torokhov case KE_KEY: 5435fc14680SDmitry Torokhov report_key(key->keycode); 5445fc14680SDmitry Torokhov break; 5455fc14680SDmitry Torokhov 5465fc14680SDmitry Torokhov case KE_WIFI: 5475fc14680SDmitry Torokhov if (have_wifi) { 5485fc14680SDmitry Torokhov wifi_enabled = !wifi_enabled; 54984b256a6SBernhard Rosenkraenzer bios_set_state(WIFI, wifi_enabled); 55084b256a6SBernhard Rosenkraenzer } 55184b256a6SBernhard Rosenkraenzer break; 55284b256a6SBernhard Rosenkraenzer 55384b256a6SBernhard Rosenkraenzer case KE_BLUETOOTH: 55484b256a6SBernhard Rosenkraenzer if (have_bluetooth) { 55584b256a6SBernhard Rosenkraenzer bluetooth_enabled = !bluetooth_enabled; 55684b256a6SBernhard Rosenkraenzer bios_set_state(BLUETOOTH, bluetooth_enabled); 5575fc14680SDmitry Torokhov } 5585fc14680SDmitry Torokhov break; 5595fc14680SDmitry Torokhov 5605fc14680SDmitry Torokhov case KE_END: 5615fc14680SDmitry Torokhov default: 5625fc14680SDmitry Torokhov BUG(); 5635fc14680SDmitry Torokhov } 5645fc14680SDmitry Torokhov return; 5655fc14680SDmitry Torokhov } 5665fc14680SDmitry Torokhov } 5675fc14680SDmitry Torokhov printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); 5685fc14680SDmitry Torokhov } 5695fc14680SDmitry Torokhov 5705fc14680SDmitry Torokhov static void poll_bios(unsigned long discard) 5715fc14680SDmitry Torokhov { 5725fc14680SDmitry Torokhov u8 qlen; 5735fc14680SDmitry Torokhov u16 val; 5745fc14680SDmitry Torokhov 5755fc14680SDmitry Torokhov for (;;) { 5765fc14680SDmitry Torokhov qlen = CMOS_READ(cmos_address); 5775fc14680SDmitry Torokhov if (qlen == 0) 5785fc14680SDmitry Torokhov break; 5795fc14680SDmitry Torokhov val = bios_pop_queue(); 5805fc14680SDmitry Torokhov if (val != 0 && !discard) 5815fc14680SDmitry Torokhov handle_key((u8)val); 5825fc14680SDmitry Torokhov } 5835fc14680SDmitry Torokhov 5845fc14680SDmitry Torokhov mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); 5855fc14680SDmitry Torokhov } 5865fc14680SDmitry Torokhov 587e7c3aad5SDmitry Torokhov static int __devinit wistron_probe(struct platform_device *dev) 588e7c3aad5SDmitry Torokhov { 589e7c3aad5SDmitry Torokhov int err = setup_input_dev(); 590e7c3aad5SDmitry Torokhov if (err) 591e7c3aad5SDmitry Torokhov return err; 592e7c3aad5SDmitry Torokhov 593e7c3aad5SDmitry Torokhov bios_attach(); 594e7c3aad5SDmitry Torokhov cmos_address = bios_get_cmos_address(); 595e7c3aad5SDmitry Torokhov 596e7c3aad5SDmitry Torokhov if (have_wifi) { 597e7c3aad5SDmitry Torokhov u16 wifi = bios_get_default_setting(WIFI); 598e7c3aad5SDmitry Torokhov if (wifi & 1) 599e7c3aad5SDmitry Torokhov wifi_enabled = (wifi & 2) ? 1 : 0; 600e7c3aad5SDmitry Torokhov else 601e7c3aad5SDmitry Torokhov have_wifi = 0; 602e7c3aad5SDmitry Torokhov 603e7c3aad5SDmitry Torokhov if (have_wifi) 604e7c3aad5SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 605e7c3aad5SDmitry Torokhov } 606e7c3aad5SDmitry Torokhov 607e7c3aad5SDmitry Torokhov if (have_bluetooth) { 608e7c3aad5SDmitry Torokhov u16 bt = bios_get_default_setting(BLUETOOTH); 609e7c3aad5SDmitry Torokhov if (bt & 1) 610e7c3aad5SDmitry Torokhov bluetooth_enabled = (bt & 2) ? 1 : 0; 611e7c3aad5SDmitry Torokhov else 612e7c3aad5SDmitry Torokhov have_bluetooth = 0; 613e7c3aad5SDmitry Torokhov 614e7c3aad5SDmitry Torokhov if (have_bluetooth) 615e7c3aad5SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 616e7c3aad5SDmitry Torokhov } 617e7c3aad5SDmitry Torokhov 618e7c3aad5SDmitry Torokhov poll_bios(1); /* Flush stale event queue and arm timer */ 619e7c3aad5SDmitry Torokhov 620e7c3aad5SDmitry Torokhov return 0; 621e7c3aad5SDmitry Torokhov } 622e7c3aad5SDmitry Torokhov 623e7c3aad5SDmitry Torokhov static int __devexit wistron_remove(struct platform_device *dev) 624e7c3aad5SDmitry Torokhov { 625e7c3aad5SDmitry Torokhov del_timer_sync(&poll_timer); 626e7c3aad5SDmitry Torokhov input_unregister_device(input_dev); 627e7c3aad5SDmitry Torokhov bios_detach(); 628e7c3aad5SDmitry Torokhov 629e7c3aad5SDmitry Torokhov return 0; 630e7c3aad5SDmitry Torokhov } 631e7c3aad5SDmitry Torokhov 632e7c3aad5SDmitry Torokhov #ifdef CONFIG_PM 633a5b0cc80SDmitry Torokhov static int wistron_suspend(struct platform_device *dev, pm_message_t state) 634a5b0cc80SDmitry Torokhov { 635a5b0cc80SDmitry Torokhov del_timer_sync(&poll_timer); 636a5b0cc80SDmitry Torokhov 637e753b650SMiloslav Trmac if (have_wifi) 638e753b650SMiloslav Trmac bios_set_state(WIFI, 0); 639e753b650SMiloslav Trmac 640e753b650SMiloslav Trmac if (have_bluetooth) 641e753b650SMiloslav Trmac bios_set_state(BLUETOOTH, 0); 642e753b650SMiloslav Trmac 643a5b0cc80SDmitry Torokhov return 0; 644a5b0cc80SDmitry Torokhov } 645a5b0cc80SDmitry Torokhov 646a5b0cc80SDmitry Torokhov static int wistron_resume(struct platform_device *dev) 647a5b0cc80SDmitry Torokhov { 648a5b0cc80SDmitry Torokhov if (have_wifi) 649a5b0cc80SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 650a5b0cc80SDmitry Torokhov 651a5b0cc80SDmitry Torokhov if (have_bluetooth) 652a5b0cc80SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 653a5b0cc80SDmitry Torokhov 654a5b0cc80SDmitry Torokhov poll_bios(1); 655a5b0cc80SDmitry Torokhov 656a5b0cc80SDmitry Torokhov return 0; 657a5b0cc80SDmitry Torokhov } 658e7c3aad5SDmitry Torokhov #else 659e7c3aad5SDmitry Torokhov #define wistron_suspend NULL 660e7c3aad5SDmitry Torokhov #define wistron_resume NULL 661e7c3aad5SDmitry Torokhov #endif 662a5b0cc80SDmitry Torokhov 663a5b0cc80SDmitry Torokhov static struct platform_driver wistron_driver = { 664a5b0cc80SDmitry Torokhov .driver = { 665a5b0cc80SDmitry Torokhov .name = "wistron-bios", 666e7c3aad5SDmitry Torokhov .owner = THIS_MODULE, 667a5b0cc80SDmitry Torokhov }, 668e7c3aad5SDmitry Torokhov .probe = wistron_probe, 669e7c3aad5SDmitry Torokhov .remove = __devexit_p(wistron_remove), 670e7c3aad5SDmitry Torokhov .suspend = wistron_suspend, 671e7c3aad5SDmitry Torokhov .resume = wistron_resume, 672a5b0cc80SDmitry Torokhov }; 673a5b0cc80SDmitry Torokhov 6745fc14680SDmitry Torokhov static int __init wb_module_init(void) 6755fc14680SDmitry Torokhov { 6765fc14680SDmitry Torokhov int err; 6775fc14680SDmitry Torokhov 6785fc14680SDmitry Torokhov err = select_keymap(); 6795fc14680SDmitry Torokhov if (err) 6805fc14680SDmitry Torokhov return err; 68122a397e2SDmitry Torokhov 6825fc14680SDmitry Torokhov err = map_bios(); 6835fc14680SDmitry Torokhov if (err) 6845fc14680SDmitry Torokhov return err; 68522a397e2SDmitry Torokhov 686a5b0cc80SDmitry Torokhov err = platform_driver_register(&wistron_driver); 687a5b0cc80SDmitry Torokhov if (err) 688e7c3aad5SDmitry Torokhov goto err_unmap_bios; 689a5b0cc80SDmitry Torokhov 690e7c3aad5SDmitry Torokhov wistron_device = platform_device_alloc("wistron-bios", -1); 691e7c3aad5SDmitry Torokhov if (!wistron_device) { 692e7c3aad5SDmitry Torokhov err = -ENOMEM; 693a5b0cc80SDmitry Torokhov goto err_unregister_driver; 694a5b0cc80SDmitry Torokhov } 695a5b0cc80SDmitry Torokhov 696e7c3aad5SDmitry Torokhov err = platform_device_add(wistron_device); 697a5b0cc80SDmitry Torokhov if (err) 698e7c3aad5SDmitry Torokhov goto err_free_device; 6995fc14680SDmitry Torokhov 7005fc14680SDmitry Torokhov return 0; 701a5b0cc80SDmitry Torokhov 702e7c3aad5SDmitry Torokhov err_free_device: 703e7c3aad5SDmitry Torokhov platform_device_put(wistron_device); 704a5b0cc80SDmitry Torokhov err_unregister_driver: 705a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 706e7c3aad5SDmitry Torokhov err_unmap_bios: 707a5b0cc80SDmitry Torokhov unmap_bios(); 708a5b0cc80SDmitry Torokhov 709a5b0cc80SDmitry Torokhov return err; 7105fc14680SDmitry Torokhov } 7115fc14680SDmitry Torokhov 7125fc14680SDmitry Torokhov static void __exit wb_module_exit(void) 7135fc14680SDmitry Torokhov { 714a5b0cc80SDmitry Torokhov platform_device_unregister(wistron_device); 715a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 7165fc14680SDmitry Torokhov unmap_bios(); 7175fc14680SDmitry Torokhov } 7185fc14680SDmitry Torokhov 7195fc14680SDmitry Torokhov module_init(wb_module_init); 7205fc14680SDmitry Torokhov module_exit(wb_module_exit); 721