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 { 975fc14680SDmitry Torokhov static const 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 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 276e2aa507aSJohn Reed Riley static struct key_entry keymap_fujitsu_n3510[] = { 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 2885fc14680SDmitry Torokhov static struct key_entry keymap_wistron_ms2141[] = { 2895fc14680SDmitry Torokhov { KE_KEY, 0x11, KEY_PROG1 }, 2905fc14680SDmitry Torokhov { KE_KEY, 0x12, KEY_PROG2 }, 2915fc14680SDmitry Torokhov { KE_WIFI, 0x30, 0 }, 2925fc14680SDmitry Torokhov { KE_KEY, 0x22, KEY_REWIND }, 2935fc14680SDmitry Torokhov { KE_KEY, 0x23, KEY_FORWARD }, 2945fc14680SDmitry Torokhov { KE_KEY, 0x24, KEY_PLAYPAUSE }, 2955fc14680SDmitry Torokhov { KE_KEY, 0x25, KEY_STOPCD }, 2965fc14680SDmitry Torokhov { KE_KEY, 0x31, KEY_MAIL }, 2975fc14680SDmitry Torokhov { KE_KEY, 0x36, KEY_WWW }, 2985fc14680SDmitry Torokhov { KE_END, 0 } 2995fc14680SDmitry Torokhov }; 3005fc14680SDmitry Torokhov 30184b256a6SBernhard Rosenkraenzer static struct key_entry keymap_acer_aspire_1500[] = { 30284b256a6SBernhard Rosenkraenzer { KE_KEY, 0x11, KEY_PROG1 }, 30384b256a6SBernhard Rosenkraenzer { KE_KEY, 0x12, KEY_PROG2 }, 30484b256a6SBernhard Rosenkraenzer { KE_WIFI, 0x30, 0 }, 30584b256a6SBernhard Rosenkraenzer { KE_KEY, 0x31, KEY_MAIL }, 30684b256a6SBernhard Rosenkraenzer { KE_KEY, 0x36, KEY_WWW }, 30784b256a6SBernhard Rosenkraenzer { KE_BLUETOOTH, 0x44, 0 }, 30884b256a6SBernhard Rosenkraenzer { KE_END, 0 } 30984b256a6SBernhard Rosenkraenzer }; 31084b256a6SBernhard Rosenkraenzer 31174a89c96SAshutosh Naik static struct key_entry keymap_acer_travelmate_240[] = { 31274a89c96SAshutosh Naik { KE_KEY, 0x31, KEY_MAIL }, 31374a89c96SAshutosh Naik { KE_KEY, 0x36, KEY_WWW }, 31474a89c96SAshutosh Naik { KE_KEY, 0x11, KEY_PROG1 }, 31574a89c96SAshutosh Naik { KE_KEY, 0x12, KEY_PROG2 }, 31674a89c96SAshutosh Naik { KE_BLUETOOTH, 0x44, 0 }, 31774a89c96SAshutosh Naik { KE_WIFI, 0x30, 0 }, 31874a89c96SAshutosh Naik { KE_END, 0 } 31974a89c96SAshutosh Naik }; 32074a89c96SAshutosh Naik 321e107b8eeSmasc@theaterzentrum.at static struct key_entry keymap_aopen_1559as[] = { 322e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x01, KEY_HELP }, 323e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x06, KEY_PROG3 }, 324e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x11, KEY_PROG1 }, 325e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x12, KEY_PROG2 }, 326e107b8eeSmasc@theaterzentrum.at { KE_WIFI, 0x30, 0 }, 327e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x31, KEY_MAIL }, 328e107b8eeSmasc@theaterzentrum.at { KE_KEY, 0x36, KEY_WWW }, 329e107b8eeSmasc@theaterzentrum.at }; 330e107b8eeSmasc@theaterzentrum.at 3315fc14680SDmitry Torokhov /* 3325fc14680SDmitry Torokhov * If your machine is not here (which is currently rather likely), please send 3335fc14680SDmitry Torokhov * a list of buttons and their key codes (reported when loading this module 3345fc14680SDmitry Torokhov * with force=1) and the output of dmidecode to $MODULE_AUTHOR. 3355fc14680SDmitry Torokhov */ 3365fc14680SDmitry Torokhov static struct dmi_system_id dmi_ids[] = { 3375fc14680SDmitry Torokhov { 3385fc14680SDmitry Torokhov .callback = dmi_matched, 3395fc14680SDmitry Torokhov .ident = "Fujitsu-Siemens Amilo Pro V2000", 3405fc14680SDmitry Torokhov .matches = { 3415fc14680SDmitry Torokhov DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3425fc14680SDmitry Torokhov DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"), 3435fc14680SDmitry Torokhov }, 3445fc14680SDmitry Torokhov .driver_data = keymap_fs_amilo_pro_v2000 3455fc14680SDmitry Torokhov }, 34684b256a6SBernhard Rosenkraenzer { 34784b256a6SBernhard Rosenkraenzer .callback = dmi_matched, 3488a1b1708SStefan Rompf .ident = "Fujitsu-Siemens Amilo M7400", 3498a1b1708SStefan Rompf .matches = { 3508a1b1708SStefan Rompf DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 3518a1b1708SStefan Rompf DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "), 3528a1b1708SStefan Rompf }, 3538a1b1708SStefan Rompf .driver_data = keymap_fs_amilo_pro_v2000 3548a1b1708SStefan Rompf }, 3558a1b1708SStefan Rompf { 3568a1b1708SStefan Rompf .callback = dmi_matched, 357e2aa507aSJohn Reed Riley .ident = "Fujitsu N3510", 358e2aa507aSJohn Reed Riley .matches = { 359e2aa507aSJohn Reed Riley DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), 360e2aa507aSJohn Reed Riley DMI_MATCH(DMI_PRODUCT_NAME, "N3510"), 361e2aa507aSJohn Reed Riley }, 362e2aa507aSJohn Reed Riley .driver_data = keymap_fujitsu_n3510 363e2aa507aSJohn Reed Riley }, 364e2aa507aSJohn Reed Riley { 365e2aa507aSJohn Reed Riley .callback = dmi_matched, 36684b256a6SBernhard Rosenkraenzer .ident = "Acer Aspire 1500", 36784b256a6SBernhard Rosenkraenzer .matches = { 36884b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 36984b256a6SBernhard Rosenkraenzer DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"), 37084b256a6SBernhard Rosenkraenzer }, 37184b256a6SBernhard Rosenkraenzer .driver_data = keymap_acer_aspire_1500 37284b256a6SBernhard Rosenkraenzer }, 37374a89c96SAshutosh Naik { 37474a89c96SAshutosh Naik .callback = dmi_matched, 37574a89c96SAshutosh Naik .ident = "Acer TravelMate 240", 37674a89c96SAshutosh Naik .matches = { 37774a89c96SAshutosh Naik DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 37874a89c96SAshutosh Naik DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"), 37974a89c96SAshutosh Naik }, 38074a89c96SAshutosh Naik .driver_data = keymap_acer_travelmate_240 38174a89c96SAshutosh Naik }, 382e107b8eeSmasc@theaterzentrum.at { 383e107b8eeSmasc@theaterzentrum.at .callback = dmi_matched, 384e107b8eeSmasc@theaterzentrum.at .ident = "AOpen 1559AS", 385e107b8eeSmasc@theaterzentrum.at .matches = { 386e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_PRODUCT_NAME, "E2U"), 387e107b8eeSmasc@theaterzentrum.at DMI_MATCH(DMI_BOARD_NAME, "E2U"), 388e107b8eeSmasc@theaterzentrum.at }, 389e107b8eeSmasc@theaterzentrum.at .driver_data = keymap_aopen_1559as 390e107b8eeSmasc@theaterzentrum.at }, 39181f0a91eSAl Viro { NULL, } 3925fc14680SDmitry Torokhov }; 3935fc14680SDmitry Torokhov 3945fc14680SDmitry Torokhov static int __init select_keymap(void) 3955fc14680SDmitry Torokhov { 3965fc14680SDmitry Torokhov if (keymap_name != NULL) { 3975fc14680SDmitry Torokhov if (strcmp (keymap_name, "1557/MS2141") == 0) 3985fc14680SDmitry Torokhov keymap = keymap_wistron_ms2141; 3995fc14680SDmitry Torokhov else { 4005fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: Keymap unknown\n"); 4015fc14680SDmitry Torokhov return -EINVAL; 4025fc14680SDmitry Torokhov } 4035fc14680SDmitry Torokhov } 4045fc14680SDmitry Torokhov dmi_check_system(dmi_ids); 4055fc14680SDmitry Torokhov if (keymap == NULL) { 4065fc14680SDmitry Torokhov if (!force) { 4075fc14680SDmitry Torokhov printk(KERN_ERR "wistron_btns: System unknown\n"); 4085fc14680SDmitry Torokhov return -ENODEV; 4095fc14680SDmitry Torokhov } 4105fc14680SDmitry Torokhov keymap = keymap_empty; 4115fc14680SDmitry Torokhov } 4125fc14680SDmitry Torokhov return 0; 4135fc14680SDmitry Torokhov } 4145fc14680SDmitry Torokhov 4155fc14680SDmitry Torokhov /* Input layer interface */ 4165fc14680SDmitry Torokhov 41722a397e2SDmitry Torokhov static struct input_dev *input_dev; 4185fc14680SDmitry Torokhov 419e7c3aad5SDmitry Torokhov static int __devinit setup_input_dev(void) 4205fc14680SDmitry Torokhov { 4215fc14680SDmitry Torokhov const struct key_entry *key; 42222a397e2SDmitry Torokhov int error; 42322a397e2SDmitry Torokhov 42422a397e2SDmitry Torokhov input_dev = input_allocate_device(); 42522a397e2SDmitry Torokhov if (!input_dev) 42622a397e2SDmitry Torokhov return -ENOMEM; 42722a397e2SDmitry Torokhov 42822a397e2SDmitry Torokhov input_dev->name = "Wistron laptop buttons"; 42922a397e2SDmitry Torokhov input_dev->phys = "wistron/input0"; 43022a397e2SDmitry Torokhov input_dev->id.bustype = BUS_HOST; 431a5b0cc80SDmitry Torokhov input_dev->cdev.dev = &wistron_device->dev; 4325fc14680SDmitry Torokhov 4335fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 4345fc14680SDmitry Torokhov if (key->type == KE_KEY) { 43522a397e2SDmitry Torokhov input_dev->evbit[LONG(EV_KEY)] = BIT(EV_KEY); 43622a397e2SDmitry Torokhov set_bit(key->keycode, input_dev->keybit); 4375fc14680SDmitry Torokhov } 4385fc14680SDmitry Torokhov } 4395fc14680SDmitry Torokhov 44022a397e2SDmitry Torokhov error = input_register_device(input_dev); 44122a397e2SDmitry Torokhov if (error) { 44222a397e2SDmitry Torokhov input_free_device(input_dev); 44322a397e2SDmitry Torokhov return error; 44422a397e2SDmitry Torokhov } 44522a397e2SDmitry Torokhov 44622a397e2SDmitry Torokhov return 0; 4475fc14680SDmitry Torokhov } 4485fc14680SDmitry Torokhov 4495fc14680SDmitry Torokhov static void report_key(unsigned keycode) 4505fc14680SDmitry Torokhov { 45122a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 1); 45222a397e2SDmitry Torokhov input_sync(input_dev); 45322a397e2SDmitry Torokhov input_report_key(input_dev, keycode, 0); 45422a397e2SDmitry Torokhov input_sync(input_dev); 4555fc14680SDmitry Torokhov } 4565fc14680SDmitry Torokhov 4575fc14680SDmitry Torokhov /* Driver core */ 4585fc14680SDmitry Torokhov 4595fc14680SDmitry Torokhov static int wifi_enabled; 46084b256a6SBernhard Rosenkraenzer static int bluetooth_enabled; 4615fc14680SDmitry Torokhov 4625fc14680SDmitry Torokhov static void poll_bios(unsigned long); 4635fc14680SDmitry Torokhov 4645fc14680SDmitry Torokhov static struct timer_list poll_timer = TIMER_INITIALIZER(poll_bios, 0, 0); 4655fc14680SDmitry Torokhov 4665fc14680SDmitry Torokhov static void handle_key(u8 code) 4675fc14680SDmitry Torokhov { 4685fc14680SDmitry Torokhov const struct key_entry *key; 4695fc14680SDmitry Torokhov 4705fc14680SDmitry Torokhov for (key = keymap; key->type != KE_END; key++) { 4715fc14680SDmitry Torokhov if (code == key->code) { 4725fc14680SDmitry Torokhov switch (key->type) { 4735fc14680SDmitry Torokhov case KE_KEY: 4745fc14680SDmitry Torokhov report_key(key->keycode); 4755fc14680SDmitry Torokhov break; 4765fc14680SDmitry Torokhov 4775fc14680SDmitry Torokhov case KE_WIFI: 4785fc14680SDmitry Torokhov if (have_wifi) { 4795fc14680SDmitry Torokhov wifi_enabled = !wifi_enabled; 48084b256a6SBernhard Rosenkraenzer bios_set_state(WIFI, wifi_enabled); 48184b256a6SBernhard Rosenkraenzer } 48284b256a6SBernhard Rosenkraenzer break; 48384b256a6SBernhard Rosenkraenzer 48484b256a6SBernhard Rosenkraenzer case KE_BLUETOOTH: 48584b256a6SBernhard Rosenkraenzer if (have_bluetooth) { 48684b256a6SBernhard Rosenkraenzer bluetooth_enabled = !bluetooth_enabled; 48784b256a6SBernhard Rosenkraenzer bios_set_state(BLUETOOTH, bluetooth_enabled); 4885fc14680SDmitry Torokhov } 4895fc14680SDmitry Torokhov break; 4905fc14680SDmitry Torokhov 4915fc14680SDmitry Torokhov case KE_END: 4925fc14680SDmitry Torokhov default: 4935fc14680SDmitry Torokhov BUG(); 4945fc14680SDmitry Torokhov } 4955fc14680SDmitry Torokhov return; 4965fc14680SDmitry Torokhov } 4975fc14680SDmitry Torokhov } 4985fc14680SDmitry Torokhov printk(KERN_NOTICE "wistron_btns: Unknown key code %02X\n", code); 4995fc14680SDmitry Torokhov } 5005fc14680SDmitry Torokhov 5015fc14680SDmitry Torokhov static void poll_bios(unsigned long discard) 5025fc14680SDmitry Torokhov { 5035fc14680SDmitry Torokhov u8 qlen; 5045fc14680SDmitry Torokhov u16 val; 5055fc14680SDmitry Torokhov 5065fc14680SDmitry Torokhov for (;;) { 5075fc14680SDmitry Torokhov qlen = CMOS_READ(cmos_address); 5085fc14680SDmitry Torokhov if (qlen == 0) 5095fc14680SDmitry Torokhov break; 5105fc14680SDmitry Torokhov val = bios_pop_queue(); 5115fc14680SDmitry Torokhov if (val != 0 && !discard) 5125fc14680SDmitry Torokhov handle_key((u8)val); 5135fc14680SDmitry Torokhov } 5145fc14680SDmitry Torokhov 5155fc14680SDmitry Torokhov mod_timer(&poll_timer, jiffies + HZ / POLL_FREQUENCY); 5165fc14680SDmitry Torokhov } 5175fc14680SDmitry Torokhov 518e7c3aad5SDmitry Torokhov static int __devinit wistron_probe(struct platform_device *dev) 519e7c3aad5SDmitry Torokhov { 520e7c3aad5SDmitry Torokhov int err = setup_input_dev(); 521e7c3aad5SDmitry Torokhov if (err) 522e7c3aad5SDmitry Torokhov return err; 523e7c3aad5SDmitry Torokhov 524e7c3aad5SDmitry Torokhov bios_attach(); 525e7c3aad5SDmitry Torokhov cmos_address = bios_get_cmos_address(); 526e7c3aad5SDmitry Torokhov 527e7c3aad5SDmitry Torokhov if (have_wifi) { 528e7c3aad5SDmitry Torokhov u16 wifi = bios_get_default_setting(WIFI); 529e7c3aad5SDmitry Torokhov if (wifi & 1) 530e7c3aad5SDmitry Torokhov wifi_enabled = (wifi & 2) ? 1 : 0; 531e7c3aad5SDmitry Torokhov else 532e7c3aad5SDmitry Torokhov have_wifi = 0; 533e7c3aad5SDmitry Torokhov 534e7c3aad5SDmitry Torokhov if (have_wifi) 535e7c3aad5SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 536e7c3aad5SDmitry Torokhov } 537e7c3aad5SDmitry Torokhov 538e7c3aad5SDmitry Torokhov if (have_bluetooth) { 539e7c3aad5SDmitry Torokhov u16 bt = bios_get_default_setting(BLUETOOTH); 540e7c3aad5SDmitry Torokhov if (bt & 1) 541e7c3aad5SDmitry Torokhov bluetooth_enabled = (bt & 2) ? 1 : 0; 542e7c3aad5SDmitry Torokhov else 543e7c3aad5SDmitry Torokhov have_bluetooth = 0; 544e7c3aad5SDmitry Torokhov 545e7c3aad5SDmitry Torokhov if (have_bluetooth) 546e7c3aad5SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 547e7c3aad5SDmitry Torokhov } 548e7c3aad5SDmitry Torokhov 549e7c3aad5SDmitry Torokhov poll_bios(1); /* Flush stale event queue and arm timer */ 550e7c3aad5SDmitry Torokhov 551e7c3aad5SDmitry Torokhov return 0; 552e7c3aad5SDmitry Torokhov } 553e7c3aad5SDmitry Torokhov 554e7c3aad5SDmitry Torokhov static int __devexit wistron_remove(struct platform_device *dev) 555e7c3aad5SDmitry Torokhov { 556e7c3aad5SDmitry Torokhov del_timer_sync(&poll_timer); 557e7c3aad5SDmitry Torokhov input_unregister_device(input_dev); 558e7c3aad5SDmitry Torokhov bios_detach(); 559e7c3aad5SDmitry Torokhov 560e7c3aad5SDmitry Torokhov return 0; 561e7c3aad5SDmitry Torokhov } 562e7c3aad5SDmitry Torokhov 563e7c3aad5SDmitry Torokhov #ifdef CONFIG_PM 564a5b0cc80SDmitry Torokhov static int wistron_suspend(struct platform_device *dev, pm_message_t state) 565a5b0cc80SDmitry Torokhov { 566a5b0cc80SDmitry Torokhov del_timer_sync(&poll_timer); 567a5b0cc80SDmitry Torokhov 568e753b650SMiloslav Trmac if (have_wifi) 569e753b650SMiloslav Trmac bios_set_state(WIFI, 0); 570e753b650SMiloslav Trmac 571e753b650SMiloslav Trmac if (have_bluetooth) 572e753b650SMiloslav Trmac bios_set_state(BLUETOOTH, 0); 573e753b650SMiloslav Trmac 574a5b0cc80SDmitry Torokhov return 0; 575a5b0cc80SDmitry Torokhov } 576a5b0cc80SDmitry Torokhov 577a5b0cc80SDmitry Torokhov static int wistron_resume(struct platform_device *dev) 578a5b0cc80SDmitry Torokhov { 579a5b0cc80SDmitry Torokhov if (have_wifi) 580a5b0cc80SDmitry Torokhov bios_set_state(WIFI, wifi_enabled); 581a5b0cc80SDmitry Torokhov 582a5b0cc80SDmitry Torokhov if (have_bluetooth) 583a5b0cc80SDmitry Torokhov bios_set_state(BLUETOOTH, bluetooth_enabled); 584a5b0cc80SDmitry Torokhov 585a5b0cc80SDmitry Torokhov poll_bios(1); 586a5b0cc80SDmitry Torokhov 587a5b0cc80SDmitry Torokhov return 0; 588a5b0cc80SDmitry Torokhov } 589e7c3aad5SDmitry Torokhov #else 590e7c3aad5SDmitry Torokhov #define wistron_suspend NULL 591e7c3aad5SDmitry Torokhov #define wistron_resume NULL 592e7c3aad5SDmitry Torokhov #endif 593a5b0cc80SDmitry Torokhov 594a5b0cc80SDmitry Torokhov static struct platform_driver wistron_driver = { 595a5b0cc80SDmitry Torokhov .driver = { 596a5b0cc80SDmitry Torokhov .name = "wistron-bios", 597e7c3aad5SDmitry Torokhov .owner = THIS_MODULE, 598a5b0cc80SDmitry Torokhov }, 599e7c3aad5SDmitry Torokhov .probe = wistron_probe, 600e7c3aad5SDmitry Torokhov .remove = __devexit_p(wistron_remove), 601e7c3aad5SDmitry Torokhov .suspend = wistron_suspend, 602e7c3aad5SDmitry Torokhov .resume = wistron_resume, 603a5b0cc80SDmitry Torokhov }; 604a5b0cc80SDmitry Torokhov 6055fc14680SDmitry Torokhov static int __init wb_module_init(void) 6065fc14680SDmitry Torokhov { 6075fc14680SDmitry Torokhov int err; 6085fc14680SDmitry Torokhov 6095fc14680SDmitry Torokhov err = select_keymap(); 6105fc14680SDmitry Torokhov if (err) 6115fc14680SDmitry Torokhov return err; 61222a397e2SDmitry Torokhov 6135fc14680SDmitry Torokhov err = map_bios(); 6145fc14680SDmitry Torokhov if (err) 6155fc14680SDmitry Torokhov return err; 61622a397e2SDmitry Torokhov 617a5b0cc80SDmitry Torokhov err = platform_driver_register(&wistron_driver); 618a5b0cc80SDmitry Torokhov if (err) 619e7c3aad5SDmitry Torokhov goto err_unmap_bios; 620a5b0cc80SDmitry Torokhov 621e7c3aad5SDmitry Torokhov wistron_device = platform_device_alloc("wistron-bios", -1); 622e7c3aad5SDmitry Torokhov if (!wistron_device) { 623e7c3aad5SDmitry Torokhov err = -ENOMEM; 624a5b0cc80SDmitry Torokhov goto err_unregister_driver; 625a5b0cc80SDmitry Torokhov } 626a5b0cc80SDmitry Torokhov 627e7c3aad5SDmitry Torokhov err = platform_device_add(wistron_device); 628a5b0cc80SDmitry Torokhov if (err) 629e7c3aad5SDmitry Torokhov goto err_free_device; 6305fc14680SDmitry Torokhov 6315fc14680SDmitry Torokhov return 0; 632a5b0cc80SDmitry Torokhov 633e7c3aad5SDmitry Torokhov err_free_device: 634e7c3aad5SDmitry Torokhov platform_device_put(wistron_device); 635a5b0cc80SDmitry Torokhov err_unregister_driver: 636a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 637e7c3aad5SDmitry Torokhov err_unmap_bios: 638a5b0cc80SDmitry Torokhov unmap_bios(); 639a5b0cc80SDmitry Torokhov 640a5b0cc80SDmitry Torokhov return err; 6415fc14680SDmitry Torokhov } 6425fc14680SDmitry Torokhov 6435fc14680SDmitry Torokhov static void __exit wb_module_exit(void) 6445fc14680SDmitry Torokhov { 645a5b0cc80SDmitry Torokhov platform_device_unregister(wistron_device); 646a5b0cc80SDmitry Torokhov platform_driver_unregister(&wistron_driver); 6475fc14680SDmitry Torokhov unmap_bios(); 6485fc14680SDmitry Torokhov } 6495fc14680SDmitry Torokhov 6505fc14680SDmitry Torokhov module_init(wb_module_init); 6515fc14680SDmitry Torokhov module_exit(wb_module_exit); 652