2005-11-20 08:50:06 +03:00
/*
* Wistron laptop button driver
* Copyright ( C ) 2005 Miloslav Trmac < mitr @ volny . cz >
2005-11-20 08:50:37 +03:00
* Copyright ( C ) 2005 Bernhard Rosenkraenzer < bero @ arklinux . org >
2005-11-20 08:50:58 +03:00
* Copyright ( C ) 2005 Dmitry Torokhov < dtor @ mail . ru >
2005-11-20 08:50:06 +03:00
*
* You can redistribute and / or modify this program under the terms of the
* GNU General Public License version 2 as published by the Free Software
* Foundation .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU General
* Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
2006-10-11 12:22:01 +04:00
# include <linux/io.h>
2005-11-20 08:50:06 +03:00
# include <linux/dmi.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/mc146818rtc.h>
# include <linux/module.h>
# include <linux/preempt.h>
# include <linux/string.h>
# include <linux/timer.h>
# include <linux/types.h>
2005-11-20 08:50:58 +03:00
# include <linux/platform_device.h>
2005-11-20 08:50:06 +03:00
/*
* Number of attempts to read data from queue per poll ;
* the queue can hold up to 31 entries
*/
# define MAX_POLL_ITERATIONS 64
# define POLL_FREQUENCY 10 /* Number of polls per second */
# if POLL_FREQUENCY > HZ
# error "POLL_FREQUENCY too high"
# endif
2005-11-20 08:50:37 +03:00
/* BIOS subsystem IDs */
# define WIFI 0x35
# define BLUETOOTH 0x34
2005-11-20 08:50:06 +03:00
MODULE_AUTHOR ( " Miloslav Trmac <mitr@volny.cz> " ) ;
MODULE_DESCRIPTION ( " Wistron laptop button driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_VERSION ( " 0.1 " ) ;
static int force ; /* = 0; */
module_param ( force , bool , 0 ) ;
MODULE_PARM_DESC ( force , " Load even if computer is not in database " ) ;
static char * keymap_name ; /* = NULL; */
module_param_named ( keymap , keymap_name , charp , 0 ) ;
MODULE_PARM_DESC ( keymap , " Keymap name, if it can't be autodetected " ) ;
2005-11-20 08:50:58 +03:00
static struct platform_device * wistron_device ;
2005-11-20 08:50:06 +03:00
/* BIOS interface implementation */
static void __iomem * bios_entry_point ; /* BIOS routine entry point */
static void __iomem * bios_code_map_base ;
static void __iomem * bios_data_map_base ;
static u8 cmos_address ;
struct regs {
u32 eax , ebx , ecx ;
} ;
static void call_bios ( struct regs * regs )
{
unsigned long flags ;
preempt_disable ( ) ;
local_irq_save ( flags ) ;
asm volatile ( " pushl %%ebp; "
" movl %7, %%ebp; "
" call *%6; "
" popl %%ebp "
: " =a " ( regs - > eax ) , " =b " ( regs - > ebx ) , " =c " ( regs - > ecx )
: " 0 " ( regs - > eax ) , " 1 " ( regs - > ebx ) , " 2 " ( regs - > ecx ) ,
" m " ( bios_entry_point ) , " m " ( bios_data_map_base )
: " edx " , " edi " , " esi " , " memory " ) ;
local_irq_restore ( flags ) ;
preempt_enable ( ) ;
}
2006-01-10 09:59:07 +03:00
static ssize_t __init locate_wistron_bios ( void __iomem * base )
2005-11-20 08:50:06 +03:00
{
2006-07-06 08:23:38 +04:00
static unsigned char __initdata signature [ ] =
2005-11-20 08:50:06 +03:00
{ 0x42 , 0x21 , 0x55 , 0x30 } ;
2006-01-10 09:59:07 +03:00
ssize_t offset ;
2005-11-20 08:50:06 +03:00
for ( offset = 0 ; offset < 0x10000 ; offset + = 0x10 ) {
if ( check_signature ( base + offset , signature ,
sizeof ( signature ) ) ! = 0 )
return offset ;
}
return - 1 ;
}
static int __init map_bios ( void )
{
void __iomem * base ;
2006-01-10 09:59:07 +03:00
ssize_t offset ;
2005-11-20 08:50:06 +03:00
u32 entry_point ;
base = ioremap ( 0xF0000 , 0x10000 ) ; /* Can't fail */
offset = locate_wistron_bios ( base ) ;
if ( offset < 0 ) {
printk ( KERN_ERR " wistron_btns: BIOS entry point not found \n " ) ;
iounmap ( base ) ;
return - ENODEV ;
}
entry_point = readl ( base + offset + 5 ) ;
printk ( KERN_DEBUG
" wistron_btns: BIOS signature found at %p, entry point %08X \n " ,
base + offset , entry_point ) ;
if ( entry_point > = 0xF0000 ) {
bios_code_map_base = base ;
bios_entry_point = bios_code_map_base + ( entry_point & 0xFFFF ) ;
} else {
iounmap ( base ) ;
bios_code_map_base = ioremap ( entry_point & ~ 0x3FFF , 0x4000 ) ;
if ( bios_code_map_base = = NULL ) {
printk ( KERN_ERR
" wistron_btns: Can't map BIOS code at %08X \n " ,
entry_point & ~ 0x3FFF ) ;
goto err ;
}
bios_entry_point = bios_code_map_base + ( entry_point & 0x3FFF ) ;
}
/* The Windows driver maps 0x10000 bytes, we keep only one page... */
bios_data_map_base = ioremap ( 0x400 , 0xc00 ) ;
if ( bios_data_map_base = = NULL ) {
printk ( KERN_ERR " wistron_btns: Can't map BIOS data \n " ) ;
goto err_code ;
}
return 0 ;
err_code :
iounmap ( bios_code_map_base ) ;
err :
return - ENOMEM ;
}
2005-11-20 08:50:46 +03:00
static inline void unmap_bios ( void )
2005-11-20 08:50:06 +03:00
{
iounmap ( bios_code_map_base ) ;
iounmap ( bios_data_map_base ) ;
}
/* BIOS calls */
static u16 bios_pop_queue ( void )
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
regs . ebx = 0x061C ;
regs . ecx = 0x0000 ;
call_bios ( & regs ) ;
return regs . eax ;
}
2005-12-28 09:26:24 +03:00
static void __devinit bios_attach ( void )
2005-11-20 08:50:06 +03:00
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
regs . ebx = 0x012E ;
call_bios ( & regs ) ;
}
2005-11-20 08:50:46 +03:00
static void bios_detach ( void )
2005-11-20 08:50:06 +03:00
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
regs . ebx = 0x002E ;
call_bios ( & regs ) ;
}
2005-12-28 09:26:24 +03:00
static u8 __devinit bios_get_cmos_address ( void )
2005-11-20 08:50:06 +03:00
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
regs . ebx = 0x051C ;
call_bios ( & regs ) ;
return regs . ecx ;
}
2005-12-28 09:26:24 +03:00
static u16 __devinit bios_get_default_setting ( u8 subsys )
2005-11-20 08:50:06 +03:00
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
2005-11-20 08:50:37 +03:00
regs . ebx = 0x0200 | subsys ;
2005-11-20 08:50:06 +03:00
call_bios ( & regs ) ;
return regs . eax ;
}
2005-11-20 08:50:37 +03:00
static void bios_set_state ( u8 subsys , int enable )
2005-11-20 08:50:06 +03:00
{
struct regs regs ;
memset ( & regs , 0 , sizeof ( regs ) ) ;
regs . eax = 0x9610 ;
2005-11-20 08:50:37 +03:00
regs . ebx = ( enable ? 0x0100 : 0x0000 ) | subsys ;
2005-11-20 08:50:06 +03:00
call_bios ( & regs ) ;
}
2005-11-20 08:50:37 +03:00
/* Hardware database */
2005-11-20 08:50:06 +03:00
struct key_entry {
char type ; /* See KE_* below */
u8 code ;
unsigned keycode ; /* For KE_KEY */
} ;
2005-11-20 08:50:37 +03:00
enum { KE_END , KE_KEY , KE_WIFI , KE_BLUETOOTH } ;
2005-11-20 08:50:06 +03:00
static const struct key_entry * keymap ; /* = NULL; Current key map */
static int have_wifi ;
2005-11-20 08:50:37 +03:00
static int have_bluetooth ;
2005-11-20 08:50:06 +03:00
static int __init dmi_matched ( struct dmi_system_id * dmi )
{
const struct key_entry * key ;
keymap = dmi - > driver_data ;
for ( key = keymap ; key - > type ! = KE_END ; key + + ) {
2006-10-02 05:58:51 +04:00
if ( key - > type = = KE_WIFI )
2005-11-20 08:50:06 +03:00
have_wifi = 1 ;
2006-10-02 05:58:51 +04:00
else if ( key - > type = = KE_BLUETOOTH )
2005-11-20 08:50:37 +03:00
have_bluetooth = 1 ;
2005-11-20 08:50:06 +03:00
}
return 1 ;
}
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_empty [ ] = {
2005-11-20 08:50:06 +03:00
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_fs_amilo_pro_v2000 [ ] = {
2005-11-20 08:50:06 +03:00
{ KE_KEY , 0x01 , KEY_HELP } ,
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_WIFI , 0x30 , 0 } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_fujitsu_n3510 [ ] = {
2006-04-05 08:40:01 +04:00
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x71 , KEY_STOPCD } ,
{ KE_KEY , 0x72 , KEY_PLAYPAUSE } ,
{ KE_KEY , 0x74 , KEY_REWIND } ,
{ KE_KEY , 0x78 , KEY_FORWARD } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_wistron_ms2111 [ ] = {
2006-06-27 09:48:24 +04:00
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_KEY , 0x13 , KEY_PROG3 } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_wistron_ms2141 [ ] = {
2005-11-20 08:50:06 +03:00
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_WIFI , 0x30 , 0 } ,
{ KE_KEY , 0x22 , KEY_REWIND } ,
{ KE_KEY , 0x23 , KEY_FORWARD } ,
{ KE_KEY , 0x24 , KEY_PLAYPAUSE } ,
{ KE_KEY , 0x25 , KEY_STOPCD } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_acer_aspire_1500 [ ] = {
2005-11-20 08:50:37 +03:00
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_WIFI , 0x30 , 0 } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_BLUETOOTH , 0x44 , 0 } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_acer_travelmate_240 [ ] = {
2005-12-11 20:41:32 +03:00
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_BLUETOOTH , 0x44 , 0 } ,
{ KE_WIFI , 0x30 , 0 } ,
{ KE_END , 0 }
} ;
2006-08-23 08:47:39 +04:00
static struct key_entry keymap_aopen_1559as [ ] = {
2006-05-30 07:29:36 +04:00
{ KE_KEY , 0x01 , KEY_HELP } ,
{ KE_KEY , 0x06 , KEY_PROG3 } ,
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_WIFI , 0x30 , 0 } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
2006-06-27 09:48:24 +04:00
{ KE_END , 0 } ,
2006-05-30 07:29:36 +04:00
} ;
2007-02-10 09:29:42 +03:00
static struct key_entry keymap_fs_amilo_d88x0 [ ] = {
{ KE_KEY , 0x01 , KEY_HELP } ,
{ KE_KEY , 0x08 , KEY_MUTE } ,
{ KE_KEY , 0x31 , KEY_MAIL } ,
{ KE_KEY , 0x36 , KEY_WWW } ,
{ KE_KEY , 0x11 , KEY_PROG1 } ,
{ KE_KEY , 0x12 , KEY_PROG2 } ,
{ KE_KEY , 0x13 , KEY_PROG3 } ,
{ KE_END , 0 }
} ;
2005-11-20 08:50:06 +03:00
/*
* If your machine is not here ( which is currently rather likely ) , please send
* a list of buttons and their key codes ( reported when loading this module
* with force = 1 ) and the output of dmidecode to $ MODULE_AUTHOR .
*/
2006-07-06 08:23:38 +04:00
static struct dmi_system_id dmi_ids [ ] __initdata = {
2005-11-20 08:50:06 +03:00
{
. callback = dmi_matched ,
. ident = " Fujitsu-Siemens Amilo Pro V2000 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU SIEMENS " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " AMILO Pro V2000 " ) ,
} ,
. driver_data = keymap_fs_amilo_pro_v2000
} ,
2006-04-05 08:39:20 +04:00
{
. callback = dmi_matched ,
. ident = " Fujitsu-Siemens Amilo M7400 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU SIEMENS " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " AMILO M " ) ,
} ,
. driver_data = keymap_fs_amilo_pro_v2000
} ,
2006-04-05 08:40:01 +04:00
{
. callback = dmi_matched ,
. ident = " Fujitsu N3510 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " N3510 " ) ,
} ,
. driver_data = keymap_fujitsu_n3510
} ,
2005-11-20 08:50:37 +03:00
{
. callback = dmi_matched ,
. ident = " Acer Aspire 1500 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Aspire 1500 " ) ,
} ,
. driver_data = keymap_acer_aspire_1500
} ,
2005-12-11 20:41:32 +03:00
{
. callback = dmi_matched ,
. ident = " Acer TravelMate 240 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " TravelMate 240 " ) ,
} ,
. driver_data = keymap_acer_travelmate_240
} ,
2006-10-02 06:07:14 +04:00
{
. callback = dmi_matched ,
. ident = " Acer TravelMate 2424NWXCi " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " TravelMate 2420 " ) ,
} ,
. driver_data = keymap_acer_travelmate_240
} ,
{
2006-05-30 07:29:36 +04:00
. callback = dmi_matched ,
. ident = " AOpen 1559AS " ,
. matches = {
DMI_MATCH ( DMI_PRODUCT_NAME , " E2U " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " E2U " ) ,
} ,
. driver_data = keymap_aopen_1559as
} ,
2006-06-27 09:48:24 +04:00
{
. callback = dmi_matched ,
. ident = " Medion MD 9783 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " MEDIONNB " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " MD 9783 " ) ,
} ,
. driver_data = keymap_wistron_ms2111
} ,
2007-02-10 09:29:42 +03:00
{
. callback = dmi_matched ,
. ident = " Fujitsu Siemens Amilo D88x0 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " FUJITSU SIEMENS " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " AMILO D " ) ,
} ,
. driver_data = keymap_fs_amilo_d88x0
} ,
2005-12-15 12:19:05 +03:00
{ NULL , }
2005-11-20 08:50:06 +03:00
} ;
static int __init select_keymap ( void )
{
if ( keymap_name ! = NULL ) {
if ( strcmp ( keymap_name , " 1557/MS2141 " ) = = 0 )
keymap = keymap_wistron_ms2141 ;
else {
printk ( KERN_ERR " wistron_btns: Keymap unknown \n " ) ;
return - EINVAL ;
}
}
dmi_check_system ( dmi_ids ) ;
if ( keymap = = NULL ) {
if ( ! force ) {
printk ( KERN_ERR " wistron_btns: System unknown \n " ) ;
return - ENODEV ;
}
keymap = keymap_empty ;
}
return 0 ;
}
/* Input layer interface */
2005-11-20 08:50:46 +03:00
static struct input_dev * input_dev ;
2005-11-20 08:50:06 +03:00
2005-12-28 09:26:24 +03:00
static int __devinit setup_input_dev ( void )
2005-11-20 08:50:06 +03:00
{
const struct key_entry * key ;
2005-11-20 08:50:46 +03:00
int error ;
input_dev = input_allocate_device ( ) ;
if ( ! input_dev )
return - ENOMEM ;
input_dev - > name = " Wistron laptop buttons " ;
input_dev - > phys = " wistron/input0 " ;
input_dev - > id . bustype = BUS_HOST ;
2005-11-20 08:50:58 +03:00
input_dev - > cdev . dev = & wistron_device - > dev ;
2005-11-20 08:50:06 +03:00
for ( key = keymap ; key - > type ! = KE_END ; key + + ) {
if ( key - > type = = KE_KEY ) {
2005-11-20 08:50:46 +03:00
input_dev - > evbit [ LONG ( EV_KEY ) ] = BIT ( EV_KEY ) ;
set_bit ( key - > keycode , input_dev - > keybit ) ;
2005-11-20 08:50:06 +03:00
}
}
2005-11-20 08:50:46 +03:00
error = input_register_device ( input_dev ) ;
if ( error ) {
input_free_device ( input_dev ) ;
return error ;
}
return 0 ;
2005-11-20 08:50:06 +03:00
}
static void report_key ( unsigned keycode )
{
2005-11-20 08:50:46 +03:00
input_report_key ( input_dev , keycode , 1 ) ;
input_sync ( input_dev ) ;
input_report_key ( input_dev , keycode , 0 ) ;
input_sync ( input_dev ) ;
2005-11-20 08:50:06 +03:00
}
/* Driver core */
static int wifi_enabled ;
2005-11-20 08:50:37 +03:00
static int bluetooth_enabled ;
2005-11-20 08:50:06 +03:00
static void poll_bios ( unsigned long ) ;
static struct timer_list poll_timer = TIMER_INITIALIZER ( poll_bios , 0 , 0 ) ;
static void handle_key ( u8 code )
{
const struct key_entry * key ;
for ( key = keymap ; key - > type ! = KE_END ; key + + ) {
if ( code = = key - > code ) {
switch ( key - > type ) {
case KE_KEY :
report_key ( key - > keycode ) ;
break ;
case KE_WIFI :
if ( have_wifi ) {
wifi_enabled = ! wifi_enabled ;
2005-11-20 08:50:37 +03:00
bios_set_state ( WIFI , wifi_enabled ) ;
}
break ;
case KE_BLUETOOTH :
if ( have_bluetooth ) {
bluetooth_enabled = ! bluetooth_enabled ;
bios_set_state ( BLUETOOTH , bluetooth_enabled ) ;
2005-11-20 08:50:06 +03:00
}
break ;
case KE_END :
default :
BUG ( ) ;
}
return ;
}
}
printk ( KERN_NOTICE " wistron_btns: Unknown key code %02X \n " , code ) ;
}
static void poll_bios ( unsigned long discard )
{
u8 qlen ;
u16 val ;
for ( ; ; ) {
qlen = CMOS_READ ( cmos_address ) ;
if ( qlen = = 0 )
break ;
val = bios_pop_queue ( ) ;
if ( val ! = 0 & & ! discard )
handle_key ( ( u8 ) val ) ;
}
mod_timer ( & poll_timer , jiffies + HZ / POLL_FREQUENCY ) ;
}
2005-12-28 09:26:24 +03:00
static int __devinit wistron_probe ( struct platform_device * dev )
{
int err = setup_input_dev ( ) ;
if ( err )
return err ;
bios_attach ( ) ;
cmos_address = bios_get_cmos_address ( ) ;
if ( have_wifi ) {
u16 wifi = bios_get_default_setting ( WIFI ) ;
if ( wifi & 1 )
wifi_enabled = ( wifi & 2 ) ? 1 : 0 ;
else
have_wifi = 0 ;
if ( have_wifi )
bios_set_state ( WIFI , wifi_enabled ) ;
}
if ( have_bluetooth ) {
u16 bt = bios_get_default_setting ( BLUETOOTH ) ;
if ( bt & 1 )
bluetooth_enabled = ( bt & 2 ) ? 1 : 0 ;
else
have_bluetooth = 0 ;
if ( have_bluetooth )
bios_set_state ( BLUETOOTH , bluetooth_enabled ) ;
}
poll_bios ( 1 ) ; /* Flush stale event queue and arm timer */
return 0 ;
}
static int __devexit wistron_remove ( struct platform_device * dev )
{
del_timer_sync ( & poll_timer ) ;
input_unregister_device ( input_dev ) ;
bios_detach ( ) ;
return 0 ;
}
# ifdef CONFIG_PM
2005-11-20 08:50:58 +03:00
static int wistron_suspend ( struct platform_device * dev , pm_message_t state )
{
del_timer_sync ( & poll_timer ) ;
2005-11-20 08:51:05 +03:00
if ( have_wifi )
bios_set_state ( WIFI , 0 ) ;
if ( have_bluetooth )
bios_set_state ( BLUETOOTH , 0 ) ;
2005-11-20 08:50:58 +03:00
return 0 ;
}
static int wistron_resume ( struct platform_device * dev )
{
if ( have_wifi )
bios_set_state ( WIFI , wifi_enabled ) ;
if ( have_bluetooth )
bios_set_state ( BLUETOOTH , bluetooth_enabled ) ;
poll_bios ( 1 ) ;
return 0 ;
}
2005-12-28 09:26:24 +03:00
# else
# define wistron_suspend NULL
# define wistron_resume NULL
# endif
2005-11-20 08:50:58 +03:00
static struct platform_driver wistron_driver = {
. driver = {
. name = " wistron-bios " ,
2005-12-28 09:26:24 +03:00
. owner = THIS_MODULE ,
2005-11-20 08:50:58 +03:00
} ,
2005-12-28 09:26:24 +03:00
. probe = wistron_probe ,
. remove = __devexit_p ( wistron_remove ) ,
. suspend = wistron_suspend ,
. resume = wistron_resume ,
2005-11-20 08:50:58 +03:00
} ;
2005-11-20 08:50:06 +03:00
static int __init wb_module_init ( void )
{
int err ;
err = select_keymap ( ) ;
if ( err )
return err ;
2005-11-20 08:50:46 +03:00
2005-11-20 08:50:06 +03:00
err = map_bios ( ) ;
if ( err )
return err ;
2005-11-20 08:50:46 +03:00
2005-11-20 08:50:58 +03:00
err = platform_driver_register ( & wistron_driver ) ;
if ( err )
2005-12-28 09:26:24 +03:00
goto err_unmap_bios ;
2005-11-20 08:50:58 +03:00
2005-12-28 09:26:24 +03:00
wistron_device = platform_device_alloc ( " wistron-bios " , - 1 ) ;
if ( ! wistron_device ) {
err = - ENOMEM ;
2005-11-20 08:50:58 +03:00
goto err_unregister_driver ;
}
2005-12-28 09:26:24 +03:00
err = platform_device_add ( wistron_device ) ;
2005-11-20 08:50:58 +03:00
if ( err )
2005-12-28 09:26:24 +03:00
goto err_free_device ;
2005-11-20 08:50:06 +03:00
return 0 ;
2005-11-20 08:50:58 +03:00
2005-12-28 09:26:24 +03:00
err_free_device :
platform_device_put ( wistron_device ) ;
2005-11-20 08:50:58 +03:00
err_unregister_driver :
platform_driver_unregister ( & wistron_driver ) ;
2005-12-28 09:26:24 +03:00
err_unmap_bios :
2005-11-20 08:50:58 +03:00
unmap_bios ( ) ;
return err ;
2005-11-20 08:50:06 +03:00
}
static void __exit wb_module_exit ( void )
{
2005-11-20 08:50:58 +03:00
platform_device_unregister ( wistron_device ) ;
platform_driver_unregister ( & wistron_driver ) ;
2005-11-20 08:50:06 +03:00
unmap_bios ( ) ;
}
module_init ( wb_module_init ) ;
module_exit ( wb_module_exit ) ;