Path: blob/master/drivers/input/misc/wistron_btns.c
15109 views
/*1* Wistron laptop button driver2* Copyright (C) 2005 Miloslav Trmac <[email protected]>3* Copyright (C) 2005 Bernhard Rosenkraenzer <[email protected]>4* Copyright (C) 2005 Dmitry Torokhov <[email protected]>5*6* You can redistribute and/or modify this program under the terms of the7* GNU General Public License version 2 as published by the Free Software8* Foundation.9*10* This program is distributed in the hope that it will be useful, but11* WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General13* Public License for more details.14*15* You should have received a copy of the GNU General Public License along16* with this program; if not, write to the Free Software Foundation, Inc.,17* 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.18*/19#include <linux/io.h>20#include <linux/dmi.h>21#include <linux/init.h>22#include <linux/input-polldev.h>23#include <linux/input/sparse-keymap.h>24#include <linux/interrupt.h>25#include <linux/jiffies.h>26#include <linux/kernel.h>27#include <linux/mc146818rtc.h>28#include <linux/module.h>29#include <linux/preempt.h>30#include <linux/string.h>31#include <linux/slab.h>32#include <linux/types.h>33#include <linux/platform_device.h>34#include <linux/leds.h>3536/* How often we poll keys - msecs */37#define POLL_INTERVAL_DEFAULT 500 /* when idle */38#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */3940/* BIOS subsystem IDs */41#define WIFI 0x3542#define BLUETOOTH 0x3443#define MAIL_LED 0x314445MODULE_AUTHOR("Miloslav Trmac <[email protected]>");46MODULE_DESCRIPTION("Wistron laptop button driver");47MODULE_LICENSE("GPL v2");48MODULE_VERSION("0.3");4950static int force; /* = 0; */51module_param(force, bool, 0);52MODULE_PARM_DESC(force, "Load even if computer is not in database");5354static char *keymap_name; /* = NULL; */55module_param_named(keymap, keymap_name, charp, 0);56MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]");5758static struct platform_device *wistron_device;5960/* BIOS interface implementation */6162static void __iomem *bios_entry_point; /* BIOS routine entry point */63static void __iomem *bios_code_map_base;64static void __iomem *bios_data_map_base;6566static u8 cmos_address;6768struct regs {69u32 eax, ebx, ecx;70};7172static void call_bios(struct regs *regs)73{74unsigned long flags;7576preempt_disable();77local_irq_save(flags);78asm volatile ("pushl %%ebp;"79"movl %7, %%ebp;"80"call *%6;"81"popl %%ebp"82: "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx)83: "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx),84"m" (bios_entry_point), "m" (bios_data_map_base)85: "edx", "edi", "esi", "memory");86local_irq_restore(flags);87preempt_enable();88}8990static ssize_t __init locate_wistron_bios(void __iomem *base)91{92static unsigned char __initdata signature[] =93{ 0x42, 0x21, 0x55, 0x30 };94ssize_t offset;9596for (offset = 0; offset < 0x10000; offset += 0x10) {97if (check_signature(base + offset, signature,98sizeof(signature)) != 0)99return offset;100}101return -1;102}103104static int __init map_bios(void)105{106void __iomem *base;107ssize_t offset;108u32 entry_point;109110base = ioremap(0xF0000, 0x10000); /* Can't fail */111offset = locate_wistron_bios(base);112if (offset < 0) {113printk(KERN_ERR "wistron_btns: BIOS entry point not found\n");114iounmap(base);115return -ENODEV;116}117118entry_point = readl(base + offset + 5);119printk(KERN_DEBUG120"wistron_btns: BIOS signature found at %p, entry point %08X\n",121base + offset, entry_point);122123if (entry_point >= 0xF0000) {124bios_code_map_base = base;125bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF);126} else {127iounmap(base);128bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000);129if (bios_code_map_base == NULL) {130printk(KERN_ERR131"wistron_btns: Can't map BIOS code at %08X\n",132entry_point & ~0x3FFF);133goto err;134}135bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF);136}137/* The Windows driver maps 0x10000 bytes, we keep only one page... */138bios_data_map_base = ioremap(0x400, 0xc00);139if (bios_data_map_base == NULL) {140printk(KERN_ERR "wistron_btns: Can't map BIOS data\n");141goto err_code;142}143return 0;144145err_code:146iounmap(bios_code_map_base);147err:148return -ENOMEM;149}150151static inline void unmap_bios(void)152{153iounmap(bios_code_map_base);154iounmap(bios_data_map_base);155}156157/* BIOS calls */158159static u16 bios_pop_queue(void)160{161struct regs regs;162163memset(®s, 0, sizeof (regs));164regs.eax = 0x9610;165regs.ebx = 0x061C;166regs.ecx = 0x0000;167call_bios(®s);168169return regs.eax;170}171172static void __devinit bios_attach(void)173{174struct regs regs;175176memset(®s, 0, sizeof (regs));177regs.eax = 0x9610;178regs.ebx = 0x012E;179call_bios(®s);180}181182static void bios_detach(void)183{184struct regs regs;185186memset(®s, 0, sizeof (regs));187regs.eax = 0x9610;188regs.ebx = 0x002E;189call_bios(®s);190}191192static u8 __devinit bios_get_cmos_address(void)193{194struct regs regs;195196memset(®s, 0, sizeof (regs));197regs.eax = 0x9610;198regs.ebx = 0x051C;199call_bios(®s);200201return regs.ecx;202}203204static u16 __devinit bios_get_default_setting(u8 subsys)205{206struct regs regs;207208memset(®s, 0, sizeof (regs));209regs.eax = 0x9610;210regs.ebx = 0x0200 | subsys;211call_bios(®s);212213return regs.eax;214}215216static void bios_set_state(u8 subsys, int enable)217{218struct regs regs;219220memset(®s, 0, sizeof (regs));221regs.eax = 0x9610;222regs.ebx = (enable ? 0x0100 : 0x0000) | subsys;223call_bios(®s);224}225226/* Hardware database */227228#define KE_WIFI (KE_LAST + 1)229#define KE_BLUETOOTH (KE_LAST + 2)230231#define FE_MAIL_LED 0x01232#define FE_WIFI_LED 0x02233#define FE_UNTESTED 0x80234235static struct key_entry *keymap; /* = NULL; Current key map */236static bool have_wifi;237static bool have_bluetooth;238static int leds_present; /* bitmask of leds present */239240static int __init dmi_matched(const struct dmi_system_id *dmi)241{242const struct key_entry *key;243244keymap = dmi->driver_data;245for (key = keymap; key->type != KE_END; key++) {246if (key->type == KE_WIFI)247have_wifi = true;248else if (key->type == KE_BLUETOOTH)249have_bluetooth = true;250}251leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED);252253return 1;254}255256static struct key_entry keymap_empty[] __initdata = {257{ KE_END, 0 }258};259260static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {261{ KE_KEY, 0x01, {KEY_HELP} },262{ KE_KEY, 0x11, {KEY_PROG1} },263{ KE_KEY, 0x12, {KEY_PROG2} },264{ KE_WIFI, 0x30 },265{ KE_KEY, 0x31, {KEY_MAIL} },266{ KE_KEY, 0x36, {KEY_WWW} },267{ KE_END, 0 }268};269270static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {271{ KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */272{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */273{ KE_BLUETOOTH, 0x30 }, /* Fn+F10 */274{ KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */275{ KE_KEY, 0x36, {KEY_WWW} }, /* www button */276{ KE_WIFI, 0x78 }, /* satellite dish button */277{ KE_END, 0 }278};279280static struct key_entry keymap_fujitsu_n3510[] __initdata = {281{ KE_KEY, 0x11, {KEY_PROG1} },282{ KE_KEY, 0x12, {KEY_PROG2} },283{ KE_KEY, 0x36, {KEY_WWW} },284{ KE_KEY, 0x31, {KEY_MAIL} },285{ KE_KEY, 0x71, {KEY_STOPCD} },286{ KE_KEY, 0x72, {KEY_PLAYPAUSE} },287{ KE_KEY, 0x74, {KEY_REWIND} },288{ KE_KEY, 0x78, {KEY_FORWARD} },289{ KE_END, 0 }290};291292static struct key_entry keymap_wistron_ms2111[] __initdata = {293{ KE_KEY, 0x11, {KEY_PROG1} },294{ KE_KEY, 0x12, {KEY_PROG2} },295{ KE_KEY, 0x13, {KEY_PROG3} },296{ KE_KEY, 0x31, {KEY_MAIL} },297{ KE_KEY, 0x36, {KEY_WWW} },298{ KE_END, FE_MAIL_LED }299};300301static struct key_entry keymap_wistron_md40100[] __initdata = {302{ KE_KEY, 0x01, {KEY_HELP} },303{ KE_KEY, 0x02, {KEY_CONFIG} },304{ KE_KEY, 0x31, {KEY_MAIL} },305{ KE_KEY, 0x36, {KEY_WWW} },306{ KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */307{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }308};309310static struct key_entry keymap_wistron_ms2141[] __initdata = {311{ KE_KEY, 0x11, {KEY_PROG1} },312{ KE_KEY, 0x12, {KEY_PROG2} },313{ KE_WIFI, 0x30 },314{ KE_KEY, 0x22, {KEY_REWIND} },315{ KE_KEY, 0x23, {KEY_FORWARD} },316{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },317{ KE_KEY, 0x25, {KEY_STOPCD} },318{ KE_KEY, 0x31, {KEY_MAIL} },319{ KE_KEY, 0x36, {KEY_WWW} },320{ KE_END, 0 }321};322323static struct key_entry keymap_acer_aspire_1500[] __initdata = {324{ KE_KEY, 0x01, {KEY_HELP} },325{ KE_KEY, 0x03, {KEY_POWER} },326{ KE_KEY, 0x11, {KEY_PROG1} },327{ KE_KEY, 0x12, {KEY_PROG2} },328{ KE_WIFI, 0x30 },329{ KE_KEY, 0x31, {KEY_MAIL} },330{ KE_KEY, 0x36, {KEY_WWW} },331{ KE_KEY, 0x49, {KEY_CONFIG} },332{ KE_BLUETOOTH, 0x44 },333{ KE_END, FE_UNTESTED }334};335336static struct key_entry keymap_acer_aspire_1600[] __initdata = {337{ KE_KEY, 0x01, {KEY_HELP} },338{ KE_KEY, 0x03, {KEY_POWER} },339{ KE_KEY, 0x08, {KEY_MUTE} },340{ KE_KEY, 0x11, {KEY_PROG1} },341{ KE_KEY, 0x12, {KEY_PROG2} },342{ KE_KEY, 0x13, {KEY_PROG3} },343{ KE_KEY, 0x31, {KEY_MAIL} },344{ KE_KEY, 0x36, {KEY_WWW} },345{ KE_KEY, 0x49, {KEY_CONFIG} },346{ KE_WIFI, 0x30 },347{ KE_BLUETOOTH, 0x44 },348{ KE_END, FE_MAIL_LED | FE_UNTESTED }349};350351/* 3020 has been tested */352static struct key_entry keymap_acer_aspire_5020[] __initdata = {353{ KE_KEY, 0x01, {KEY_HELP} },354{ KE_KEY, 0x03, {KEY_POWER} },355{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */356{ KE_KEY, 0x11, {KEY_PROG1} },357{ KE_KEY, 0x12, {KEY_PROG2} },358{ KE_KEY, 0x31, {KEY_MAIL} },359{ KE_KEY, 0x36, {KEY_WWW} },360{ KE_KEY, 0x6a, {KEY_CONFIG} },361{ KE_WIFI, 0x30 },362{ KE_BLUETOOTH, 0x44 },363{ KE_END, FE_MAIL_LED | FE_UNTESTED }364};365366static struct key_entry keymap_acer_travelmate_2410[] __initdata = {367{ KE_KEY, 0x01, {KEY_HELP} },368{ KE_KEY, 0x6d, {KEY_POWER} },369{ KE_KEY, 0x11, {KEY_PROG1} },370{ KE_KEY, 0x12, {KEY_PROG2} },371{ KE_KEY, 0x31, {KEY_MAIL} },372{ KE_KEY, 0x36, {KEY_WWW} },373{ KE_KEY, 0x6a, {KEY_CONFIG} },374{ KE_WIFI, 0x30 },375{ KE_BLUETOOTH, 0x44 },376{ KE_END, FE_MAIL_LED | FE_UNTESTED }377};378379static struct key_entry keymap_acer_travelmate_110[] __initdata = {380{ KE_KEY, 0x01, {KEY_HELP} },381{ KE_KEY, 0x02, {KEY_CONFIG} },382{ KE_KEY, 0x03, {KEY_POWER} },383{ KE_KEY, 0x08, {KEY_MUTE} },384{ KE_KEY, 0x11, {KEY_PROG1} },385{ KE_KEY, 0x12, {KEY_PROG2} },386{ KE_KEY, 0x20, {KEY_VOLUMEUP} },387{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },388{ KE_KEY, 0x31, {KEY_MAIL} },389{ KE_KEY, 0x36, {KEY_WWW} },390{ KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */391{ KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */392{ KE_WIFI, 0x30 },393{ KE_END, FE_MAIL_LED | FE_UNTESTED }394};395396static struct key_entry keymap_acer_travelmate_300[] __initdata = {397{ KE_KEY, 0x01, {KEY_HELP} },398{ KE_KEY, 0x02, {KEY_CONFIG} },399{ KE_KEY, 0x03, {KEY_POWER} },400{ KE_KEY, 0x08, {KEY_MUTE} },401{ KE_KEY, 0x11, {KEY_PROG1} },402{ KE_KEY, 0x12, {KEY_PROG2} },403{ KE_KEY, 0x20, {KEY_VOLUMEUP} },404{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },405{ KE_KEY, 0x31, {KEY_MAIL} },406{ KE_KEY, 0x36, {KEY_WWW} },407{ KE_WIFI, 0x30 },408{ KE_BLUETOOTH, 0x44 },409{ KE_END, FE_MAIL_LED | FE_UNTESTED }410};411412static struct key_entry keymap_acer_travelmate_380[] __initdata = {413{ KE_KEY, 0x01, {KEY_HELP} },414{ KE_KEY, 0x02, {KEY_CONFIG} },415{ KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */416{ KE_KEY, 0x11, {KEY_PROG1} },417{ KE_KEY, 0x12, {KEY_PROG2} },418{ KE_KEY, 0x13, {KEY_PROG3} },419{ KE_KEY, 0x31, {KEY_MAIL} },420{ KE_KEY, 0x36, {KEY_WWW} },421{ KE_WIFI, 0x30 },422{ KE_END, FE_MAIL_LED | FE_UNTESTED }423};424425/* unusual map */426static struct key_entry keymap_acer_travelmate_220[] __initdata = {427{ KE_KEY, 0x01, {KEY_HELP} },428{ KE_KEY, 0x02, {KEY_CONFIG} },429{ KE_KEY, 0x11, {KEY_MAIL} },430{ KE_KEY, 0x12, {KEY_WWW} },431{ KE_KEY, 0x13, {KEY_PROG2} },432{ KE_KEY, 0x31, {KEY_PROG1} },433{ KE_END, FE_WIFI_LED | FE_UNTESTED }434};435436static struct key_entry keymap_acer_travelmate_230[] __initdata = {437{ KE_KEY, 0x01, {KEY_HELP} },438{ KE_KEY, 0x02, {KEY_CONFIG} },439{ KE_KEY, 0x11, {KEY_PROG1} },440{ KE_KEY, 0x12, {KEY_PROG2} },441{ KE_KEY, 0x31, {KEY_MAIL} },442{ KE_KEY, 0x36, {KEY_WWW} },443{ KE_END, FE_WIFI_LED | FE_UNTESTED }444};445446static struct key_entry keymap_acer_travelmate_240[] __initdata = {447{ KE_KEY, 0x01, {KEY_HELP} },448{ KE_KEY, 0x02, {KEY_CONFIG} },449{ KE_KEY, 0x03, {KEY_POWER} },450{ KE_KEY, 0x08, {KEY_MUTE} },451{ KE_KEY, 0x31, {KEY_MAIL} },452{ KE_KEY, 0x36, {KEY_WWW} },453{ KE_KEY, 0x11, {KEY_PROG1} },454{ KE_KEY, 0x12, {KEY_PROG2} },455{ KE_BLUETOOTH, 0x44 },456{ KE_WIFI, 0x30 },457{ KE_END, FE_UNTESTED }458};459460static struct key_entry keymap_acer_travelmate_350[] __initdata = {461{ KE_KEY, 0x01, {KEY_HELP} },462{ KE_KEY, 0x02, {KEY_CONFIG} },463{ KE_KEY, 0x11, {KEY_PROG1} },464{ KE_KEY, 0x12, {KEY_PROG2} },465{ KE_KEY, 0x13, {KEY_MAIL} },466{ KE_KEY, 0x14, {KEY_PROG3} },467{ KE_KEY, 0x15, {KEY_WWW} },468{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }469};470471static struct key_entry keymap_acer_travelmate_360[] __initdata = {472{ KE_KEY, 0x01, {KEY_HELP} },473{ KE_KEY, 0x02, {KEY_CONFIG} },474{ KE_KEY, 0x11, {KEY_PROG1} },475{ KE_KEY, 0x12, {KEY_PROG2} },476{ KE_KEY, 0x13, {KEY_MAIL} },477{ KE_KEY, 0x14, {KEY_PROG3} },478{ KE_KEY, 0x15, {KEY_WWW} },479{ KE_KEY, 0x40, {KEY_WLAN} },480{ KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */481};482483/* Wifi subsystem only activates the led. Therefore we need to pass484* wifi event as a normal key, then userspace can really change the wifi state.485* TODO we need to export led state to userspace (wifi and mail) */486static struct key_entry keymap_acer_travelmate_610[] __initdata = {487{ KE_KEY, 0x01, {KEY_HELP} },488{ KE_KEY, 0x02, {KEY_CONFIG} },489{ KE_KEY, 0x11, {KEY_PROG1} },490{ KE_KEY, 0x12, {KEY_PROG2} },491{ KE_KEY, 0x13, {KEY_PROG3} },492{ KE_KEY, 0x14, {KEY_MAIL} },493{ KE_KEY, 0x15, {KEY_WWW} },494{ KE_KEY, 0x40, {KEY_WLAN} },495{ KE_END, FE_MAIL_LED | FE_WIFI_LED }496};497498static struct key_entry keymap_acer_travelmate_630[] __initdata = {499{ KE_KEY, 0x01, {KEY_HELP} },500{ KE_KEY, 0x02, {KEY_CONFIG} },501{ KE_KEY, 0x03, {KEY_POWER} },502{ KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */503{ KE_KEY, 0x11, {KEY_PROG1} },504{ KE_KEY, 0x12, {KEY_PROG2} },505{ KE_KEY, 0x13, {KEY_PROG3} },506{ KE_KEY, 0x20, {KEY_VOLUMEUP} },507{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },508{ KE_KEY, 0x31, {KEY_MAIL} },509{ KE_KEY, 0x36, {KEY_WWW} },510{ KE_WIFI, 0x30 },511{ KE_END, FE_MAIL_LED | FE_UNTESTED }512};513514static struct key_entry keymap_aopen_1559as[] __initdata = {515{ KE_KEY, 0x01, {KEY_HELP} },516{ KE_KEY, 0x06, {KEY_PROG3} },517{ KE_KEY, 0x11, {KEY_PROG1} },518{ KE_KEY, 0x12, {KEY_PROG2} },519{ KE_WIFI, 0x30 },520{ KE_KEY, 0x31, {KEY_MAIL} },521{ KE_KEY, 0x36, {KEY_WWW} },522{ KE_END, 0 },523};524525static struct key_entry keymap_fs_amilo_d88x0[] __initdata = {526{ KE_KEY, 0x01, {KEY_HELP} },527{ KE_KEY, 0x08, {KEY_MUTE} },528{ KE_KEY, 0x31, {KEY_MAIL} },529{ KE_KEY, 0x36, {KEY_WWW} },530{ KE_KEY, 0x11, {KEY_PROG1} },531{ KE_KEY, 0x12, {KEY_PROG2} },532{ KE_KEY, 0x13, {KEY_PROG3} },533{ KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }534};535536static struct key_entry keymap_wistron_md2900[] __initdata = {537{ KE_KEY, 0x01, {KEY_HELP} },538{ KE_KEY, 0x02, {KEY_CONFIG} },539{ KE_KEY, 0x11, {KEY_PROG1} },540{ KE_KEY, 0x12, {KEY_PROG2} },541{ KE_KEY, 0x31, {KEY_MAIL} },542{ KE_KEY, 0x36, {KEY_WWW} },543{ KE_WIFI, 0x30 },544{ KE_END, FE_MAIL_LED | FE_UNTESTED }545};546547static struct key_entry keymap_wistron_md96500[] __initdata = {548{ KE_KEY, 0x01, {KEY_HELP} },549{ KE_KEY, 0x02, {KEY_CONFIG} },550{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */551{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */552{ KE_KEY, 0x08, {KEY_MUTE} },553{ KE_KEY, 0x11, {KEY_PROG1} },554{ KE_KEY, 0x12, {KEY_PROG2} },555{ KE_KEY, 0x20, {KEY_VOLUMEUP} },556{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },557{ KE_KEY, 0x22, {KEY_REWIND} },558{ KE_KEY, 0x23, {KEY_FORWARD} },559{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },560{ KE_KEY, 0x25, {KEY_STOPCD} },561{ KE_KEY, 0x31, {KEY_MAIL} },562{ KE_KEY, 0x36, {KEY_WWW} },563{ KE_WIFI, 0x30 },564{ KE_BLUETOOTH, 0x44 },565{ KE_END, FE_UNTESTED }566};567568static struct key_entry keymap_wistron_generic[] __initdata = {569{ KE_KEY, 0x01, {KEY_HELP} },570{ KE_KEY, 0x02, {KEY_CONFIG} },571{ KE_KEY, 0x03, {KEY_POWER} },572{ KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */573{ KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */574{ KE_KEY, 0x08, {KEY_MUTE} },575{ KE_KEY, 0x11, {KEY_PROG1} },576{ KE_KEY, 0x12, {KEY_PROG2} },577{ KE_KEY, 0x13, {KEY_PROG3} },578{ KE_KEY, 0x14, {KEY_MAIL} },579{ KE_KEY, 0x15, {KEY_WWW} },580{ KE_KEY, 0x20, {KEY_VOLUMEUP} },581{ KE_KEY, 0x21, {KEY_VOLUMEDOWN} },582{ KE_KEY, 0x22, {KEY_REWIND} },583{ KE_KEY, 0x23, {KEY_FORWARD} },584{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },585{ KE_KEY, 0x25, {KEY_STOPCD} },586{ KE_KEY, 0x31, {KEY_MAIL} },587{ KE_KEY, 0x36, {KEY_WWW} },588{ KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */589{ KE_KEY, 0x40, {KEY_WLAN} },590{ KE_KEY, 0x49, {KEY_CONFIG} },591{ KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */592{ KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */593{ KE_KEY, 0x6a, {KEY_CONFIG} },594{ KE_KEY, 0x6d, {KEY_POWER} },595{ KE_KEY, 0x71, {KEY_STOPCD} },596{ KE_KEY, 0x72, {KEY_PLAYPAUSE} },597{ KE_KEY, 0x74, {KEY_REWIND} },598{ KE_KEY, 0x78, {KEY_FORWARD} },599{ KE_WIFI, 0x30 },600{ KE_BLUETOOTH, 0x44 },601{ KE_END, 0 }602};603604static struct key_entry keymap_aopen_1557[] __initdata = {605{ KE_KEY, 0x01, {KEY_HELP} },606{ KE_KEY, 0x11, {KEY_PROG1} },607{ KE_KEY, 0x12, {KEY_PROG2} },608{ KE_WIFI, 0x30 },609{ KE_KEY, 0x22, {KEY_REWIND} },610{ KE_KEY, 0x23, {KEY_FORWARD} },611{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },612{ KE_KEY, 0x25, {KEY_STOPCD} },613{ KE_KEY, 0x31, {KEY_MAIL} },614{ KE_KEY, 0x36, {KEY_WWW} },615{ KE_END, 0 }616};617618static struct key_entry keymap_prestigio[] __initdata = {619{ KE_KEY, 0x11, {KEY_PROG1} },620{ KE_KEY, 0x12, {KEY_PROG2} },621{ KE_WIFI, 0x30 },622{ KE_KEY, 0x22, {KEY_REWIND} },623{ KE_KEY, 0x23, {KEY_FORWARD} },624{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },625{ KE_KEY, 0x25, {KEY_STOPCD} },626{ KE_KEY, 0x31, {KEY_MAIL} },627{ KE_KEY, 0x36, {KEY_WWW} },628{ KE_END, 0 }629};630631632/*633* If your machine is not here (which is currently rather likely), please send634* a list of buttons and their key codes (reported when loading this module635* with force=1) and the output of dmidecode to $MODULE_AUTHOR.636*/637static const struct dmi_system_id __initconst dmi_ids[] = {638{639/* Fujitsu-Siemens Amilo Pro V2000 */640.callback = dmi_matched,641.matches = {642DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),643DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),644},645.driver_data = keymap_fs_amilo_pro_v2000646},647{648/* Fujitsu-Siemens Amilo Pro Edition V3505 */649.callback = dmi_matched,650.matches = {651DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),652DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),653},654.driver_data = keymap_fs_amilo_pro_v3505655},656{657/* Fujitsu-Siemens Amilo M7400 */658.callback = dmi_matched,659.matches = {660DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),661DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "),662},663.driver_data = keymap_fs_amilo_pro_v2000664},665{666/* Maxdata Pro 7000 DX */667.callback = dmi_matched,668.matches = {669DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"),670DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"),671},672.driver_data = keymap_fs_amilo_pro_v2000673},674{675/* Fujitsu N3510 */676.callback = dmi_matched,677.matches = {678DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),679DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),680},681.driver_data = keymap_fujitsu_n3510682},683{684/* Acer Aspire 1500 */685.callback = dmi_matched,686.matches = {687DMI_MATCH(DMI_SYS_VENDOR, "Acer"),688DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),689},690.driver_data = keymap_acer_aspire_1500691},692{693/* Acer Aspire 1600 */694.callback = dmi_matched,695.matches = {696DMI_MATCH(DMI_SYS_VENDOR, "Acer"),697DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),698},699.driver_data = keymap_acer_aspire_1600700},701{702/* Acer Aspire 3020 */703.callback = dmi_matched,704.matches = {705DMI_MATCH(DMI_SYS_VENDOR, "Acer"),706DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),707},708.driver_data = keymap_acer_aspire_5020709},710{711/* Acer Aspire 5020 */712.callback = dmi_matched,713.matches = {714DMI_MATCH(DMI_SYS_VENDOR, "Acer"),715DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),716},717.driver_data = keymap_acer_aspire_5020718},719{720/* Acer TravelMate 2100 */721.callback = dmi_matched,722.matches = {723DMI_MATCH(DMI_SYS_VENDOR, "Acer"),724DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),725},726.driver_data = keymap_acer_aspire_5020727},728{729/* Acer TravelMate 2410 */730.callback = dmi_matched,731.matches = {732DMI_MATCH(DMI_SYS_VENDOR, "Acer"),733DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),734},735.driver_data = keymap_acer_travelmate_2410736},737{738/* Acer TravelMate C300 */739.callback = dmi_matched,740.matches = {741DMI_MATCH(DMI_SYS_VENDOR, "Acer"),742DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),743},744.driver_data = keymap_acer_travelmate_300745},746{747/* Acer TravelMate C100 */748.callback = dmi_matched,749.matches = {750DMI_MATCH(DMI_SYS_VENDOR, "Acer"),751DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),752},753.driver_data = keymap_acer_travelmate_300754},755{756/* Acer TravelMate C110 */757.callback = dmi_matched,758.matches = {759DMI_MATCH(DMI_SYS_VENDOR, "Acer"),760DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),761},762.driver_data = keymap_acer_travelmate_110763},764{765/* Acer TravelMate 380 */766.callback = dmi_matched,767.matches = {768DMI_MATCH(DMI_SYS_VENDOR, "Acer"),769DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),770},771.driver_data = keymap_acer_travelmate_380772},773{774/* Acer TravelMate 370 */775.callback = dmi_matched,776.matches = {777DMI_MATCH(DMI_SYS_VENDOR, "Acer"),778DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),779},780.driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */781},782{783/* Acer TravelMate 220 */784.callback = dmi_matched,785.matches = {786DMI_MATCH(DMI_SYS_VENDOR, "Acer"),787DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),788},789.driver_data = keymap_acer_travelmate_220790},791{792/* Acer TravelMate 260 */793.callback = dmi_matched,794.matches = {795DMI_MATCH(DMI_SYS_VENDOR, "Acer"),796DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),797},798.driver_data = keymap_acer_travelmate_220799},800{801/* Acer TravelMate 230 */802.callback = dmi_matched,803.matches = {804DMI_MATCH(DMI_SYS_VENDOR, "Acer"),805DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),806/* acerhk looks for "TravelMate F4..." ?! */807},808.driver_data = keymap_acer_travelmate_230809},810{811/* Acer TravelMate 280 */812.callback = dmi_matched,813.matches = {814DMI_MATCH(DMI_SYS_VENDOR, "Acer"),815DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),816},817.driver_data = keymap_acer_travelmate_230818},819{820/* Acer TravelMate 240 */821.callback = dmi_matched,822.matches = {823DMI_MATCH(DMI_SYS_VENDOR, "Acer"),824DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),825},826.driver_data = keymap_acer_travelmate_240827},828{829/* Acer TravelMate 250 */830.callback = dmi_matched,831.matches = {832DMI_MATCH(DMI_SYS_VENDOR, "Acer"),833DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),834},835.driver_data = keymap_acer_travelmate_240836},837{838/* Acer TravelMate 2424NWXCi */839.callback = dmi_matched,840.matches = {841DMI_MATCH(DMI_SYS_VENDOR, "Acer"),842DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),843},844.driver_data = keymap_acer_travelmate_240845},846{847/* Acer TravelMate 350 */848.callback = dmi_matched,849.matches = {850DMI_MATCH(DMI_SYS_VENDOR, "Acer"),851DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),852},853.driver_data = keymap_acer_travelmate_350854},855{856/* Acer TravelMate 360 */857.callback = dmi_matched,858.matches = {859DMI_MATCH(DMI_SYS_VENDOR, "Acer"),860DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),861},862.driver_data = keymap_acer_travelmate_360863},864{865/* Acer TravelMate 610 */866.callback = dmi_matched,867.matches = {868DMI_MATCH(DMI_SYS_VENDOR, "ACER"),869DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),870},871.driver_data = keymap_acer_travelmate_610872},873{874/* Acer TravelMate 620 */875.callback = dmi_matched,876.matches = {877DMI_MATCH(DMI_SYS_VENDOR, "Acer"),878DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),879},880.driver_data = keymap_acer_travelmate_630881},882{883/* Acer TravelMate 630 */884.callback = dmi_matched,885.matches = {886DMI_MATCH(DMI_SYS_VENDOR, "Acer"),887DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),888},889.driver_data = keymap_acer_travelmate_630890},891{892/* AOpen 1559AS */893.callback = dmi_matched,894.matches = {895DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),896DMI_MATCH(DMI_BOARD_NAME, "E2U"),897},898.driver_data = keymap_aopen_1559as899},900{901/* Medion MD 9783 */902.callback = dmi_matched,903.matches = {904DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),905DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),906},907.driver_data = keymap_wistron_ms2111908},909{910/* Medion MD 40100 */911.callback = dmi_matched,912.matches = {913DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),914DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),915},916.driver_data = keymap_wistron_md40100917},918{919/* Medion MD 2900 */920.callback = dmi_matched,921.matches = {922DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),923DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),924},925.driver_data = keymap_wistron_md2900926},927{928/* Medion MD 42200 */929.callback = dmi_matched,930.matches = {931DMI_MATCH(DMI_SYS_VENDOR, "Medion"),932DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"),933},934.driver_data = keymap_fs_amilo_pro_v2000935},936{937/* Medion MD 96500 */938.callback = dmi_matched,939.matches = {940DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),941DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),942},943.driver_data = keymap_wistron_md96500944},945{946/* Medion MD 95400 */947.callback = dmi_matched,948.matches = {949DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),950DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),951},952.driver_data = keymap_wistron_md96500953},954{955/* Fujitsu Siemens Amilo D7820 */956.callback = dmi_matched,957.matches = {958DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */959DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),960},961.driver_data = keymap_fs_amilo_d88x0962},963{964/* Fujitsu Siemens Amilo D88x0 */965.callback = dmi_matched,966.matches = {967DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),968DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),969},970.driver_data = keymap_fs_amilo_d88x0971},972{ NULL, }973};974975/* Copy the good keymap, as the original ones are free'd */976static int __init copy_keymap(void)977{978const struct key_entry *key;979struct key_entry *new_keymap;980unsigned int length = 1;981982for (key = keymap; key->type != KE_END; key++)983length++;984985new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),986GFP_KERNEL);987if (!new_keymap)988return -ENOMEM;989990keymap = new_keymap;991992return 0;993}994995static int __init select_keymap(void)996{997dmi_check_system(dmi_ids);998if (keymap_name != NULL) {999if (strcmp (keymap_name, "1557/MS2141") == 0)1000keymap = keymap_wistron_ms2141;1001else if (strcmp (keymap_name, "aopen1557") == 0)1002keymap = keymap_aopen_1557;1003else if (strcmp (keymap_name, "prestigio") == 0)1004keymap = keymap_prestigio;1005else if (strcmp (keymap_name, "generic") == 0)1006keymap = keymap_wistron_generic;1007else {1008printk(KERN_ERR "wistron_btns: Keymap unknown\n");1009return -EINVAL;1010}1011}1012if (keymap == NULL) {1013if (!force) {1014printk(KERN_ERR "wistron_btns: System unknown\n");1015return -ENODEV;1016}1017keymap = keymap_empty;1018}10191020return copy_keymap();1021}10221023/* Input layer interface */10241025static struct input_polled_dev *wistron_idev;1026static unsigned long jiffies_last_press;1027static bool wifi_enabled;1028static bool bluetooth_enabled;10291030/* led management */1031static void wistron_mail_led_set(struct led_classdev *led_cdev,1032enum led_brightness value)1033{1034bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0);1035}10361037/* same as setting up wifi card, but for laptops on which the led is managed */1038static void wistron_wifi_led_set(struct led_classdev *led_cdev,1039enum led_brightness value)1040{1041bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0);1042}10431044static struct led_classdev wistron_mail_led = {1045.name = "wistron:green:mail",1046.brightness_set = wistron_mail_led_set,1047};10481049static struct led_classdev wistron_wifi_led = {1050.name = "wistron:red:wifi",1051.brightness_set = wistron_wifi_led_set,1052};10531054static void __devinit wistron_led_init(struct device *parent)1055{1056if (leds_present & FE_WIFI_LED) {1057u16 wifi = bios_get_default_setting(WIFI);1058if (wifi & 1) {1059wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF;1060if (led_classdev_register(parent, &wistron_wifi_led))1061leds_present &= ~FE_WIFI_LED;1062else1063bios_set_state(WIFI, wistron_wifi_led.brightness);10641065} else1066leds_present &= ~FE_WIFI_LED;1067}10681069if (leds_present & FE_MAIL_LED) {1070/* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */1071wistron_mail_led.brightness = LED_OFF;1072if (led_classdev_register(parent, &wistron_mail_led))1073leds_present &= ~FE_MAIL_LED;1074else1075bios_set_state(MAIL_LED, wistron_mail_led.brightness);1076}1077}10781079static void __devexit wistron_led_remove(void)1080{1081if (leds_present & FE_MAIL_LED)1082led_classdev_unregister(&wistron_mail_led);10831084if (leds_present & FE_WIFI_LED)1085led_classdev_unregister(&wistron_wifi_led);1086}10871088static inline void wistron_led_suspend(void)1089{1090if (leds_present & FE_MAIL_LED)1091led_classdev_suspend(&wistron_mail_led);10921093if (leds_present & FE_WIFI_LED)1094led_classdev_suspend(&wistron_wifi_led);1095}10961097static inline void wistron_led_resume(void)1098{1099if (leds_present & FE_MAIL_LED)1100led_classdev_resume(&wistron_mail_led);11011102if (leds_present & FE_WIFI_LED)1103led_classdev_resume(&wistron_wifi_led);1104}11051106static void handle_key(u8 code)1107{1108const struct key_entry *key =1109sparse_keymap_entry_from_scancode(wistron_idev->input, code);11101111if (key) {1112switch (key->type) {1113case KE_WIFI:1114if (have_wifi) {1115wifi_enabled = !wifi_enabled;1116bios_set_state(WIFI, wifi_enabled);1117}1118break;11191120case KE_BLUETOOTH:1121if (have_bluetooth) {1122bluetooth_enabled = !bluetooth_enabled;1123bios_set_state(BLUETOOTH, bluetooth_enabled);1124}1125break;11261127default:1128sparse_keymap_report_entry(wistron_idev->input,1129key, 1, true);1130break;1131}1132jiffies_last_press = jiffies;1133} else1134printk(KERN_NOTICE1135"wistron_btns: Unknown key code %02X\n", code);1136}11371138static void poll_bios(bool discard)1139{1140u8 qlen;1141u16 val;11421143for (;;) {1144qlen = CMOS_READ(cmos_address);1145if (qlen == 0)1146break;1147val = bios_pop_queue();1148if (val != 0 && !discard)1149handle_key((u8)val);1150}1151}11521153static void wistron_flush(struct input_polled_dev *dev)1154{1155/* Flush stale event queue */1156poll_bios(true);1157}11581159static void wistron_poll(struct input_polled_dev *dev)1160{1161poll_bios(false);11621163/* Increase poll frequency if user is currently pressing keys (< 2s ago) */1164if (time_before(jiffies, jiffies_last_press + 2 * HZ))1165dev->poll_interval = POLL_INTERVAL_BURST;1166else1167dev->poll_interval = POLL_INTERVAL_DEFAULT;1168}11691170static int __devinit wistron_setup_keymap(struct input_dev *dev,1171struct key_entry *entry)1172{1173switch (entry->type) {11741175/* if wifi or bluetooth are not available, create normal keys */1176case KE_WIFI:1177if (!have_wifi) {1178entry->type = KE_KEY;1179entry->keycode = KEY_WLAN;1180}1181break;11821183case KE_BLUETOOTH:1184if (!have_bluetooth) {1185entry->type = KE_KEY;1186entry->keycode = KEY_BLUETOOTH;1187}1188break;11891190case KE_END:1191if (entry->code & FE_UNTESTED)1192printk(KERN_WARNING "Untested laptop multimedia keys, "1193"please report success or failure to "1194"[email protected]\n");1195break;1196}11971198return 0;1199}12001201static int __devinit setup_input_dev(void)1202{1203struct input_dev *input_dev;1204int error;12051206wistron_idev = input_allocate_polled_device();1207if (!wistron_idev)1208return -ENOMEM;12091210wistron_idev->open = wistron_flush;1211wistron_idev->poll = wistron_poll;1212wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;12131214input_dev = wistron_idev->input;1215input_dev->name = "Wistron laptop buttons";1216input_dev->phys = "wistron/input0";1217input_dev->id.bustype = BUS_HOST;1218input_dev->dev.parent = &wistron_device->dev;12191220error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);1221if (error)1222goto err_free_dev;12231224error = input_register_polled_device(wistron_idev);1225if (error)1226goto err_free_keymap;12271228return 0;12291230err_free_keymap:1231sparse_keymap_free(input_dev);1232err_free_dev:1233input_free_polled_device(wistron_idev);1234return error;1235}12361237/* Driver core */12381239static int __devinit wistron_probe(struct platform_device *dev)1240{1241int err;12421243bios_attach();1244cmos_address = bios_get_cmos_address();12451246if (have_wifi) {1247u16 wifi = bios_get_default_setting(WIFI);1248if (wifi & 1)1249wifi_enabled = wifi & 2;1250else1251have_wifi = 0;12521253if (have_wifi)1254bios_set_state(WIFI, wifi_enabled);1255}12561257if (have_bluetooth) {1258u16 bt = bios_get_default_setting(BLUETOOTH);1259if (bt & 1)1260bluetooth_enabled = bt & 2;1261else1262have_bluetooth = false;12631264if (have_bluetooth)1265bios_set_state(BLUETOOTH, bluetooth_enabled);1266}12671268wistron_led_init(&dev->dev);12691270err = setup_input_dev();1271if (err) {1272bios_detach();1273return err;1274}12751276return 0;1277}12781279static int __devexit wistron_remove(struct platform_device *dev)1280{1281wistron_led_remove();1282input_unregister_polled_device(wistron_idev);1283sparse_keymap_free(wistron_idev->input);1284input_free_polled_device(wistron_idev);1285bios_detach();12861287return 0;1288}12891290#ifdef CONFIG_PM1291static int wistron_suspend(struct device *dev)1292{1293if (have_wifi)1294bios_set_state(WIFI, 0);12951296if (have_bluetooth)1297bios_set_state(BLUETOOTH, 0);12981299wistron_led_suspend();13001301return 0;1302}13031304static int wistron_resume(struct device *dev)1305{1306if (have_wifi)1307bios_set_state(WIFI, wifi_enabled);13081309if (have_bluetooth)1310bios_set_state(BLUETOOTH, bluetooth_enabled);13111312wistron_led_resume();13131314poll_bios(true);13151316return 0;1317}13181319static const struct dev_pm_ops wistron_pm_ops = {1320.suspend = wistron_suspend,1321.resume = wistron_resume,1322.poweroff = wistron_suspend,1323.restore = wistron_resume,1324};1325#endif13261327static struct platform_driver wistron_driver = {1328.driver = {1329.name = "wistron-bios",1330.owner = THIS_MODULE,1331#ifdef CONFIG_PM1332.pm = &wistron_pm_ops,1333#endif1334},1335.probe = wistron_probe,1336.remove = __devexit_p(wistron_remove),1337};13381339static int __init wb_module_init(void)1340{1341int err;13421343err = select_keymap();1344if (err)1345return err;13461347err = map_bios();1348if (err)1349goto err_free_keymap;13501351err = platform_driver_register(&wistron_driver);1352if (err)1353goto err_unmap_bios;13541355wistron_device = platform_device_alloc("wistron-bios", -1);1356if (!wistron_device) {1357err = -ENOMEM;1358goto err_unregister_driver;1359}13601361err = platform_device_add(wistron_device);1362if (err)1363goto err_free_device;13641365return 0;13661367err_free_device:1368platform_device_put(wistron_device);1369err_unregister_driver:1370platform_driver_unregister(&wistron_driver);1371err_unmap_bios:1372unmap_bios();1373err_free_keymap:1374kfree(keymap);13751376return err;1377}13781379static void __exit wb_module_exit(void)1380{1381platform_device_unregister(wistron_device);1382platform_driver_unregister(&wistron_driver);1383unmap_bios();1384kfree(keymap);1385}13861387module_init(wb_module_init);1388module_exit(wb_module_exit);138913901391