2005-04-17 02:20:36 +04:00
/*
* toshiba_acpi . c - Toshiba Laptop ACPI Extras
*
*
* Copyright ( C ) 2002 - 2004 John Belmonte
2008-08-30 19:57:39 +04:00
* Copyright ( C ) 2008 Philip Langdale
2005-04-17 02:20:36 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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
*
*
* The devolpment page for this driver is located at
* http : //memebeam.org/toys/ToshibaAcpiDriver.
*
* Credits :
* Jonathan A . Buzzard - Toshiba HCI info , and critical tips on reverse
* engineering the Windows drivers
* Yasushi Nagato - changes for linux kernel 2.4 - > 2.5
* Rob Miller - TV out and hotkeys help
*
*
* TODO
*
*/
2008-08-30 19:57:39 +04:00
# define TOSHIBA_ACPI_VERSION "0.19"
2005-04-17 02:20:36 +04:00
# define PROC_INTERFACE_VERSION 1
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
2006-10-21 01:30:29 +04:00
# include <linux/backlight.h>
2008-08-30 19:57:39 +04:00
# include <linux/platform_device.h>
# include <linux/rfkill.h>
# include <linux/input-polldev.h>
2006-10-21 01:30:29 +04:00
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <acpi/acpi_drivers.h>
MODULE_AUTHOR ( " John Belmonte " ) ;
MODULE_DESCRIPTION ( " Toshiba Laptop ACPI Extras Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define MY_LOGPREFIX "toshiba_acpi: "
# define MY_ERR KERN_ERR MY_LOGPREFIX
# define MY_NOTICE KERN_NOTICE MY_LOGPREFIX
# define MY_INFO KERN_INFO MY_LOGPREFIX
/* Toshiba ACPI method paths */
# define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
# define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
# define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
# define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
/* Toshiba HCI interface definitions
*
* HCI is Toshiba ' s " Hardware Control Interface " which is supposed to
* be uniform across all their models . Ideally we would just call
* dedicated ACPI methods instead of using this primitive interface .
* However the ACPI methods seem to be incomplete in some areas ( for
* example they allow setting , but not reading , the LCD brightness value ) ,
* so this is still useful .
*/
# define HCI_WORDS 6
/* operations */
# define HCI_SET 0xff00
# define HCI_GET 0xfe00
/* return codes */
# define HCI_SUCCESS 0x0000
# define HCI_FAILURE 0x1000
# define HCI_NOT_SUPPORTED 0x8000
# define HCI_EMPTY 0x8c00
/* registers */
# define HCI_FAN 0x0004
# define HCI_SYSTEM_EVENT 0x0016
# define HCI_VIDEO_OUT 0x001c
# define HCI_HOTKEY_EVENT 0x001e
# define HCI_LCD_BRIGHTNESS 0x002a
2008-08-30 19:57:39 +04:00
# define HCI_WIRELESS 0x0056
2005-04-17 02:20:36 +04:00
/* field definitions */
# define HCI_LCD_BRIGHTNESS_BITS 3
# define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
# define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
# define HCI_VIDEO_OUT_LCD 0x1
# define HCI_VIDEO_OUT_CRT 0x2
# define HCI_VIDEO_OUT_TV 0x4
2008-08-30 19:57:39 +04:00
# define HCI_WIRELESS_KILL_SWITCH 0x01
# define HCI_WIRELESS_BT_PRESENT 0x0f
# define HCI_WIRELESS_BT_ATTACH 0x40
# define HCI_WIRELESS_BT_POWER 0x80
2005-04-17 02:20:36 +04:00
2008-03-05 02:06:34 +03:00
static const struct acpi_device_id toshiba_device_ids [ ] = {
{ " TOS6200 " , 0 } ,
2008-08-30 19:57:39 +04:00
{ " TOS6208 " , 0 } ,
2008-03-05 02:06:34 +03:00
{ " TOS1900 " , 0 } ,
{ " " , 0 } ,
} ;
MODULE_DEVICE_TABLE ( acpi , toshiba_device_ids ) ;
2005-04-17 02:20:36 +04:00
/* utility
*/
2005-08-05 08:44:28 +04:00
static __inline__ void _set_bit ( u32 * word , u32 mask , int value )
2005-04-17 02:20:36 +04:00
{
* word = ( * word & ~ mask ) | ( mask * value ) ;
}
/* acpi interface wrappers
*/
2005-08-05 08:44:28 +04:00
static int is_valid_acpi_path ( const char * methodName )
2005-04-17 02:20:36 +04:00
{
acpi_handle handle ;
acpi_status status ;
2005-08-05 08:44:28 +04:00
status = acpi_get_handle ( NULL , ( char * ) methodName , & handle ) ;
2005-04-17 02:20:36 +04:00
return ! ACPI_FAILURE ( status ) ;
}
2005-08-05 08:44:28 +04:00
static int write_acpi_int ( const char * methodName , int val )
2005-04-17 02:20:36 +04:00
{
struct acpi_object_list params ;
union acpi_object in_objs [ 1 ] ;
acpi_status status ;
2007-02-07 03:14:43 +03:00
params . count = ARRAY_SIZE ( in_objs ) ;
2005-04-17 02:20:36 +04:00
params . pointer = in_objs ;
in_objs [ 0 ] . type = ACPI_TYPE_INTEGER ;
in_objs [ 0 ] . integer . value = val ;
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_object ( NULL , ( char * ) methodName , & params , NULL ) ;
2005-04-17 02:20:36 +04:00
return ( status = = AE_OK ) ;
}
#if 0
2005-08-05 08:44:28 +04:00
static int read_acpi_int ( const char * methodName , int * pVal )
2005-04-17 02:20:36 +04:00
{
struct acpi_buffer results ;
union acpi_object out_objs [ 1 ] ;
acpi_status status ;
results . length = sizeof ( out_objs ) ;
results . pointer = out_objs ;
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_object ( 0 , ( char * ) methodName , 0 , & results ) ;
2005-04-17 02:20:36 +04:00
* pVal = out_objs [ 0 ] . integer . value ;
return ( status = = AE_OK ) & & ( out_objs [ 0 ] . type = = ACPI_TYPE_INTEGER ) ;
}
# endif
2005-08-05 08:44:28 +04:00
static const char * method_hci /*= 0*/ ;
2005-04-17 02:20:36 +04:00
/* Perform a raw HCI call. Here we don't care about input or output buffer
* format .
*/
2005-08-05 08:44:28 +04:00
static acpi_status hci_raw ( const u32 in [ HCI_WORDS ] , u32 out [ HCI_WORDS ] )
2005-04-17 02:20:36 +04:00
{
struct acpi_object_list params ;
union acpi_object in_objs [ HCI_WORDS ] ;
struct acpi_buffer results ;
2005-08-05 08:44:28 +04:00
union acpi_object out_objs [ HCI_WORDS + 1 ] ;
2005-04-17 02:20:36 +04:00
acpi_status status ;
int i ;
params . count = HCI_WORDS ;
params . pointer = in_objs ;
for ( i = 0 ; i < HCI_WORDS ; + + i ) {
in_objs [ i ] . type = ACPI_TYPE_INTEGER ;
in_objs [ i ] . integer . value = in [ i ] ;
}
results . length = sizeof ( out_objs ) ;
results . pointer = out_objs ;
2005-08-05 08:44:28 +04:00
status = acpi_evaluate_object ( NULL , ( char * ) method_hci , & params ,
& results ) ;
2005-04-17 02:20:36 +04:00
if ( ( status = = AE_OK ) & & ( out_objs - > package . count < = HCI_WORDS ) ) {
for ( i = 0 ; i < out_objs - > package . count ; + + i ) {
out [ i ] = out_objs - > package . elements [ i ] . integer . value ;
}
}
return status ;
}
2008-08-30 19:57:39 +04:00
/* common hci tasks (get or set one or two value)
2005-04-17 02:20:36 +04:00
*
* In addition to the ACPI status , the HCI system returns a result which
* may be useful ( such as " not supported " ) .
*/
2005-08-05 08:44:28 +04:00
static acpi_status hci_write1 ( u32 reg , u32 in1 , u32 * result )
2005-04-17 02:20:36 +04:00
{
u32 in [ HCI_WORDS ] = { HCI_SET , reg , in1 , 0 , 0 , 0 } ;
u32 out [ HCI_WORDS ] ;
acpi_status status = hci_raw ( in , out ) ;
* result = ( status = = AE_OK ) ? out [ 0 ] : HCI_FAILURE ;
return status ;
}
2005-08-05 08:44:28 +04:00
static acpi_status hci_read1 ( u32 reg , u32 * out1 , u32 * result )
2005-04-17 02:20:36 +04:00
{
u32 in [ HCI_WORDS ] = { HCI_GET , reg , 0 , 0 , 0 , 0 } ;
u32 out [ HCI_WORDS ] ;
acpi_status status = hci_raw ( in , out ) ;
* out1 = out [ 2 ] ;
* result = ( status = = AE_OK ) ? out [ 0 ] : HCI_FAILURE ;
return status ;
}
2008-08-30 19:57:39 +04:00
static acpi_status hci_write2 ( u32 reg , u32 in1 , u32 in2 , u32 * result )
{
u32 in [ HCI_WORDS ] = { HCI_SET , reg , in1 , in2 , 0 , 0 } ;
u32 out [ HCI_WORDS ] ;
acpi_status status = hci_raw ( in , out ) ;
* result = ( status = = AE_OK ) ? out [ 0 ] : HCI_FAILURE ;
return status ;
}
static acpi_status hci_read2 ( u32 reg , u32 * out1 , u32 * out2 , u32 * result )
{
u32 in [ HCI_WORDS ] = { HCI_GET , reg , * out1 , * out2 , 0 , 0 } ;
u32 out [ HCI_WORDS ] ;
acpi_status status = hci_raw ( in , out ) ;
* out1 = out [ 2 ] ;
* out2 = out [ 3 ] ;
* result = ( status = = AE_OK ) ? out [ 0 ] : HCI_FAILURE ;
return status ;
}
struct toshiba_acpi_dev {
struct platform_device * p_dev ;
struct rfkill * rfk_dev ;
struct input_polled_dev * poll_dev ;
const char * bt_name ;
const char * rfk_name ;
bool last_rfk_state ;
struct mutex mutex ;
} ;
static struct toshiba_acpi_dev toshiba_acpi = {
. bt_name = " Toshiba Bluetooth " ,
. rfk_name = " Toshiba RFKill Switch " ,
. last_rfk_state = false ,
} ;
/* Bluetooth rfkill handlers */
static u32 hci_get_bt_present ( bool * present )
{
u32 hci_result ;
u32 value , value2 ;
value = 0 ;
value2 = 0 ;
hci_read2 ( HCI_WIRELESS , & value , & value2 , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS )
* present = ( value & HCI_WIRELESS_BT_PRESENT ) ? true : false ;
return hci_result ;
}
static u32 hci_get_bt_on ( bool * on )
{
u32 hci_result ;
u32 value , value2 ;
value = 0 ;
value2 = 0x0001 ;
hci_read2 ( HCI_WIRELESS , & value , & value2 , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS )
* on = ( value & HCI_WIRELESS_BT_POWER ) & &
( value & HCI_WIRELESS_BT_ATTACH ) ;
return hci_result ;
}
static u32 hci_get_radio_state ( bool * radio_state )
{
u32 hci_result ;
u32 value , value2 ;
value = 0 ;
value2 = 0x0001 ;
hci_read2 ( HCI_WIRELESS , & value , & value2 , & hci_result ) ;
* radio_state = value & HCI_WIRELESS_KILL_SWITCH ;
return hci_result ;
}
static int bt_rfkill_toggle_radio ( void * data , enum rfkill_state state )
{
u32 result1 , result2 ;
u32 value ;
bool radio_state ;
struct toshiba_acpi_dev * dev = data ;
value = ( state = = RFKILL_STATE_UNBLOCKED ) ;
if ( hci_get_radio_state ( & radio_state ) ! = HCI_SUCCESS )
return - EFAULT ;
switch ( state ) {
case RFKILL_STATE_UNBLOCKED :
if ( ! radio_state )
return - EPERM ;
break ;
case RFKILL_STATE_SOFT_BLOCKED :
break ;
default :
return - EINVAL ;
}
mutex_lock ( & dev - > mutex ) ;
hci_write2 ( HCI_WIRELESS , value , HCI_WIRELESS_BT_POWER , & result1 ) ;
hci_write2 ( HCI_WIRELESS , value , HCI_WIRELESS_BT_ATTACH , & result2 ) ;
mutex_unlock ( & dev - > mutex ) ;
if ( result1 ! = HCI_SUCCESS | | result2 ! = HCI_SUCCESS )
return - EFAULT ;
return 0 ;
}
static void bt_poll_rfkill ( struct input_polled_dev * poll_dev )
{
bool state_changed ;
bool new_rfk_state ;
bool value ;
u32 hci_result ;
struct toshiba_acpi_dev * dev = poll_dev - > private ;
hci_result = hci_get_radio_state ( & value ) ;
if ( hci_result ! = HCI_SUCCESS )
return ; /* Can't do anything useful */
new_rfk_state = value ;
mutex_lock ( & dev - > mutex ) ;
state_changed = new_rfk_state ! = dev - > last_rfk_state ;
dev - > last_rfk_state = new_rfk_state ;
mutex_unlock ( & dev - > mutex ) ;
if ( unlikely ( state_changed ) ) {
rfkill_force_state ( dev - > rfk_dev ,
new_rfk_state ?
RFKILL_STATE_SOFT_BLOCKED :
RFKILL_STATE_HARD_BLOCKED ) ;
input_report_switch ( poll_dev - > input , SW_RFKILL_ALL ,
new_rfk_state ) ;
}
}
2005-08-05 08:44:28 +04:00
static struct proc_dir_entry * toshiba_proc_dir /*= 0*/ ;
2006-10-21 01:30:29 +04:00
static struct backlight_device * toshiba_backlight_device ;
2005-08-05 08:44:28 +04:00
static int force_fan ;
static int last_key_event ;
static int key_event_valid ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
typedef struct _ProcItem {
const char * name ;
char * ( * read_func ) ( char * ) ;
unsigned long ( * write_func ) ( const char * , unsigned long ) ;
2005-04-17 02:20:36 +04:00
} ProcItem ;
/* proc file handlers
*/
static int
2005-08-05 08:44:28 +04:00
dispatch_read ( char * page , char * * start , off_t off , int count , int * eof ,
ProcItem * item )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
char * p = page ;
2005-04-17 02:20:36 +04:00
int len ;
if ( off = = 0 )
p = item - > read_func ( p ) ;
/* ISSUE: I don't understand this code */
len = ( p - page ) ;
2005-08-05 08:44:28 +04:00
if ( len < = off + count )
* eof = 1 ;
2005-04-17 02:20:36 +04:00
* start = page + off ;
len - = off ;
2005-08-05 08:44:28 +04:00
if ( len > count )
len = count ;
if ( len < 0 )
len = 0 ;
2005-04-17 02:20:36 +04:00
return len ;
}
static int
2005-08-05 08:44:28 +04:00
dispatch_write ( struct file * file , const char __user * buffer ,
unsigned long count , ProcItem * item )
2005-04-17 02:20:36 +04:00
{
int result ;
2005-08-05 08:44:28 +04:00
char * tmp_buffer ;
2005-04-17 02:20:36 +04:00
/* Arg buffer points to userspace memory, which can't be accessed
* directly . Since we ' re making a copy , zero - terminate the
* destination so that sscanf can be used on it safely .
*/
tmp_buffer = kmalloc ( count + 1 , GFP_KERNEL ) ;
2005-08-05 08:44:28 +04:00
if ( ! tmp_buffer )
2005-03-31 07:15:36 +04:00
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( tmp_buffer , buffer , count ) ) {
result = - EFAULT ;
2005-08-05 08:44:28 +04:00
} else {
2005-04-17 02:20:36 +04:00
tmp_buffer [ count ] = 0 ;
result = item - > write_func ( tmp_buffer , count ) ;
}
kfree ( tmp_buffer ) ;
return result ;
}
2006-10-21 01:30:29 +04:00
static int get_lcd ( struct backlight_device * bd )
2005-04-17 02:20:36 +04:00
{
u32 hci_result ;
u32 value ;
hci_read1 ( HCI_LCD_BRIGHTNESS , & value , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS ) {
2006-10-21 01:30:29 +04:00
return ( value > > HCI_LCD_BRIGHTNESS_SHIFT ) ;
} else
return - EFAULT ;
}
static char * read_lcd ( char * p )
{
int value = get_lcd ( NULL ) ;
if ( value > = 0 ) {
2005-04-17 02:20:36 +04:00
p + = sprintf ( p , " brightness: %d \n " , value ) ;
p + = sprintf ( p , " brightness_levels: %d \n " ,
2005-08-05 08:44:28 +04:00
HCI_LCD_BRIGHTNESS_LEVELS ) ;
2005-04-17 02:20:36 +04:00
} else {
printk ( MY_ERR " Error reading LCD brightness \n " ) ;
}
return p ;
}
2006-10-21 01:30:29 +04:00
static int set_lcd ( int value )
{
u32 hci_result ;
value = value < < HCI_LCD_BRIGHTNESS_SHIFT ;
hci_write1 ( HCI_LCD_BRIGHTNESS , value , & hci_result ) ;
if ( hci_result ! = HCI_SUCCESS )
return - EFAULT ;
return 0 ;
}
static int set_lcd_status ( struct backlight_device * bd )
{
2007-02-11 02:07:48 +03:00
return set_lcd ( bd - > props . brightness ) ;
2006-10-21 01:30:29 +04:00
}
2005-08-05 08:44:28 +04:00
static unsigned long write_lcd ( const char * buffer , unsigned long count )
2005-04-17 02:20:36 +04:00
{
int value ;
2007-01-06 03:37:03 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
if ( sscanf ( buffer , " brightness : %i " , & value ) = = 1 & &
2007-01-06 03:37:03 +03:00
value > = 0 & & value < HCI_LCD_BRIGHTNESS_LEVELS ) {
2006-10-21 01:30:29 +04:00
ret = set_lcd ( value ) ;
2007-01-06 03:37:03 +03:00
if ( ret = = 0 )
ret = count ;
} else {
2006-10-21 01:30:29 +04:00
ret = - EINVAL ;
2007-01-06 03:37:03 +03:00
}
2006-10-21 01:30:29 +04:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-08-05 08:44:28 +04:00
static char * read_video ( char * p )
2005-04-17 02:20:36 +04:00
{
u32 hci_result ;
u32 value ;
hci_read1 ( HCI_VIDEO_OUT , & value , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS ) {
int is_lcd = ( value & HCI_VIDEO_OUT_LCD ) ? 1 : 0 ;
int is_crt = ( value & HCI_VIDEO_OUT_CRT ) ? 1 : 0 ;
2005-08-05 08:44:28 +04:00
int is_tv = ( value & HCI_VIDEO_OUT_TV ) ? 1 : 0 ;
2005-04-17 02:20:36 +04:00
p + = sprintf ( p , " lcd_out: %d \n " , is_lcd ) ;
p + = sprintf ( p , " crt_out: %d \n " , is_crt ) ;
p + = sprintf ( p , " tv_out: %d \n " , is_tv ) ;
} else {
printk ( MY_ERR " Error reading video out status \n " ) ;
}
return p ;
}
2005-08-05 08:44:28 +04:00
static unsigned long write_video ( const char * buffer , unsigned long count )
2005-04-17 02:20:36 +04:00
{
int value ;
int remain = count ;
int lcd_out = - 1 ;
int crt_out = - 1 ;
int tv_out = - 1 ;
u32 hci_result ;
2007-10-14 22:35:40 +04:00
u32 video_out ;
2005-04-17 02:20:36 +04:00
/* scan expression. Multiple expressions may be delimited with ;
*
* NOTE : to keep scanning simple , invalid fields are ignored
*/
while ( remain ) {
if ( sscanf ( buffer , " lcd_out : %i " , & value ) = = 1 )
lcd_out = value & 1 ;
else if ( sscanf ( buffer , " crt_out : %i " , & value ) = = 1 )
crt_out = value & 1 ;
else if ( sscanf ( buffer , " tv_out : %i " , & value ) = = 1 )
tv_out = value & 1 ;
/* advance to one character past the next ; */
do {
+ + buffer ;
- - remain ;
}
2005-08-05 08:44:28 +04:00
while ( remain & & * ( buffer - 1 ) ! = ' ; ' ) ;
2005-04-17 02:20:36 +04:00
}
hci_read1 ( HCI_VIDEO_OUT , & video_out , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS ) {
int new_video_out = video_out ;
if ( lcd_out ! = - 1 )
_set_bit ( & new_video_out , HCI_VIDEO_OUT_LCD , lcd_out ) ;
if ( crt_out ! = - 1 )
_set_bit ( & new_video_out , HCI_VIDEO_OUT_CRT , crt_out ) ;
if ( tv_out ! = - 1 )
_set_bit ( & new_video_out , HCI_VIDEO_OUT_TV , tv_out ) ;
/* To avoid unnecessary video disruption, only write the new
* video setting if something changed . */
if ( new_video_out ! = video_out )
write_acpi_int ( METHOD_VIDEO_OUT , new_video_out ) ;
} else {
return - EFAULT ;
}
return count ;
}
2005-08-05 08:44:28 +04:00
static char * read_fan ( char * p )
2005-04-17 02:20:36 +04:00
{
u32 hci_result ;
u32 value ;
hci_read1 ( HCI_FAN , & value , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS ) {
p + = sprintf ( p , " running: %d \n " , ( value > 0 ) ) ;
p + = sprintf ( p , " force_on: %d \n " , force_fan ) ;
} else {
printk ( MY_ERR " Error reading fan status \n " ) ;
}
return p ;
}
2005-08-05 08:44:28 +04:00
static unsigned long write_fan ( const char * buffer , unsigned long count )
2005-04-17 02:20:36 +04:00
{
int value ;
u32 hci_result ;
if ( sscanf ( buffer , " force_on : %i " , & value ) = = 1 & &
2005-08-05 08:44:28 +04:00
value > = 0 & & value < = 1 ) {
2005-04-17 02:20:36 +04:00
hci_write1 ( HCI_FAN , value , & hci_result ) ;
if ( hci_result ! = HCI_SUCCESS )
return - EFAULT ;
else
force_fan = value ;
} else {
return - EINVAL ;
}
return count ;
}
2005-08-05 08:44:28 +04:00
static char * read_keys ( char * p )
2005-04-17 02:20:36 +04:00
{
u32 hci_result ;
u32 value ;
if ( ! key_event_valid ) {
hci_read1 ( HCI_SYSTEM_EVENT , & value , & hci_result ) ;
if ( hci_result = = HCI_SUCCESS ) {
key_event_valid = 1 ;
last_key_event = value ;
} else if ( hci_result = = HCI_EMPTY ) {
/* better luck next time */
} else if ( hci_result = = HCI_NOT_SUPPORTED ) {
/* This is a workaround for an unresolved issue on
* some machines where system events sporadically
* become disabled . */
hci_write1 ( HCI_SYSTEM_EVENT , 1 , & hci_result ) ;
printk ( MY_NOTICE " Re-enabled hotkeys \n " ) ;
} else {
printk ( MY_ERR " Error reading hotkey status \n " ) ;
goto end ;
}
}
p + = sprintf ( p , " hotkey_ready: %d \n " , key_event_valid ) ;
p + = sprintf ( p , " hotkey: 0x%04x \n " , last_key_event ) ;
2005-08-05 08:44:28 +04:00
end :
2005-04-17 02:20:36 +04:00
return p ;
}
2005-08-05 08:44:28 +04:00
static unsigned long write_keys ( const char * buffer , unsigned long count )
2005-04-17 02:20:36 +04:00
{
int value ;
2005-08-05 08:44:28 +04:00
if ( sscanf ( buffer , " hotkey_ready : %i " , & value ) = = 1 & & value = = 0 ) {
2005-04-17 02:20:36 +04:00
key_event_valid = 0 ;
} else {
return - EINVAL ;
}
return count ;
}
2005-08-05 08:44:28 +04:00
static char * read_version ( char * p )
2005-04-17 02:20:36 +04:00
{
p + = sprintf ( p , " driver: %s \n " , TOSHIBA_ACPI_VERSION ) ;
p + = sprintf ( p , " proc_interface: %d \n " ,
2005-08-05 08:44:28 +04:00
PROC_INTERFACE_VERSION ) ;
2005-04-17 02:20:36 +04:00
return p ;
}
/* proc and module init
*/
# define PROC_TOSHIBA "toshiba"
2005-08-05 08:44:28 +04:00
static ProcItem proc_items [ ] = {
{ " lcd " , read_lcd , write_lcd } ,
{ " video " , read_video , write_video } ,
{ " fan " , read_fan , write_fan } ,
{ " keys " , read_keys , write_keys } ,
{ " version " , read_version , NULL } ,
{ NULL }
2005-04-17 02:20:36 +04:00
} ;
2005-08-05 08:44:28 +04:00
static acpi_status __init add_device ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
struct proc_dir_entry * proc ;
ProcItem * item ;
2005-04-17 02:20:36 +04:00
2005-08-05 08:44:28 +04:00
for ( item = proc_items ; item - > name ; + + item ) {
2005-04-17 02:20:36 +04:00
proc = create_proc_read_entry ( item - > name ,
2005-08-05 08:44:28 +04:00
S_IFREG | S_IRUGO | S_IWUSR ,
toshiba_proc_dir ,
( read_proc_t * ) dispatch_read ,
item ) ;
2005-04-17 02:20:36 +04:00
if ( proc )
proc - > owner = THIS_MODULE ;
if ( proc & & item - > write_func )
2005-08-05 08:44:28 +04:00
proc - > write_proc = ( write_proc_t * ) dispatch_write ;
2005-04-17 02:20:36 +04:00
}
return AE_OK ;
}
2007-06-16 21:16:02 +04:00
static acpi_status remove_device ( void )
2005-04-17 02:20:36 +04:00
{
2005-08-05 08:44:28 +04:00
ProcItem * item ;
2005-04-17 02:20:36 +04:00
for ( item = proc_items ; item - > name ; + + item )
remove_proc_entry ( item - > name , toshiba_proc_dir ) ;
return AE_OK ;
}
2007-02-11 02:07:48 +03:00
static struct backlight_ops toshiba_backlight_data = {
2006-10-21 01:30:29 +04:00
. get_brightness = get_lcd ,
. update_status = set_lcd_status ,
} ;
2007-06-01 11:47:12 +04:00
static void toshiba_acpi_exit ( void )
2006-10-21 01:30:29 +04:00
{
2008-08-30 19:57:39 +04:00
if ( toshiba_acpi . poll_dev ) {
input_unregister_polled_device ( toshiba_acpi . poll_dev ) ;
input_free_polled_device ( toshiba_acpi . poll_dev ) ;
}
if ( toshiba_acpi . rfk_dev )
rfkill_unregister ( toshiba_acpi . rfk_dev ) ;
2006-10-21 01:30:29 +04:00
if ( toshiba_backlight_device )
backlight_device_unregister ( toshiba_backlight_device ) ;
remove_device ( ) ;
if ( toshiba_proc_dir )
remove_proc_entry ( PROC_TOSHIBA , acpi_root_dir ) ;
2008-08-30 19:57:39 +04:00
platform_device_unregister ( toshiba_acpi . p_dev ) ;
2006-10-21 01:30:29 +04:00
return ;
}
2005-08-05 08:44:28 +04:00
static int __init toshiba_acpi_init ( void )
2005-04-17 02:20:36 +04:00
{
acpi_status status = AE_OK ;
u32 hci_result ;
2008-08-30 19:57:39 +04:00
bool bt_present ;
bool bt_on ;
bool radio_on ;
int ret = 0 ;
2005-04-17 02:20:36 +04:00
if ( acpi_disabled )
return - ENODEV ;
2005-03-19 02:03:45 +03:00
2005-04-17 02:20:36 +04:00
/* simple device detection: look for HCI method */
if ( is_valid_acpi_path ( METHOD_HCI_1 ) )
method_hci = METHOD_HCI_1 ;
else if ( is_valid_acpi_path ( METHOD_HCI_2 ) )
method_hci = METHOD_HCI_2 ;
else
return - ENODEV ;
printk ( MY_INFO " Toshiba Laptop ACPI Extras version %s \n " ,
2005-08-05 08:44:28 +04:00
TOSHIBA_ACPI_VERSION ) ;
2005-04-17 02:20:36 +04:00
printk ( MY_INFO " HCI method: %s \n " , method_hci ) ;
2008-08-30 19:57:39 +04:00
mutex_init ( & toshiba_acpi . mutex ) ;
toshiba_acpi . p_dev = platform_device_register_simple ( " toshiba_acpi " ,
- 1 , NULL , 0 ) ;
if ( IS_ERR ( toshiba_acpi . p_dev ) ) {
ret = PTR_ERR ( toshiba_acpi . p_dev ) ;
printk ( MY_ERR " unable to register platform device \n " ) ;
toshiba_acpi . p_dev = NULL ;
toshiba_acpi_exit ( ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
force_fan = 0 ;
key_event_valid = 0 ;
/* enable event fifo */
hci_write1 ( HCI_SYSTEM_EVENT , 1 , & hci_result ) ;
toshiba_proc_dir = proc_mkdir ( PROC_TOSHIBA , acpi_root_dir ) ;
if ( ! toshiba_proc_dir ) {
2008-08-30 19:57:39 +04:00
toshiba_acpi_exit ( ) ;
return - ENODEV ;
2005-04-17 02:20:36 +04:00
} else {
toshiba_proc_dir - > owner = THIS_MODULE ;
status = add_device ( ) ;
2008-08-30 19:57:39 +04:00
if ( ACPI_FAILURE ( status ) ) {
toshiba_acpi_exit ( ) ;
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
}
2008-08-30 19:57:39 +04:00
toshiba_backlight_device = backlight_device_register ( " toshiba " ,
& toshiba_acpi . p_dev - > dev ,
2006-12-19 23:56:15 +03:00
NULL ,
2006-10-21 01:30:29 +04:00
& toshiba_backlight_data ) ;
if ( IS_ERR ( toshiba_backlight_device ) ) {
2008-08-30 19:57:39 +04:00
ret = PTR_ERR ( toshiba_backlight_device ) ;
2007-11-15 03:58:28 +03:00
2006-10-21 01:30:29 +04:00
printk ( KERN_ERR " Could not register toshiba backlight device \n " ) ;
toshiba_backlight_device = NULL ;
toshiba_acpi_exit ( ) ;
2007-11-15 03:58:28 +03:00
return ret ;
2006-10-21 01:30:29 +04:00
}
2007-02-11 02:07:48 +03:00
toshiba_backlight_device - > props . max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1 ;
2005-04-17 02:20:36 +04:00
2008-08-30 19:57:39 +04:00
/* Register rfkill switch for Bluetooth */
if ( hci_get_bt_present ( & bt_present ) = = HCI_SUCCESS & & bt_present ) {
toshiba_acpi . rfk_dev = rfkill_allocate ( & toshiba_acpi . p_dev - > dev ,
RFKILL_TYPE_BLUETOOTH ) ;
if ( ! toshiba_acpi . rfk_dev ) {
printk ( MY_ERR " unable to allocate rfkill device \n " ) ;
toshiba_acpi_exit ( ) ;
return - ENOMEM ;
}
toshiba_acpi . rfk_dev - > name = toshiba_acpi . bt_name ;
toshiba_acpi . rfk_dev - > toggle_radio = bt_rfkill_toggle_radio ;
toshiba_acpi . rfk_dev - > user_claim_unsupported = 1 ;
toshiba_acpi . rfk_dev - > data = & toshiba_acpi ;
if ( hci_get_bt_on ( & bt_on ) = = HCI_SUCCESS & & bt_on ) {
toshiba_acpi . rfk_dev - > state = RFKILL_STATE_UNBLOCKED ;
} else if ( hci_get_radio_state ( & radio_on ) = = HCI_SUCCESS & &
radio_on ) {
toshiba_acpi . rfk_dev - > state = RFKILL_STATE_SOFT_BLOCKED ;
} else {
toshiba_acpi . rfk_dev - > state = RFKILL_STATE_HARD_BLOCKED ;
}
ret = rfkill_register ( toshiba_acpi . rfk_dev ) ;
if ( ret ) {
printk ( MY_ERR " unable to register rfkill device \n " ) ;
toshiba_acpi_exit ( ) ;
return - ENOMEM ;
}
}
/* Register input device for kill switch */
toshiba_acpi . poll_dev = input_allocate_polled_device ( ) ;
if ( ! toshiba_acpi . poll_dev ) {
printk ( MY_ERR " unable to allocate kill-switch input device \n " ) ;
toshiba_acpi_exit ( ) ;
return - ENOMEM ;
}
toshiba_acpi . poll_dev - > private = & toshiba_acpi ;
toshiba_acpi . poll_dev - > poll = bt_poll_rfkill ;
toshiba_acpi . poll_dev - > poll_interval = 1000 ; /* msecs */
toshiba_acpi . poll_dev - > input - > name = toshiba_acpi . rfk_name ;
toshiba_acpi . poll_dev - > input - > id . bustype = BUS_HOST ;
toshiba_acpi . poll_dev - > input - > id . vendor = 0x0930 ; /* Toshiba USB ID */
set_bit ( EV_SW , toshiba_acpi . poll_dev - > input - > evbit ) ;
set_bit ( SW_RFKILL_ALL , toshiba_acpi . poll_dev - > input - > swbit ) ;
input_report_switch ( toshiba_acpi . poll_dev - > input , SW_RFKILL_ALL , TRUE ) ;
ret = input_register_polled_device ( toshiba_acpi . poll_dev ) ;
if ( ret ) {
printk ( MY_ERR " unable to register kill-switch input device \n " ) ;
rfkill_free ( toshiba_acpi . rfk_dev ) ;
toshiba_acpi . rfk_dev = NULL ;
toshiba_acpi_exit ( ) ;
return ret ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
module_init ( toshiba_acpi_init ) ;
module_exit ( toshiba_acpi_exit ) ;