2005-04-16 15:20:36 -07:00
/*
* ibm_acpi . c - IBM ThinkPad ACPI Extras
*
*
2005-08-17 00:00:00 -04:00
* Copyright ( C ) 2004 - 2005 Borislav Deianov < borislav @ users . sf . net >
2005-04-16 15:20:36 -07: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
2005-08-17 00:00:00 -04:00
*/
# define IBM_VERSION "0.12a"
/*
2005-04-16 15:20:36 -07:00
* Changelog :
2005-08-17 00:00:00 -04:00
*
* 2005 - 08 - 17 0.12 fix compilation on 2.6 .13 - rc kernels
* 2005 - 03 - 17 0.11 support for 600 e , 770 x
* thanks to Jamie Lentin < lentinj @ dial . pipex . com >
* support for 770 e , G41
* G40 and G41 don ' t have a thinklight
* temperatures no longer experimental
* experimental brightness control
* experimental volume control
* experimental fan enable / disable
* 2005 - 01 - 16 0.10 fix module loading on R30 , R31
* 2005 - 01 - 16 0.9 support for 570 , R30 , R31
* ultrabay support on A22p , A3x
* limit arg for cmos , led , beep , drop experimental status
* more capable led control on A21e , A22p , T20 - 22 , X20
* experimental temperatures and fan speed
* experimental embedded controller register dump
* mark more functions as __init , drop incorrect __exit
* use MODULE_VERSION
* thanks to Henrik Brix Andersen < brix @ gentoo . org >
* fix parameter passing on module loading
* thanks to Rusty Russell < rusty @ rustcorp . com . au >
* thanks to Jim Radford < radford @ blackbean . org >
* 2004 - 11 - 08 0.8 fix init error case , don ' t return from a macro
* thanks to Chris Wright < chrisw @ osdl . org >
* 2004 - 10 - 23 0.7 fix module loading on A21e , A22p , T20 , T21 , X20
* fix led control on A21e
* 2004 - 10 - 19 0.6 use acpi_bus_register_driver ( ) to claim HKEY device
2005-04-16 15:20:36 -07:00
* 2004 - 10 - 18 0.5 thinklight support on A21e , G40 , R32 , T20 , T21 , X20
* proc file format changed
* video_switch command
* experimental cmos control
* experimental led control
* experimental acpi sounds
2005-08-17 00:00:00 -04:00
* 2004 - 09 - 16 0.4 support for module parameters
* hotkey mask can be prefixed by 0 x
* video output switching
* video expansion control
* ultrabay eject support
* removed lcd brightness / on / off control , didn ' t work
* 2004 - 08 - 17 0.3 support for R40
* lcd off , brightness control
* thinklight on / off
* 2004 - 08 - 14 0.2 support for T series , X20
* bluetooth enable / disable
* hotkey events disabled by default
* removed fan control , currently useless
* 2004 - 08 - 09 0.1 initial release , support for X series
2005-04-16 15:20:36 -07:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/proc_fs.h>
# include <asm/uaccess.h>
# include <acpi/acpi_drivers.h>
# include <acpi/acnamesp.h>
# define IBM_NAME "ibm"
# define IBM_DESC "IBM ThinkPad ACPI Extras"
# define IBM_FILE "ibm_acpi"
# define IBM_URL "http: //ibm-acpi.sf.net/"
2005-08-17 00:00:00 -04:00
MODULE_AUTHOR ( " Borislav Deianov " ) ;
MODULE_DESCRIPTION ( IBM_DESC ) ;
MODULE_VERSION ( IBM_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-04-16 15:20:36 -07:00
# define IBM_DIR IBM_NAME
# define IBM_LOG IBM_FILE ": "
# define IBM_ERR KERN_ERR IBM_LOG
# define IBM_NOTICE KERN_NOTICE IBM_LOG
# define IBM_INFO KERN_INFO IBM_LOG
# define IBM_DEBUG KERN_DEBUG IBM_LOG
# define IBM_MAX_ACPI_ARGS 3
# define __unused __attribute__ ((unused))
static int experimental ;
module_param ( experimental , int , 0 ) ;
static acpi_handle root_handle = NULL ;
# define IBM_HANDLE(object, parent, paths...) \
static acpi_handle object # # _handle ; \
static acpi_handle * object # # _parent = & parent # # _handle ; \
2005-08-17 00:00:00 -04:00
static char * object # # _path ; \
2005-04-16 15:20:36 -07:00
static char * object # # _paths [ ] = { paths }
2005-08-17 00:00:00 -04:00
/*
* The following models are supported to various degrees :
*
* 570 , 600 e , 600 x , 770 e , 770 x
* A20m , A21e , A21m , A21p , A22p , A30 , A30p , A31 , A31p
* G40 , G41
* R30 , R31 , R32 , R40 , R40e , R50 , R50e , R50p , R51
* T20 , T21 , T22 , T23 , T30 , T40 , T40p , T41 , T41p , T42 , T42p , T43
* X20 , X21 , X22 , X23 , X24 , X30 , X31 , X40
*
* The following models have no supported features :
*
* 240 , 240 x , i1400
*
* Still missing DSDTs for the following models :
*
* A20p , A22e , A22m
* R52
* S31
* T43p
*/
IBM_HANDLE ( ec , root , " \\ _SB.PCI0.ISA.EC0 " , /* 240, 240x */
" \\ _SB.PCI.ISA.EC " , /* 570 */
" \\ _SB.PCI0.ISA0.EC0 " , /* 600e/x, 770e, 770x */
" \\ _SB.PCI0.ISA.EC " , /* A21e, A2xm/p, T20-22, X20-21 */
" \\ _SB.PCI0.AD4S.EC0 " , /* i1400, R30 */
" \\ _SB.PCI0.ICH3.EC0 " , /* R31 */
" \\ _SB.PCI0.LPC.EC " , /* all others */
) ;
IBM_HANDLE ( vid , root , " \\ _SB.PCI.AGP.VGA " , /* 570 */
" \\ _SB.PCI0.AGP0.VID0 " , /* 600e/x, 770x */
" \\ _SB.PCI0.VID0 " , /* 770e */
" \\ _SB.PCI0.VID " , /* A21e, G4x, R50e, X30, X40 */
" \\ _SB.PCI0.AGP.VID " , /* all others */
) ; /* R30, R31 */
IBM_HANDLE ( vid2 , root , " \\ _SB.PCI0.AGPB.VID " ) ; /* G41 */
IBM_HANDLE ( cmos , root , " \\ UCMS " , /* R50, R50e, R50p, R51, T4x, X31, X40 */
" \\ CMOS " , /* A3x, G4x, R32, T23, T30, X22-24, X30 */
" \\ CMS " , /* R40, R40e */
) ; /* all others */
2006-02-23 17:56:06 -08:00
# ifdef CONFIG_ACPI_IBM_DOCK
2005-08-17 00:00:00 -04:00
IBM_HANDLE ( dock , root , " \\ _SB.GDCK " , /* X30, X31, X40 */
" \\ _SB.PCI0.DOCK " , /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
" \\ _SB.PCI0.PCI1.DOCK " , /* all others */
" \\ _SB.PCI.ISA.SLCE " , /* 570 */
) ; /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
2006-02-23 17:56:06 -08:00
# endif
2005-08-17 00:00:00 -04:00
IBM_HANDLE ( bay , root , " \\ _SB.PCI.IDE.SECN.MAST " , /* 570 */
" \\ _SB.PCI0.IDE0.IDES.IDSM " , /* 600e/x, 770e, 770x */
" \\ _SB.PCI0.IDE0.SCND.MSTR " , /* all others */
) ; /* A21e, R30, R31 */
IBM_HANDLE ( bay_ej , bay , " _EJ3 " , /* 600e/x, A2xm/p, A3x */
" _EJ0 " , /* all others */
) ; /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
IBM_HANDLE ( bay2 , root , " \\ _SB.PCI0.IDE0.PRIM.SLAV " , /* A3x, R32 */
" \\ _SB.PCI0.IDE0.IDEP.IDPS " , /* 600e/x, 770e, 770x */
) ; /* all others */
IBM_HANDLE ( bay2_ej , bay2 , " _EJ3 " , /* 600e/x, 770e, A3x */
" _EJ0 " , /* 770x */
) ; /* all others */
/* don't list other alternatives as we install a notify handler on the 570 */
IBM_HANDLE ( pci , root , " \\ _SB.PCI " ) ; /* 570 */
IBM_HANDLE ( hkey , ec , " \\ _SB.HKEY " , /* 600e/x, 770e, 770x */
" ^HKEY " , /* R30, R31 */
" HKEY " , /* all others */
) ; /* 570 */
IBM_HANDLE ( lght , root , " \\ LGHT " ) ; /* A21e, A2xm/p, T20-22, X20-21 */
IBM_HANDLE ( ledb , ec , " LEDB " ) ; /* G4x */
IBM_HANDLE ( led , ec , " SLED " , /* 570 */
" SYSL " , /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
" LED " , /* all others */
) ; /* R30, R31 */
IBM_HANDLE ( beep , ec , " BEEP " ) ; /* all except R30, R31 */
IBM_HANDLE ( ecrd , ec , " ECRD " ) ; /* 570 */
IBM_HANDLE ( ecwr , ec , " ECWR " ) ; /* 570 */
IBM_HANDLE ( fans , ec , " FANS " ) ; /* X31, X40 */
IBM_HANDLE ( gfan , ec , " GFAN " , /* 570 */
" \\ FSPD " , /* 600e/x, 770e, 770x */
) ; /* all others */
IBM_HANDLE ( sfan , ec , " SFAN " , /* 570 */
" JFNS " , /* 770x-JL */
) ; /* all others */
# define IBM_HKEY_HID "IBM0068"
# define IBM_PCI_HID "PNP0A03"
2005-04-16 15:20:36 -07:00
struct ibm_struct {
char * name ;
2005-08-17 00:00:00 -04:00
char param [ 32 ] ;
2005-04-16 15:20:36 -07:00
char * hid ;
struct acpi_driver * driver ;
2005-08-17 00:00:00 -04:00
int ( * init ) ( void ) ;
int ( * read ) ( char * ) ;
int ( * write ) ( char * ) ;
void ( * exit ) ( void ) ;
void ( * notify ) ( struct ibm_struct * , u32 ) ;
2005-04-16 15:20:36 -07:00
acpi_handle * handle ;
int type ;
struct acpi_device * device ;
int driver_registered ;
int proc_created ;
int init_called ;
int notify_installed ;
int experimental ;
} ;
static struct proc_dir_entry * proc_dir = NULL ;
# define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
# define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
# define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
static int acpi_evalf ( acpi_handle handle ,
void * res , char * method , char * fmt , . . . )
{
char * fmt0 = fmt ;
2005-08-17 00:00:00 -04:00
struct acpi_object_list params ;
union acpi_object in_objs [ IBM_MAX_ACPI_ARGS ] ;
struct acpi_buffer result , * resultp ;
union acpi_object out_obj ;
acpi_status status ;
va_list ap ;
char res_type ;
int success ;
int quiet ;
2005-04-16 15:20:36 -07:00
if ( ! * fmt ) {
printk ( IBM_ERR " acpi_evalf() called with empty format \n " ) ;
return 0 ;
}
if ( * fmt = = ' q ' ) {
quiet = 1 ;
fmt + + ;
} else
quiet = 0 ;
res_type = * ( fmt + + ) ;
params . count = 0 ;
params . pointer = & in_objs [ 0 ] ;
va_start ( ap , fmt ) ;
while ( * fmt ) {
char c = * ( fmt + + ) ;
switch ( c ) {
case ' d ' : /* int */
in_objs [ params . count ] . integer . value = va_arg ( ap , int ) ;
in_objs [ params . count + + ] . type = ACPI_TYPE_INTEGER ;
break ;
2005-08-17 00:00:00 -04:00
/* add more types as needed */
2005-04-16 15:20:36 -07:00
default :
printk ( IBM_ERR " acpi_evalf() called "
" with invalid format character '%c' \n " , c ) ;
return 0 ;
}
}
va_end ( ap ) ;
2005-08-17 00:00:00 -04:00
if ( res_type ! = ' v ' ) {
result . length = sizeof ( out_obj ) ;
result . pointer = & out_obj ;
resultp = & result ;
} else
resultp = NULL ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
status = acpi_evaluate_object ( handle , method , & params , resultp ) ;
2005-04-16 15:20:36 -07:00
switch ( res_type ) {
2005-08-17 00:00:00 -04:00
case ' d ' : /* int */
2005-04-16 15:20:36 -07:00
if ( res )
* ( int * ) res = out_obj . integer . value ;
success = status = = AE_OK & & out_obj . type = = ACPI_TYPE_INTEGER ;
break ;
2005-08-17 00:00:00 -04:00
case ' v ' : /* void */
2005-04-16 15:20:36 -07:00
success = status = = AE_OK ;
break ;
2005-08-17 00:00:00 -04:00
/* add more types as needed */
2005-04-16 15:20:36 -07:00
default :
printk ( IBM_ERR " acpi_evalf() called "
" with invalid format character '%c' \n " , res_type ) ;
return 0 ;
}
if ( ! success & & ! quiet )
printk ( IBM_ERR " acpi_evalf(%s, %s, ...) failed: %d \n " ,
method , fmt0 , status ) ;
return success ;
}
static void __unused acpi_print_int ( acpi_handle handle , char * method )
{
int i ;
if ( acpi_evalf ( handle , & i , method , " d " ) )
printk ( IBM_INFO " %s = 0x%x \n " , method , i ) ;
else
printk ( IBM_ERR " error calling %s \n " , method ) ;
}
static char * next_cmd ( char * * cmds )
{
char * start = * cmds ;
char * end ;
while ( ( end = strchr ( start , ' , ' ) ) & & end = = start )
start = end + 1 ;
if ( ! end )
return NULL ;
* end = 0 ;
* cmds = end + 1 ;
return start ;
}
2005-08-17 00:00:00 -04:00
static int driver_init ( void )
2005-04-16 15:20:36 -07:00
{
printk ( IBM_INFO " %s v%s \n " , IBM_DESC , IBM_VERSION ) ;
printk ( IBM_INFO " %s \n " , IBM_URL ) ;
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int driver_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
len + = sprintf ( p + len , " driver: \t \t %s \n " , IBM_DESC ) ;
len + = sprintf ( p + len , " version: \t %s \n " , IBM_VERSION ) ;
return len ;
}
2005-08-17 00:00:00 -04:00
static int hotkey_supported ;
static int hotkey_mask_supported ;
static int hotkey_orig_status ;
static int hotkey_orig_mask ;
static int hotkey_get ( int * status , int * mask )
2005-04-16 15:20:36 -07:00
{
if ( ! acpi_evalf ( hkey_handle , status , " DHKC " , " d " ) )
2005-08-17 00:00:00 -04:00
return 0 ;
if ( hotkey_mask_supported )
if ( ! acpi_evalf ( hkey_handle , mask , " DHKN " , " d " ) )
return 0 ;
return 1 ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
static int hotkey_set ( int status , int mask )
2005-04-16 15:20:36 -07:00
{
int i ;
if ( ! acpi_evalf ( hkey_handle , NULL , " MHKC " , " vd " , status ) )
return 0 ;
2005-08-17 00:00:00 -04:00
if ( hotkey_mask_supported )
for ( i = 0 ; i < 32 ; i + + ) {
int bit = ( ( 1 < < i ) & mask ) ! = 0 ;
if ( ! acpi_evalf ( hkey_handle ,
NULL , " MHKM " , " vdd " , i + 1 , bit ) )
return 0 ;
}
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
return 1 ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
static int hotkey_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
/* hotkey not supported on 570 */
hotkey_supported = hkey_handle ! = NULL ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
if ( hotkey_supported ) {
/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
A30 , R30 , R31 , T20 - 22 , X20 - 21 , X22 - 24 */
hotkey_mask_supported =
acpi_evalf ( hkey_handle , NULL , " DHKN " , " qv " ) ;
if ( ! hotkey_get ( & hotkey_orig_status , & hotkey_orig_mask ) )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
return 0 ;
}
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
static int hotkey_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int status , mask ;
int len = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! hotkey_supported ) {
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
return len ;
}
if ( ! hotkey_get ( & status , & mask ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
len + = sprintf ( p + len , " status: \t \t %s \n " , enabled ( status , 0 ) ) ;
2005-08-17 00:00:00 -04:00
if ( hotkey_mask_supported ) {
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " mask: \t \t 0x%04x \n " , mask ) ;
len + = sprintf ( p + len ,
" commands: \t enable, disable, reset, <mask> \n " ) ;
} else {
len + = sprintf ( p + len , " mask: \t \t not supported \n " ) ;
len + = sprintf ( p + len , " commands: \t enable, disable, reset \n " ) ;
}
return len ;
}
2005-08-17 00:00:00 -04:00
static int hotkey_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
int status , mask ;
char * cmd ;
int do_cmd = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! hotkey_supported )
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2005-08-17 00:00:00 -04:00
if ( ! hotkey_get ( & status , & mask ) )
return - EIO ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( strlencmp ( cmd , " enable " ) = = 0 ) {
status = 1 ;
} else if ( strlencmp ( cmd , " disable " ) = = 0 ) {
status = 0 ;
} else if ( strlencmp ( cmd , " reset " ) = = 0 ) {
2005-08-17 00:00:00 -04:00
status = hotkey_orig_status ;
mask = hotkey_orig_mask ;
2005-04-16 15:20:36 -07:00
} else if ( sscanf ( cmd , " 0x%x " , & mask ) = = 1 ) {
/* mask set */
} else if ( sscanf ( cmd , " %x " , & mask ) = = 1 ) {
/* mask set */
} else
return - EINVAL ;
do_cmd = 1 ;
}
2005-08-17 00:00:00 -04:00
if ( do_cmd & & ! hotkey_set ( status , mask ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
return 0 ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
static void hotkey_exit ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
if ( hotkey_supported )
hotkey_set ( hotkey_orig_status , hotkey_orig_mask ) ;
2005-04-16 15:20:36 -07:00
}
static void hotkey_notify ( struct ibm_struct * ibm , u32 event )
{
int hkey ;
if ( acpi_evalf ( hkey_handle , & hkey , " MHKP " , " d " ) )
acpi_bus_generate_event ( ibm - > device , event , hkey ) ;
else {
printk ( IBM_ERR " unknown hotkey event %d \n " , event ) ;
acpi_bus_generate_event ( ibm - > device , event , 0 ) ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
static int bluetooth_supported ;
static int bluetooth_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
G4x , R30 , R31 , R40e , R50e , T20 - 22 , X20 - 21 */
bluetooth_supported = hkey_handle & &
acpi_evalf ( hkey_handle , NULL , " GBDC " , " qv " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int bluetooth_status ( void )
2005-04-16 15:20:36 -07:00
{
int status ;
2005-08-17 00:00:00 -04:00
if ( ! bluetooth_supported | |
! acpi_evalf ( hkey_handle , & status , " GBDC " , " d " ) )
2005-04-16 15:20:36 -07:00
status = 0 ;
return status ;
}
2005-08-17 00:00:00 -04:00
static int bluetooth_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
2005-08-17 00:00:00 -04:00
int status = bluetooth_status ( ) ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
if ( ! bluetooth_supported )
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
else if ( ! ( status & 1 ) )
len + = sprintf ( p + len , " status: \t \t not installed \n " ) ;
else {
len + = sprintf ( p + len , " status: \t \t %s \n " , enabled ( status , 1 ) ) ;
len + = sprintf ( p + len , " commands: \t enable, disable \n " ) ;
}
return len ;
}
2005-08-17 00:00:00 -04:00
static int bluetooth_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
int status = bluetooth_status ( ) ;
2005-04-16 15:20:36 -07:00
char * cmd ;
int do_cmd = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! bluetooth_supported )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( strlencmp ( cmd , " enable " ) = = 0 ) {
status | = 2 ;
} else if ( strlencmp ( cmd , " disable " ) = = 0 ) {
status & = ~ 2 ;
} else
return - EINVAL ;
do_cmd = 1 ;
}
if ( do_cmd & & ! acpi_evalf ( hkey_handle , NULL , " SBDC " , " vd " , status ) )
2005-08-17 00:00:00 -04:00
return - EIO ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int video_supported ;
static int video_orig_autosw ;
# define VIDEO_570 1
# define VIDEO_770 2
# define VIDEO_NEW 3
static int video_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
int ivga ;
if ( vid2_handle & & acpi_evalf ( NULL , & ivga , " \\ IVGA " , " d " ) & & ivga )
/* G41, assume IVGA doesn't change */
vid_handle = vid2_handle ;
if ( ! vid_handle )
/* video switching not supported on R30, R31 */
video_supported = 0 ;
else if ( acpi_evalf ( vid_handle , & video_orig_autosw , " SWIT " , " qd " ) )
/* 570 */
video_supported = VIDEO_570 ;
else if ( acpi_evalf ( vid_handle , & video_orig_autosw , " ^VADL " , " qd " ) )
/* 600e/x, 770e, 770x */
video_supported = VIDEO_770 ;
else
/* all others */
video_supported = VIDEO_NEW ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int video_status ( void )
2005-04-16 15:20:36 -07:00
{
int status = 0 ;
int i ;
2005-08-17 00:00:00 -04:00
if ( video_supported = = VIDEO_570 ) {
if ( acpi_evalf ( NULL , & i , " \\ _SB.PHS " , " dd " , 0x87 ) )
status = i & 3 ;
} else if ( video_supported = = VIDEO_770 ) {
if ( acpi_evalf ( NULL , & i , " \\ VCDL " , " d " ) )
status | = 0x01 * i ;
if ( acpi_evalf ( NULL , & i , " \\ VCDC " , " d " ) )
status | = 0x02 * i ;
} else if ( video_supported = = VIDEO_NEW ) {
acpi_evalf ( NULL , NULL , " \\ VUPS " , " vd " , 1 ) ;
if ( acpi_evalf ( NULL , & i , " \\ VCDC " , " d " ) )
status | = 0x02 * i ;
acpi_evalf ( NULL , NULL , " \\ VUPS " , " vd " , 0 ) ;
if ( acpi_evalf ( NULL , & i , " \\ VCDL " , " d " ) )
status | = 0x01 * i ;
if ( acpi_evalf ( NULL , & i , " \\ VCDD " , " d " ) )
status | = 0x08 * i ;
}
return status ;
}
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
static int video_autosw ( void )
{
int autosw = 0 ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
if ( video_supported = = VIDEO_570 )
acpi_evalf ( vid_handle , & autosw , " SWIT " , " d " ) ;
else if ( video_supported = = VIDEO_770 | | video_supported = = VIDEO_NEW )
acpi_evalf ( vid_handle , & autosw , " ^VDEE " , " d " ) ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
return autosw & 1 ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
static int video_read ( char * p )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
int status = video_status ( ) ;
int autosw = video_autosw ( ) ;
2005-04-16 15:20:36 -07:00
int len = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! video_supported ) {
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
return len ;
}
len + = sprintf ( p + len , " status: \t \t supported \n " ) ;
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " lcd: \t \t %s \n " , enabled ( status , 0 ) ) ;
len + = sprintf ( p + len , " crt: \t \t %s \n " , enabled ( status , 1 ) ) ;
2005-08-17 00:00:00 -04:00
if ( video_supported = = VIDEO_NEW )
len + = sprintf ( p + len , " dvi: \t \t %s \n " , enabled ( status , 3 ) ) ;
len + = sprintf ( p + len , " auto: \t \t %s \n " , enabled ( autosw , 0 ) ) ;
len + = sprintf ( p + len , " commands: \t lcd_enable, lcd_disable \n " ) ;
len + = sprintf ( p + len , " commands: \t crt_enable, crt_disable \n " ) ;
if ( video_supported = = VIDEO_NEW )
len + = sprintf ( p + len , " commands: \t dvi_enable, dvi_disable \n " ) ;
len + = sprintf ( p + len , " commands: \t auto_enable, auto_disable \n " ) ;
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " commands: \t video_switch, expand_toggle \n " ) ;
return len ;
}
2005-08-17 00:00:00 -04:00
static int video_switch ( void )
{
int autosw = video_autosw ( ) ;
int ret ;
if ( ! acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , 1 ) )
return - EIO ;
ret = video_supported = = VIDEO_570 ?
acpi_evalf ( ec_handle , NULL , " _Q16 " , " v " ) :
acpi_evalf ( vid_handle , NULL , " VSWT " , " v " ) ;
acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , autosw ) ;
return ret ;
}
static int video_expand ( void )
{
if ( video_supported = = VIDEO_570 )
return acpi_evalf ( ec_handle , NULL , " _Q17 " , " v " ) ;
else if ( video_supported = = VIDEO_770 )
return acpi_evalf ( vid_handle , NULL , " VEXP " , " v " ) ;
else
return acpi_evalf ( NULL , NULL , " \\ VEXP " , " v " ) ;
}
static int video_switch2 ( int status )
{
int ret ;
if ( video_supported = = VIDEO_570 ) {
ret = acpi_evalf ( NULL , NULL ,
" \\ _SB.PHS2 " , " vdd " , 0x8b , status | 0x80 ) ;
} else if ( video_supported = = VIDEO_770 ) {
int autosw = video_autosw ( ) ;
if ( ! acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , 1 ) )
return - EIO ;
ret = acpi_evalf ( vid_handle , NULL ,
" ASWT " , " vdd " , status * 0x100 , 0 ) ;
acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , autosw ) ;
} else {
ret = acpi_evalf ( NULL , NULL , " \\ VUPS " , " vd " , 0x80 ) & &
acpi_evalf ( NULL , NULL , " \\ VSDS " , " vdd " , status , 1 ) ;
}
return ret ;
}
static int video_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
char * cmd ;
int enable , disable , status ;
2005-08-17 00:00:00 -04:00
if ( ! video_supported )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
enable = disable = 0 ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( strlencmp ( cmd , " lcd_enable " ) = = 0 ) {
enable | = 0x01 ;
} else if ( strlencmp ( cmd , " lcd_disable " ) = = 0 ) {
disable | = 0x01 ;
} else if ( strlencmp ( cmd , " crt_enable " ) = = 0 ) {
enable | = 0x02 ;
} else if ( strlencmp ( cmd , " crt_disable " ) = = 0 ) {
disable | = 0x02 ;
2005-08-17 00:00:00 -04:00
} else if ( video_supported = = VIDEO_NEW & &
strlencmp ( cmd , " dvi_enable " ) = = 0 ) {
2005-04-16 15:20:36 -07:00
enable | = 0x08 ;
2005-08-17 00:00:00 -04:00
} else if ( video_supported = = VIDEO_NEW & &
strlencmp ( cmd , " dvi_disable " ) = = 0 ) {
2005-04-16 15:20:36 -07:00
disable | = 0x08 ;
} else if ( strlencmp ( cmd , " auto_enable " ) = = 0 ) {
if ( ! acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , 1 ) )
return - EIO ;
} else if ( strlencmp ( cmd , " auto_disable " ) = = 0 ) {
if ( ! acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , 0 ) )
return - EIO ;
} else if ( strlencmp ( cmd , " video_switch " ) = = 0 ) {
2005-08-17 00:00:00 -04:00
if ( ! video_switch ( ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
} else if ( strlencmp ( cmd , " expand_toggle " ) = = 0 ) {
2005-08-17 00:00:00 -04:00
if ( ! video_expand ( ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
} else
return - EINVAL ;
}
if ( enable | | disable ) {
2005-08-17 00:00:00 -04:00
status = ( video_status ( ) & 0x0f & ~ disable ) | enable ;
if ( ! video_switch2 ( status ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
}
return 0 ;
}
2005-08-17 00:00:00 -04:00
static void video_exit ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
acpi_evalf ( vid_handle , NULL , " _DOS " , " vd " , video_orig_autosw ) ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
static int light_supported ;
static int light_status_supported ;
static int light_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
light_supported = ( cmos_handle | | lght_handle ) & & ! ledb_handle ;
if ( light_supported )
/* light status not supported on
570 , 600 e / x , 770 e , 770 x , G4x , R30 , R31 , R32 , X20 */
light_status_supported = acpi_evalf ( ec_handle , NULL ,
" KBLT " , " qv " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int light_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
int status = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! light_supported ) {
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
} else if ( ! light_status_supported ) {
len + = sprintf ( p + len , " status: \t \t unknown \n " ) ;
len + = sprintf ( p + len , " commands: \t on, off \n " ) ;
} else {
2005-04-16 15:20:36 -07:00
if ( ! acpi_evalf ( ec_handle , & status , " KBLT " , " d " ) )
return - EIO ;
len + = sprintf ( p + len , " status: \t \t %s \n " , onoff ( status , 0 ) ) ;
2005-08-17 00:00:00 -04:00
len + = sprintf ( p + len , " commands: \t on, off \n " ) ;
}
2005-04-16 15:20:36 -07:00
return len ;
}
2005-08-17 00:00:00 -04:00
static int light_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
int cmos_cmd , lght_cmd ;
char * cmd ;
int success ;
2005-08-17 00:00:00 -04:00
if ( ! light_supported )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( strlencmp ( cmd , " on " ) = = 0 ) {
cmos_cmd = 0x0c ;
lght_cmd = 1 ;
} else if ( strlencmp ( cmd , " off " ) = = 0 ) {
cmos_cmd = 0x0d ;
lght_cmd = 0 ;
} else
return - EINVAL ;
2005-08-17 00:00:00 -04:00
2005-04-16 15:20:36 -07:00
success = cmos_handle ?
2005-08-17 00:00:00 -04:00
acpi_evalf ( cmos_handle , NULL , NULL , " vd " , cmos_cmd ) :
acpi_evalf ( lght_handle , NULL , NULL , " vd " , lght_cmd ) ;
2005-04-16 15:20:36 -07:00
if ( ! success )
return - EIO ;
}
return 0 ;
}
static int _sta ( acpi_handle handle )
{
int status ;
if ( ! handle | | ! acpi_evalf ( handle , & status , " _STA " , " d " ) )
status = 0 ;
return status ;
}
2006-02-23 17:56:06 -08:00
# ifdef CONFIG_ACPI_IBM_DOCK
2005-04-16 15:20:36 -07:00
# define dock_docked() (_sta(dock_handle) & 1)
2005-08-17 00:00:00 -04:00
static int dock_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
int docked = dock_docked ( ) ;
if ( ! dock_handle )
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
else if ( ! docked )
len + = sprintf ( p + len , " status: \t \t undocked \n " ) ;
else {
len + = sprintf ( p + len , " status: \t \t docked \n " ) ;
len + = sprintf ( p + len , " commands: \t dock, undock \n " ) ;
}
return len ;
}
2005-08-17 00:00:00 -04:00
static int dock_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
char * cmd ;
if ( ! dock_docked ( ) )
2005-08-17 00:00:00 -04:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( strlencmp ( cmd , " undock " ) = = 0 ) {
2005-08-17 00:00:00 -04:00
if ( ! acpi_evalf ( dock_handle , NULL , " _DCK " , " vd " , 0 ) | |
! acpi_evalf ( dock_handle , NULL , " _EJ0 " , " vd " , 1 ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
} else if ( strlencmp ( cmd , " dock " ) = = 0 ) {
if ( ! acpi_evalf ( dock_handle , NULL , " _DCK " , " vd " , 1 ) )
return - EIO ;
} else
return - EINVAL ;
}
return 0 ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
static void dock_notify ( struct ibm_struct * ibm , u32 event )
{
int docked = dock_docked ( ) ;
2005-08-17 00:00:00 -04:00
int pci = ibm - > hid & & strstr ( ibm - > hid , IBM_PCI_HID ) ;
if ( event = = 1 & & ! pci ) /* 570 */
acpi_bus_generate_event ( ibm - > device , event , 1 ) ; /* button */
else if ( event = = 1 & & pci ) /* 570 */
acpi_bus_generate_event ( ibm - > device , event , 3 ) ; /* dock */
else if ( event = = 3 & & docked )
acpi_bus_generate_event ( ibm - > device , event , 1 ) ; /* button */
2005-04-16 15:20:36 -07:00
else if ( event = = 3 & & ! docked )
2005-08-17 00:00:00 -04:00
acpi_bus_generate_event ( ibm - > device , event , 2 ) ; /* undock */
2005-04-16 15:20:36 -07:00
else if ( event = = 0 & & docked )
2005-08-17 00:00:00 -04:00
acpi_bus_generate_event ( ibm - > device , event , 3 ) ; /* dock */
2005-04-16 15:20:36 -07:00
else {
printk ( IBM_ERR " unknown dock event %d, status %d \n " ,
event , _sta ( dock_handle ) ) ;
2005-08-17 00:00:00 -04:00
acpi_bus_generate_event ( ibm - > device , event , 0 ) ; /* unknown */
2005-04-16 15:20:36 -07:00
}
}
2006-02-23 17:56:06 -08:00
# endif
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
static int bay_status_supported ;
static int bay_status2_supported ;
static int bay_eject_supported ;
static int bay_eject2_supported ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
static int bay_init ( void )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
bay_status_supported = bay_handle & &
acpi_evalf ( bay_handle , NULL , " _STA " , " qv " ) ;
bay_status2_supported = bay2_handle & &
acpi_evalf ( bay2_handle , NULL , " _STA " , " qv " ) ;
bay_eject_supported = bay_handle & & bay_ej_handle & &
( strlencmp ( bay_ej_path , " _EJ0 " ) = = 0 | | experimental ) ;
bay_eject2_supported = bay2_handle & & bay2_ej_handle & &
( strlencmp ( bay2_ej_path , " _EJ0 " ) = = 0 | | experimental ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-08-17 00:00:00 -04:00
# define bay_occupied(b) (_sta(b##_handle) & 1)
static int bay_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
2005-08-17 00:00:00 -04:00
int occupied = bay_occupied ( bay ) ;
int occupied2 = bay_occupied ( bay2 ) ;
int eject , eject2 ;
len + = sprintf ( p + len , " status: \t \t %s \n " , bay_status_supported ?
( occupied ? " occupied " : " unoccupied " ) :
" not supported " ) ;
if ( bay_status2_supported )
len + = sprintf ( p + len , " status2: \t %s \n " , occupied2 ?
" occupied " : " unoccupied " ) ;
eject = bay_eject_supported & & occupied ;
eject2 = bay_eject2_supported & & occupied2 ;
if ( eject & & eject2 )
len + = sprintf ( p + len , " commands: \t eject, eject2 \n " ) ;
else if ( eject )
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " commands: \t eject \n " ) ;
2005-08-17 00:00:00 -04:00
else if ( eject2 )
len + = sprintf ( p + len , " commands: \t eject2 \n " ) ;
2005-04-16 15:20:36 -07:00
return len ;
}
2005-08-17 00:00:00 -04:00
static int bay_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
char * cmd ;
2005-08-17 00:00:00 -04:00
if ( ! bay_eject_supported & & ! bay_eject2_supported )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
2005-08-17 00:00:00 -04:00
if ( bay_eject_supported & & strlencmp ( cmd , " eject " ) = = 0 ) {
if ( ! acpi_evalf ( bay_ej_handle , NULL , NULL , " vd " , 1 ) )
return - EIO ;
} else if ( bay_eject2_supported & &
strlencmp ( cmd , " eject2 " ) = = 0 ) {
if ( ! acpi_evalf ( bay2_ej_handle , NULL , NULL , " vd " , 1 ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
} else
return - EINVAL ;
}
return 0 ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
static void bay_notify ( struct ibm_struct * ibm , u32 event )
{
acpi_bus_generate_event ( ibm - > device , event , 0 ) ;
}
2005-08-17 00:00:00 -04:00
static int cmos_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
2005-08-17 00:00:00 -04:00
/* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
R30 , R31 , T20 - 22 , X20 - 21 */
2005-04-16 15:20:36 -07:00
if ( ! cmos_handle )
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
else {
len + = sprintf ( p + len , " status: \t \t supported \n " ) ;
2005-08-17 00:00:00 -04:00
len + = sprintf ( p + len , " commands: \t <cmd> (<cmd> is 0-21) \n " ) ;
2005-04-16 15:20:36 -07:00
}
return len ;
}
2005-08-17 00:00:00 -04:00
static int cmos_eval ( int cmos_cmd )
{
if ( cmos_handle )
return acpi_evalf ( cmos_handle , NULL , NULL , " vd " , cmos_cmd ) ;
else
return 1 ;
}
static int cmos_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
char * cmd ;
int cmos_cmd ;
if ( ! cmos_handle )
return - EINVAL ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
2005-08-17 00:00:00 -04:00
if ( sscanf ( cmd , " %u " , & cmos_cmd ) = = 1 & &
cmos_cmd > = 0 & & cmos_cmd < = 21 ) {
2005-04-16 15:20:36 -07:00
/* cmos_cmd set */
} else
return - EINVAL ;
2005-08-17 00:00:00 -04:00
if ( ! cmos_eval ( cmos_cmd ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
}
return 0 ;
2005-08-17 00:00:00 -04:00
}
static int led_supported ;
# define LED_570 1
# define LED_OLD 2
# define LED_NEW 3
static int led_init ( void )
{
if ( ! led_handle )
/* led not supported on R30, R31 */
led_supported = 0 ;
else if ( strlencmp ( led_path , " SLED " ) = = 0 )
/* 570 */
led_supported = LED_570 ;
else if ( strlencmp ( led_path , " SYSL " ) = = 0 )
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
led_supported = LED_OLD ;
else
/* all others */
led_supported = LED_NEW ;
return 0 ;
}
# define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking"))
static int led_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
2005-08-17 00:00:00 -04:00
if ( ! led_supported ) {
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
return len ;
}
len + = sprintf ( p + len , " status: \t \t supported \n " ) ;
if ( led_supported = = LED_570 ) {
/* 570 */
int i , status ;
for ( i = 0 ; i < 8 ; i + + ) {
if ( ! acpi_evalf ( ec_handle ,
& status , " GLED " , " dd " , 1 < < i ) )
return - EIO ;
len + = sprintf ( p + len , " %d: \t \t %s \n " ,
i , led_status ( status ) ) ;
}
}
2005-04-16 15:20:36 -07:00
len + = sprintf ( p + len , " commands: \t "
2005-08-17 00:00:00 -04:00
" <led> on, <led> off, <led> blink (<led> is 0-7) \n " ) ;
2005-04-16 15:20:36 -07:00
return len ;
}
2005-08-17 00:00:00 -04:00
/* off, on, blink */
static const int led_sled_arg1 [ ] = { 0 , 1 , 3 } ;
static const int led_exp_hlbl [ ] = { 0 , 0 , 1 } ; /* led# * */
static const int led_exp_hlcl [ ] = { 0 , 1 , 1 } ; /* led# * */
static const int led_led_arg1 [ ] = { 0 , 0x80 , 0xc0 } ;
# define EC_HLCL 0x0c
# define EC_HLBL 0x0d
# define EC_HLMS 0x0e
static int led_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
char * cmd ;
2005-08-17 00:00:00 -04:00
int led , ind , ret ;
if ( ! led_supported )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
while ( ( cmd = next_cmd ( & buf ) ) ) {
2005-08-17 00:00:00 -04:00
if ( sscanf ( cmd , " %d " , & led ) ! = 1 | | led < 0 | | led > 7 )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2005-08-17 00:00:00 -04:00
if ( strstr ( cmd , " off " ) ) {
ind = 0 ;
2005-04-16 15:20:36 -07:00
} else if ( strstr ( cmd , " on " ) ) {
2005-08-17 00:00:00 -04:00
ind = 1 ;
} else if ( strstr ( cmd , " blink " ) ) {
ind = 2 ;
2005-04-16 15:20:36 -07:00
} else
return - EINVAL ;
2005-08-17 00:00:00 -04:00
if ( led_supported = = LED_570 ) {
/* 570 */
led = 1 < < led ;
2005-04-16 15:20:36 -07:00
if ( ! acpi_evalf ( led_handle , NULL , NULL , " vdd " ,
2005-08-17 00:00:00 -04:00
led , led_sled_arg1 [ ind ] ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
2005-08-17 00:00:00 -04:00
} else if ( led_supported = = LED_OLD ) {
/* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
led = 1 < < led ;
ret = ec_write ( EC_HLMS , led ) ;
if ( ret > = 0 )
ret =
ec_write ( EC_HLBL , led * led_exp_hlbl [ ind ] ) ;
if ( ret > = 0 )
ret =
ec_write ( EC_HLCL , led * led_exp_hlcl [ ind ] ) ;
if ( ret < 0 )
return ret ;
} else {
/* all others */
if ( ! acpi_evalf ( led_handle , NULL , NULL , " vdd " ,
led , led_led_arg1 [ ind ] ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
2005-08-17 00:00:00 -04:00
}
}
return 0 ;
}
static int beep_read ( char * p )
{
int len = 0 ;
if ( ! beep_handle )
len + = sprintf ( p + len , " status: \t \t not supported \n " ) ;
else {
len + = sprintf ( p + len , " status: \t \t supported \n " ) ;
len + = sprintf ( p + len , " commands: \t <cmd> (<cmd> is 0-17) \n " ) ;
}
return len ;
}
static int beep_write ( char * buf )
{
char * cmd ;
int beep_cmd ;
if ( ! beep_handle )
return - ENODEV ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( sscanf ( cmd , " %u " , & beep_cmd ) = = 1 & &
beep_cmd > = 0 & & beep_cmd < = 17 ) {
/* beep_cmd set */
} else
return - EINVAL ;
if ( ! acpi_evalf ( beep_handle , NULL , NULL , " vdd " , beep_cmd , 0 ) )
return - EIO ;
}
return 0 ;
}
static int acpi_ec_read ( int i , u8 * p )
{
int v ;
if ( ecrd_handle ) {
if ( ! acpi_evalf ( ecrd_handle , & v , NULL , " dd " , i ) )
return 0 ;
* p = v ;
} else {
if ( ec_read ( i , p ) < 0 )
return 0 ;
}
return 1 ;
}
static int acpi_ec_write ( int i , u8 v )
{
if ( ecwr_handle ) {
if ( ! acpi_evalf ( ecwr_handle , NULL , NULL , " vdd " , i , v ) )
return 0 ;
} else {
if ( ec_write ( i , v ) < 0 )
return 0 ;
}
return 1 ;
}
static int thermal_tmp_supported ;
static int thermal_updt_supported ;
static int thermal_init ( void )
{
/* temperatures not supported on 570, G4x, R30, R31, R32 */
thermal_tmp_supported = acpi_evalf ( ec_handle , NULL , " TMP7 " , " qv " ) ;
/* 600e/x, 770e, 770x */
thermal_updt_supported = acpi_evalf ( ec_handle , NULL , " UPDT " , " qv " ) ;
return 0 ;
}
static int thermal_read ( char * p )
{
int len = 0 ;
if ( ! thermal_tmp_supported )
len + = sprintf ( p + len , " temperatures: \t not supported \n " ) ;
else {
int i , t ;
char tmpi [ ] = " TMPi " ;
s8 tmp [ 8 ] ;
if ( thermal_updt_supported )
if ( ! acpi_evalf ( ec_handle , NULL , " UPDT " , " v " ) )
return - EIO ;
for ( i = 0 ; i < 8 ; i + + ) {
tmpi [ 3 ] = ' 0 ' + i ;
if ( ! acpi_evalf ( ec_handle , & t , tmpi , " d " ) )
return - EIO ;
if ( thermal_updt_supported )
tmp [ i ] = ( t - 2732 + 5 ) / 10 ;
else
tmp [ i ] = t ;
}
len + = sprintf ( p + len ,
" temperatures: \t %d %d %d %d %d %d %d %d \n " ,
tmp [ 0 ] , tmp [ 1 ] , tmp [ 2 ] , tmp [ 3 ] ,
tmp [ 4 ] , tmp [ 5 ] , tmp [ 6 ] , tmp [ 7 ] ) ;
}
return len ;
}
static u8 ecdump_regs [ 256 ] ;
static int ecdump_read ( char * p )
{
int len = 0 ;
int i , j ;
u8 v ;
len + = sprintf ( p + len , " EC "
" +00 +01 +02 +03 +04 +05 +06 +07 "
" +08 +09 +0a +0b +0c +0d +0e +0f \n " ) ;
for ( i = 0 ; i < 256 ; i + = 16 ) {
len + = sprintf ( p + len , " EC 0x%02x: " , i ) ;
for ( j = 0 ; j < 16 ; j + + ) {
if ( ! acpi_ec_read ( i + j , & v ) )
break ;
if ( v ! = ecdump_regs [ i + j ] )
len + = sprintf ( p + len , " *%02x " , v ) ;
else
len + = sprintf ( p + len , " %02x " , v ) ;
ecdump_regs [ i + j ] = v ;
}
len + = sprintf ( p + len , " \n " ) ;
if ( j ! = 16 )
break ;
}
/* These are way too dangerous to advertise openly... */
#if 0
len + = sprintf ( p + len , " commands: \t 0x<offset> 0x<value> "
" (<offset> is 00-ff, <value> is 00-ff) \n " ) ;
len + = sprintf ( p + len , " commands: \t 0x<offset> <value> "
" (<offset> is 00-ff, <value> is 0-255) \n " ) ;
# endif
return len ;
}
static int ecdump_write ( char * buf )
{
char * cmd ;
int i , v ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( sscanf ( cmd , " 0x%x 0x%x " , & i , & v ) = = 2 ) {
/* i and v set */
} else if ( sscanf ( cmd , " 0x%x %u " , & i , & v ) = = 2 ) {
/* i and v set */
} else
return - EINVAL ;
if ( i > = 0 & & i < 256 & & v > = 0 & & v < 256 ) {
if ( ! acpi_ec_write ( i , v ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
} else
return - EINVAL ;
}
return 0 ;
2005-08-17 00:00:00 -04:00
}
static int brightness_offset = 0x31 ;
static int brightness_read ( char * p )
2005-04-16 15:20:36 -07:00
{
int len = 0 ;
2005-08-17 00:00:00 -04:00
u8 level ;
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
if ( ! acpi_ec_read ( brightness_offset , & level ) ) {
len + = sprintf ( p + len , " level: \t \t unreadable \n " ) ;
} else {
len + = sprintf ( p + len , " level: \t \t %d \n " , level & 0x7 ) ;
len + = sprintf ( p + len , " commands: \t up, down \n " ) ;
len + = sprintf ( p + len , " commands: \t level <level> "
" (<level> is 0-7) \n " ) ;
}
2005-04-16 15:20:36 -07:00
return len ;
}
2005-08-17 00:00:00 -04:00
# define BRIGHTNESS_UP 4
# define BRIGHTNESS_DOWN 5
static int brightness_write ( char * buf )
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
int cmos_cmd , inc , i ;
u8 level ;
int new_level ;
2005-04-16 15:20:36 -07:00
char * cmd ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
2005-08-17 00:00:00 -04:00
if ( ! acpi_ec_read ( brightness_offset , & level ) )
return - EIO ;
level & = 7 ;
if ( strlencmp ( cmd , " up " ) = = 0 ) {
new_level = level = = 7 ? 7 : level + 1 ;
} else if ( strlencmp ( cmd , " down " ) = = 0 ) {
new_level = level = = 0 ? 0 : level - 1 ;
} else if ( sscanf ( cmd , " level %d " , & new_level ) = = 1 & &
new_level > = 0 & & new_level < = 7 ) {
/* new_level set */
} else
return - EINVAL ;
cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN ;
inc = new_level > level ? 1 : - 1 ;
for ( i = level ; i ! = new_level ; i + = inc ) {
if ( ! cmos_eval ( cmos_cmd ) )
return - EIO ;
if ( ! acpi_ec_write ( brightness_offset , i + inc ) )
return - EIO ;
}
}
return 0 ;
}
static int volume_offset = 0x30 ;
static int volume_read ( char * p )
{
int len = 0 ;
u8 level ;
if ( ! acpi_ec_read ( volume_offset , & level ) ) {
len + = sprintf ( p + len , " level: \t \t unreadable \n " ) ;
} else {
len + = sprintf ( p + len , " level: \t \t %d \n " , level & 0xf ) ;
len + = sprintf ( p + len , " mute: \t \t %s \n " , onoff ( level , 6 ) ) ;
len + = sprintf ( p + len , " commands: \t up, down, mute \n " ) ;
len + = sprintf ( p + len , " commands: \t level <level> "
" (<level> is 0-15) \n " ) ;
}
return len ;
}
# define VOLUME_DOWN 0
# define VOLUME_UP 1
# define VOLUME_MUTE 2
static int volume_write ( char * buf )
{
int cmos_cmd , inc , i ;
u8 level , mute ;
int new_level , new_mute ;
char * cmd ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( ! acpi_ec_read ( volume_offset , & level ) )
return - EIO ;
new_mute = mute = level & 0x40 ;
new_level = level = level & 0xf ;
if ( strlencmp ( cmd , " up " ) = = 0 ) {
if ( mute )
new_mute = 0 ;
else
new_level = level = = 15 ? 15 : level + 1 ;
} else if ( strlencmp ( cmd , " down " ) = = 0 ) {
if ( mute )
new_mute = 0 ;
else
new_level = level = = 0 ? 0 : level - 1 ;
} else if ( sscanf ( cmd , " level %d " , & new_level ) = = 1 & &
new_level > = 0 & & new_level < = 15 ) {
/* new_level set */
} else if ( strlencmp ( cmd , " mute " ) = = 0 ) {
new_mute = 0x40 ;
2005-04-16 15:20:36 -07:00
} else
return - EINVAL ;
2005-08-17 00:00:00 -04:00
if ( new_level ! = level ) { /* mute doesn't change */
cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN ;
inc = new_level > level ? 1 : - 1 ;
if ( mute & & ( ! cmos_eval ( cmos_cmd ) | |
! acpi_ec_write ( volume_offset , level ) ) )
return - EIO ;
for ( i = level ; i ! = new_level ; i + = inc )
if ( ! cmos_eval ( cmos_cmd ) | |
! acpi_ec_write ( volume_offset , i + inc ) )
return - EIO ;
if ( mute & & ( ! cmos_eval ( VOLUME_MUTE ) | |
! acpi_ec_write ( volume_offset ,
new_level + mute ) ) )
return - EIO ;
}
if ( new_mute ! = mute ) { /* level doesn't change */
cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP ;
if ( ! cmos_eval ( cmos_cmd ) | |
! acpi_ec_write ( volume_offset , level + new_mute ) )
return - EIO ;
}
}
return 0 ;
}
static int fan_status_offset = 0x2f ;
static int fan_rpm_offset = 0x84 ;
static int fan_read ( char * p )
{
int len = 0 ;
int s ;
u8 lo , hi , status ;
if ( gfan_handle ) {
/* 570, 600e/x, 770e, 770x */
if ( ! acpi_evalf ( gfan_handle , & s , NULL , " d " ) )
2005-04-16 15:20:36 -07:00
return - EIO ;
2005-08-17 00:00:00 -04:00
len + = sprintf ( p + len , " level: \t \t %d \n " , s ) ;
} else {
/* all except 570, 600e/x, 770e, 770x */
if ( ! acpi_ec_read ( fan_status_offset , & status ) )
len + = sprintf ( p + len , " status: \t \t unreadable \n " ) ;
else
len + = sprintf ( p + len , " status: \t \t %s \n " ,
enabled ( status , 7 ) ) ;
if ( ! acpi_ec_read ( fan_rpm_offset , & lo ) | |
! acpi_ec_read ( fan_rpm_offset + 1 , & hi ) )
len + = sprintf ( p + len , " speed: \t \t unreadable \n " ) ;
else
len + = sprintf ( p + len , " speed: \t \t %d \n " ,
( hi < < 8 ) + lo ) ;
}
if ( sfan_handle )
/* 570, 770x-JL */
len + = sprintf ( p + len , " commands: \t level <level> "
" (<level> is 0-7) \n " ) ;
if ( ! gfan_handle )
/* all except 570, 600e/x, 770e, 770x */
len + = sprintf ( p + len , " commands: \t enable, disable \n " ) ;
if ( fans_handle )
/* X31, X40 */
len + = sprintf ( p + len , " commands: \t speed <speed> "
" (<speed> is 0-65535) \n " ) ;
return len ;
}
static int fan_write ( char * buf )
{
char * cmd ;
int level , speed ;
while ( ( cmd = next_cmd ( & buf ) ) ) {
if ( sfan_handle & &
sscanf ( cmd , " level %d " , & level ) = = 1 & &
level > = 0 & & level < = 7 ) {
/* 570, 770x-JL */
if ( ! acpi_evalf ( sfan_handle , NULL , NULL , " vd " , level ) )
return - EIO ;
} else if ( ! gfan_handle & & strlencmp ( cmd , " enable " ) = = 0 ) {
/* all except 570, 600e/x, 770e, 770x */
if ( ! acpi_ec_write ( fan_status_offset , 0x80 ) )
return - EIO ;
} else if ( ! gfan_handle & & strlencmp ( cmd , " disable " ) = = 0 ) {
/* all except 570, 600e/x, 770e, 770x */
if ( ! acpi_ec_write ( fan_status_offset , 0x00 ) )
return - EIO ;
} else if ( fans_handle & &
sscanf ( cmd , " speed %d " , & speed ) = = 1 & &
speed > = 0 & & speed < = 65535 ) {
/* X31, X40 */
if ( ! acpi_evalf ( fans_handle , NULL , NULL , " vddd " ,
speed , speed , speed ) )
return - EIO ;
} else
return - EINVAL ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
static struct ibm_struct ibms [ ] = {
{
2005-08-17 00:00:00 -04:00
. name = " driver " ,
. init = driver_init ,
. read = driver_read ,
} ,
{
. name = " hotkey " ,
. hid = IBM_HKEY_HID ,
. init = hotkey_init ,
. read = hotkey_read ,
. write = hotkey_write ,
. exit = hotkey_exit ,
. notify = hotkey_notify ,
. handle = & hkey_handle ,
. type = ACPI_DEVICE_NOTIFY ,
} ,
{
. name = " bluetooth " ,
. init = bluetooth_init ,
. read = bluetooth_read ,
. write = bluetooth_write ,
} ,
{
. name = " video " ,
. init = video_init ,
. read = video_read ,
. write = video_write ,
. exit = video_exit ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " light " ,
. init = light_init ,
. read = light_read ,
. write = light_write ,
} ,
2006-02-23 17:56:06 -08:00
# ifdef CONFIG_ACPI_IBM_DOCK
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " dock " ,
. read = dock_read ,
. write = dock_write ,
. notify = dock_notify ,
. handle = & dock_handle ,
. type = ACPI_SYSTEM_NOTIFY ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " dock " ,
. hid = IBM_PCI_HID ,
. notify = dock_notify ,
. handle = & pci_handle ,
. type = ACPI_SYSTEM_NOTIFY ,
} ,
2006-02-23 17:56:06 -08:00
# endif
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " bay " ,
. init = bay_init ,
. read = bay_read ,
. write = bay_write ,
. notify = bay_notify ,
. handle = & bay_handle ,
. type = ACPI_SYSTEM_NOTIFY ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " cmos " ,
. read = cmos_read ,
. write = cmos_write ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " led " ,
. init = led_init ,
. read = led_read ,
. write = led_write ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " beep " ,
. read = beep_read ,
. write = beep_write ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " thermal " ,
. init = thermal_init ,
. read = thermal_read ,
} ,
2005-04-16 15:20:36 -07:00
{
2005-08-17 00:00:00 -04:00
. name = " ecdump " ,
. read = ecdump_read ,
. write = ecdump_write ,
. experimental = 1 ,
} ,
{
. name = " brightness " ,
. read = brightness_read ,
. write = brightness_write ,
. experimental = 1 ,
} ,
{
. name = " volume " ,
. read = volume_read ,
. write = volume_write ,
. experimental = 1 ,
} ,
{
. name = " fan " ,
. read = fan_read ,
. write = fan_write ,
. experimental = 1 ,
} ,
2005-04-16 15:20:36 -07:00
} ;
static int dispatch_read ( char * page , char * * start , off_t off , int count ,
int * eof , void * data )
{
struct ibm_struct * ibm = ( struct ibm_struct * ) data ;
int len ;
2005-08-17 00:00:00 -04:00
2005-04-16 15:20:36 -07:00
if ( ! ibm | | ! ibm - > read )
return - EINVAL ;
2005-08-17 00:00:00 -04:00
len = ibm - > read ( page ) ;
2005-04-16 15:20:36 -07:00
if ( len < 0 )
return len ;
if ( len < = off + count )
* eof = 1 ;
* start = page + off ;
len - = off ;
if ( len > count )
len = count ;
if ( len < 0 )
len = 0 ;
return len ;
}
2005-08-17 00:00:00 -04:00
static int dispatch_write ( struct file * file , const char __user * userbuf ,
2005-04-16 15:20:36 -07:00
unsigned long count , void * data )
{
struct ibm_struct * ibm = ( struct ibm_struct * ) data ;
char * kernbuf ;
int ret ;
if ( ! ibm | | ! ibm - > write )
return - EINVAL ;
kernbuf = kmalloc ( count + 2 , GFP_KERNEL ) ;
if ( ! kernbuf )
return - ENOMEM ;
2005-08-17 00:00:00 -04:00
if ( copy_from_user ( kernbuf , userbuf , count ) ) {
2005-04-16 15:20:36 -07:00
kfree ( kernbuf ) ;
2005-08-17 00:00:00 -04:00
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
kernbuf [ count ] = 0 ;
strcat ( kernbuf , " , " ) ;
2005-08-17 00:00:00 -04:00
ret = ibm - > write ( kernbuf ) ;
2005-04-16 15:20:36 -07:00
if ( ret = = 0 )
ret = count ;
kfree ( kernbuf ) ;
2005-08-17 00:00:00 -04:00
return ret ;
2005-04-16 15:20:36 -07:00
}
static void dispatch_notify ( acpi_handle handle , u32 event , void * data )
{
struct ibm_struct * ibm = ( struct ibm_struct * ) data ;
if ( ! ibm | | ! ibm - > notify )
return ;
ibm - > notify ( ibm , event ) ;
}
2005-08-17 00:00:00 -04:00
static int __init setup_notify ( struct ibm_struct * ibm )
2005-04-16 15:20:36 -07:00
{
acpi_status status ;
int ret ;
if ( ! * ibm - > handle )
return 0 ;
ret = acpi_bus_get_device ( * ibm - > handle , & ibm - > device ) ;
if ( ret < 0 ) {
printk ( IBM_ERR " %s device not present \n " , ibm - > name ) ;
return 0 ;
}
acpi_driver_data ( ibm - > device ) = ibm ;
sprintf ( acpi_device_class ( ibm - > device ) , " %s/%s " , IBM_NAME , ibm - > name ) ;
status = acpi_install_notify_handler ( * ibm - > handle , ibm - > type ,
dispatch_notify , ibm ) ;
if ( ACPI_FAILURE ( status ) ) {
printk ( IBM_ERR " acpi_install_notify_handler(%s) failed: %d \n " ,
ibm - > name , status ) ;
return - ENODEV ;
}
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int __init ibm_device_add ( struct acpi_device * device )
2005-04-16 15:20:36 -07:00
{
return 0 ;
}
2005-08-17 00:00:00 -04:00
static int __init register_driver ( struct ibm_struct * ibm )
2005-04-16 15:20:36 -07:00
{
int ret ;
ibm - > driver = kmalloc ( sizeof ( struct acpi_driver ) , GFP_KERNEL ) ;
if ( ! ibm - > driver ) {
printk ( IBM_ERR " kmalloc(ibm->driver) failed \n " ) ;
return - 1 ;
}
memset ( ibm - > driver , 0 , sizeof ( struct acpi_driver ) ) ;
sprintf ( ibm - > driver - > name , " %s/%s " , IBM_NAME , ibm - > name ) ;
ibm - > driver - > ids = ibm - > hid ;
2005-08-17 00:00:00 -04:00
ibm - > driver - > ops . add = & ibm_device_add ;
2005-04-16 15:20:36 -07:00
ret = acpi_bus_register_driver ( ibm - > driver ) ;
if ( ret < 0 ) {
printk ( IBM_ERR " acpi_bus_register_driver(%s) failed: %d \n " ,
ibm - > hid , ret ) ;
kfree ( ibm - > driver ) ;
}
return ret ;
}
2005-08-17 00:00:00 -04:00
static int __init ibm_init ( struct ibm_struct * ibm )
2005-04-16 15:20:36 -07:00
{
int ret ;
struct proc_dir_entry * entry ;
if ( ibm - > experimental & & ! experimental )
return 0 ;
if ( ibm - > hid ) {
ret = register_driver ( ibm ) ;
if ( ret < 0 )
return ret ;
ibm - > driver_registered = 1 ;
}
if ( ibm - > init ) {
2005-08-17 00:00:00 -04:00
ret = ibm - > init ( ) ;
2005-04-16 15:20:36 -07:00
if ( ret ! = 0 )
return ret ;
ibm - > init_called = 1 ;
}
2005-08-17 00:00:00 -04:00
if ( ibm - > read ) {
entry = create_proc_entry ( ibm - > name ,
S_IFREG | S_IRUGO | S_IWUSR ,
proc_dir ) ;
if ( ! entry ) {
printk ( IBM_ERR " unable to create proc entry %s \n " ,
ibm - > name ) ;
return - ENODEV ;
}
entry - > owner = THIS_MODULE ;
entry - > data = ibm ;
2005-04-16 15:20:36 -07:00
entry - > read_proc = & dispatch_read ;
2005-08-17 00:00:00 -04:00
if ( ibm - > write )
entry - > write_proc = & dispatch_write ;
ibm - > proc_created = 1 ;
}
2005-04-16 15:20:36 -07:00
if ( ibm - > notify ) {
ret = setup_notify ( ibm ) ;
if ( ret < 0 )
return ret ;
2005-08-17 00:00:00 -04:00
ibm - > notify_installed = 1 ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
static void ibm_exit ( struct ibm_struct * ibm )
{
if ( ibm - > notify_installed )
acpi_remove_notify_handler ( * ibm - > handle , ibm - > type ,
dispatch_notify ) ;
if ( ibm - > proc_created )
remove_proc_entry ( ibm - > name , proc_dir ) ;
if ( ibm - > init_called & & ibm - > exit )
2005-08-17 00:00:00 -04:00
ibm - > exit ( ) ;
2005-04-16 15:20:36 -07:00
if ( ibm - > driver_registered ) {
acpi_bus_unregister_driver ( ibm - > driver ) ;
kfree ( ibm - > driver ) ;
}
}
2005-08-17 00:00:00 -04:00
static void __init ibm_handle_init ( char * name ,
acpi_handle * handle , acpi_handle parent ,
char * * paths , int num_paths , char * * path )
2005-04-16 15:20:36 -07:00
{
int i ;
acpi_status status ;
2005-08-17 00:00:00 -04:00
for ( i = 0 ; i < num_paths ; i + + ) {
2005-04-16 15:20:36 -07:00
status = acpi_get_handle ( parent , paths [ i ] , handle ) ;
2005-08-17 00:00:00 -04:00
if ( ACPI_SUCCESS ( status ) ) {
* path = paths [ i ] ;
return ;
}
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
* handle = NULL ;
2005-04-16 15:20:36 -07:00
}
2005-08-17 00:00:00 -04:00
# define IBM_HANDLE_INIT(object) \
2005-04-16 15:20:36 -07:00
ibm_handle_init ( # object , & object # # _handle , * object # # _parent , \
2005-08-17 00:00:00 -04:00
object # # _paths , ARRAY_SIZE ( object # # _paths ) , & object # # _path )
2005-04-16 15:20:36 -07:00
static int set_ibm_param ( const char * val , struct kernel_param * kp )
{
unsigned int i ;
2005-08-17 00:00:00 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( ibms ) ; i + + )
if ( strcmp ( ibms [ i ] . name , kp - > name ) = = 0 & & ibms [ i ] . write ) {
if ( strlen ( val ) > sizeof ( ibms [ i ] . param ) - 2 )
return - ENOSPC ;
strcpy ( ibms [ i ] . param , val ) ;
strcat ( ibms [ i ] . param , " , " ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
# define IBM_PARAM(feature) \
module_param_call ( feature , set_ibm_param , NULL , NULL , 0 )
2005-08-17 00:00:00 -04:00
IBM_PARAM ( hotkey ) ;
IBM_PARAM ( bluetooth ) ;
IBM_PARAM ( video ) ;
IBM_PARAM ( light ) ;
2006-02-23 17:56:06 -08:00
# ifdef CONFIG_ACPI_IBM_DOCK
2005-08-17 00:00:00 -04:00
IBM_PARAM ( dock ) ;
2006-02-23 17:56:06 -08:00
# endif
2005-08-17 00:00:00 -04:00
IBM_PARAM ( bay ) ;
IBM_PARAM ( cmos ) ;
IBM_PARAM ( led ) ;
IBM_PARAM ( beep ) ;
IBM_PARAM ( ecdump ) ;
IBM_PARAM ( brightness ) ;
IBM_PARAM ( volume ) ;
IBM_PARAM ( fan ) ;
2005-04-16 15:20:36 -07:00
static void acpi_ibm_exit ( void )
{
int i ;
2005-08-17 00:00:00 -04:00
for ( i = ARRAY_SIZE ( ibms ) - 1 ; i > = 0 ; i - - )
2005-04-16 15:20:36 -07:00
ibm_exit ( & ibms [ i ] ) ;
remove_proc_entry ( IBM_DIR , acpi_root_dir ) ;
}
static int __init acpi_ibm_init ( void )
{
int ret , i ;
if ( acpi_disabled )
return - ENODEV ;
2005-08-17 00:00:00 -04:00
if ( ! acpi_specific_hotkey_enabled ) {
printk ( IBM_ERR " using generic hotkey driver \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
2005-08-17 00:00:00 -04:00
/* ec is required because many other handles are relative to it */
IBM_HANDLE_INIT ( ec ) ;
if ( ! ec_handle ) {
printk ( IBM_ERR " ec object not found \n " ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
2005-08-17 00:00:00 -04:00
}
2005-04-16 15:20:36 -07:00
/* these handles are not required */
2005-08-17 00:00:00 -04:00
IBM_HANDLE_INIT ( vid ) ;
IBM_HANDLE_INIT ( vid2 ) ;
IBM_HANDLE_INIT ( ledb ) ;
IBM_HANDLE_INIT ( led ) ;
IBM_HANDLE_INIT ( hkey ) ;
IBM_HANDLE_INIT ( lght ) ;
IBM_HANDLE_INIT ( cmos ) ;
2006-02-23 17:56:06 -08:00
# ifdef CONFIG_ACPI_IBM_DOCK
2005-08-17 00:00:00 -04:00
IBM_HANDLE_INIT ( dock ) ;
2006-02-23 17:56:06 -08:00
# endif
2005-08-17 00:00:00 -04:00
IBM_HANDLE_INIT ( pci ) ;
IBM_HANDLE_INIT ( bay ) ;
if ( bay_handle )
IBM_HANDLE_INIT ( bay_ej ) ;
IBM_HANDLE_INIT ( bay2 ) ;
if ( bay2_handle )
IBM_HANDLE_INIT ( bay2_ej ) ;
IBM_HANDLE_INIT ( beep ) ;
IBM_HANDLE_INIT ( ecrd ) ;
IBM_HANDLE_INIT ( ecwr ) ;
IBM_HANDLE_INIT ( fans ) ;
IBM_HANDLE_INIT ( gfan ) ;
IBM_HANDLE_INIT ( sfan ) ;
2005-04-16 15:20:36 -07:00
proc_dir = proc_mkdir ( IBM_DIR , acpi_root_dir ) ;
if ( ! proc_dir ) {
printk ( IBM_ERR " unable to create proc dir %s " , IBM_DIR ) ;
return - ENODEV ;
}
proc_dir - > owner = THIS_MODULE ;
2005-08-17 00:00:00 -04:00
for ( i = 0 ; i < ARRAY_SIZE ( ibms ) ; i + + ) {
2005-04-16 15:20:36 -07:00
ret = ibm_init ( & ibms [ i ] ) ;
2005-08-17 00:00:00 -04:00
if ( ret > = 0 & & * ibms [ i ] . param )
ret = ibms [ i ] . write ( ibms [ i ] . param ) ;
2005-04-16 15:20:36 -07:00
if ( ret < 0 ) {
acpi_ibm_exit ( ) ;
return ret ;
}
}
return 0 ;
}
module_init ( acpi_ibm_init ) ;
module_exit ( acpi_ibm_exit ) ;