2005-04-17 02:20:36 +04:00
/*
* i8k . c - - Linux driver for accessing the SMM BIOS on Dell laptops .
* See http : //www.debian.org/~dz/i8k/ for more information
* and for latest version of this driver .
*
* Copyright ( C ) 2001 Massimo Dal Zotto < dz @ debian . org >
*
* 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 , 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 .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/proc_fs.h>
2005-06-26 01:54:26 +04:00
# include <linux/seq_file.h>
2005-06-26 01:54:25 +04:00
# include <linux/dmi.h>
2007-05-08 11:28:59 +04:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <asm/uaccess.h>
# include <asm/io.h>
# include <linux/i8k.h>
2005-06-26 01:54:26 +04:00
# define I8K_VERSION "1.14 21 / 02 / 2005"
2005-04-17 02:20:36 +04:00
# define I8K_SMM_FN_STATUS 0x0025
# define I8K_SMM_POWER_STATUS 0x0069
# define I8K_SMM_SET_FAN 0x01a3
# define I8K_SMM_GET_FAN 0x00a3
# define I8K_SMM_GET_SPEED 0x02a3
# define I8K_SMM_GET_TEMP 0x10a3
2005-06-26 01:54:28 +04:00
# define I8K_SMM_GET_DELL_SIG1 0xfea3
# define I8K_SMM_GET_DELL_SIG2 0xffa3
2005-04-17 02:20:36 +04:00
# define I8K_SMM_BIOS_VERSION 0x00a6
# define I8K_FAN_MULT 30
# define I8K_MAX_TEMP 127
# define I8K_FN_NONE 0x00
# define I8K_FN_UP 0x01
# define I8K_FN_DOWN 0x02
# define I8K_FN_MUTE 0x04
# define I8K_FN_MASK 0x07
# define I8K_FN_SHIFT 8
# define I8K_POWER_AC 0x05
# define I8K_POWER_BATTERY 0x01
# define I8K_TEMPERATURE_BUG 1
2005-06-26 01:54:25 +04:00
static char bios_version [ 4 ] ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Massimo Dal Zotto (dz@debian.org) " ) ;
MODULE_DESCRIPTION ( " Driver for accessing SMM BIOS on Dell laptops " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int force ;
module_param ( force , bool , 0 ) ;
MODULE_PARM_DESC ( force , " Force loading without checking for supported models " ) ;
2005-06-26 01:54:25 +04:00
static int ignore_dmi ;
module_param ( ignore_dmi , bool , 0 ) ;
MODULE_PARM_DESC ( ignore_dmi , " Continue probing hardware even if DMI data does not match " ) ;
2005-04-17 02:20:36 +04:00
static int restricted ;
module_param ( restricted , bool , 0 ) ;
MODULE_PARM_DESC ( restricted , " Allow fan control if SYS_ADMIN capability set " ) ;
static int power_status ;
module_param ( power_status , bool , 0600 ) ;
MODULE_PARM_DESC ( power_status , " Report power status in /proc/i8k " ) ;
2008-05-01 15:34:58 +04:00
static int fan_mult = I8K_FAN_MULT ;
module_param ( fan_mult , int , 0 ) ;
MODULE_PARM_DESC ( fan_mult , " Factor to multiply fan speed with " ) ;
2005-06-26 01:54:26 +04:00
static int i8k_open_fs ( struct inode * inode , struct file * file ) ;
2005-04-17 02:20:36 +04:00
static int i8k_ioctl ( struct inode * , struct file * , unsigned int ,
unsigned long ) ;
2006-07-03 11:24:21 +04:00
static const struct file_operations i8k_fops = {
2008-04-29 12:02:34 +04:00
. owner = THIS_MODULE ,
2005-06-26 01:54:26 +04:00
. open = i8k_open_fs ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
. ioctl = i8k_ioctl ,
2005-04-17 02:20:36 +04:00
} ;
2005-06-26 01:54:27 +04:00
struct smm_regs {
2005-06-26 01:54:23 +04:00
unsigned int eax ;
unsigned int ebx __attribute__ ( ( packed ) ) ;
unsigned int ecx __attribute__ ( ( packed ) ) ;
unsigned int edx __attribute__ ( ( packed ) ) ;
unsigned int esi __attribute__ ( ( packed ) ) ;
unsigned int edi __attribute__ ( ( packed ) ) ;
2005-06-26 01:54:27 +04:00
} ;
2005-04-17 02:20:36 +04:00
2007-10-03 23:15:40 +04:00
static inline const char * i8k_get_dmi_data ( int field )
2005-06-26 01:54:25 +04:00
{
2007-10-03 23:15:40 +04:00
const char * dmi_data = dmi_get_system_info ( field ) ;
2005-11-12 08:55:15 +03:00
return dmi_data & & * dmi_data ? dmi_data : " ? " ;
2005-06-26 01:54:25 +04:00
}
2005-04-17 02:20:36 +04:00
/*
* Call the System Management Mode BIOS . Code provided by Jonathan Buzzard .
*/
2005-06-26 01:54:27 +04:00
static int i8k_smm ( struct smm_regs * regs )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:23 +04:00
int rc ;
int eax = regs - > eax ;
2008-02-07 11:16:27 +03:00
# if defined(CONFIG_X86_64)
asm ( " pushq %%rax \n \t "
" movl 0(%%rax),%%edx \n \t "
" pushq %%rdx \n \t "
" movl 4(%%rax),%%ebx \n \t "
" movl 8(%%rax),%%ecx \n \t "
" movl 12(%%rax),%%edx \n \t "
" movl 16(%%rax),%%esi \n \t "
" movl 20(%%rax),%%edi \n \t "
" popq %%rax \n \t "
" out %%al,$0xb2 \n \t "
" out %%al,$0x84 \n \t "
" xchgq %%rax,(%%rsp) \n \t "
" movl %%ebx,4(%%rax) \n \t "
" movl %%ecx,8(%%rax) \n \t "
" movl %%edx,12(%%rax) \n \t "
" movl %%esi,16(%%rax) \n \t "
" movl %%edi,20(%%rax) \n \t "
" popq %%rdx \n \t "
" movl %%edx,0(%%rax) \n \t "
" lahf \n \t "
" shrl $8,%%eax \n \t "
" andl $1,%%eax \n "
: " =a " ( rc )
: " a " ( regs )
: " %ebx " , " %ecx " , " %edx " , " %esi " , " %edi " , " memory " ) ;
# else
2005-06-26 01:54:23 +04:00
asm ( " pushl %%eax \n \t "
" movl 0(%%eax),%%edx \n \t "
" push %%edx \n \t "
" movl 4(%%eax),%%ebx \n \t "
" movl 8(%%eax),%%ecx \n \t "
" movl 12(%%eax),%%edx \n \t "
" movl 16(%%eax),%%esi \n \t "
" movl 20(%%eax),%%edi \n \t "
" popl %%eax \n \t "
" out %%al,$0xb2 \n \t "
" out %%al,$0x84 \n \t "
" xchgl %%eax,(%%esp) \n \t "
" movl %%ebx,4(%%eax) \n \t "
" movl %%ecx,8(%%eax) \n \t "
" movl %%edx,12(%%eax) \n \t "
" movl %%esi,16(%%eax) \n \t "
" movl %%edi,20(%%eax) \n \t "
" popl %%edx \n \t "
" movl %%edx,0(%%eax) \n \t "
" lahf \n \t "
" shrl $8,%%eax \n \t "
" andl $1,%%eax \n " : " =a " ( rc )
: " a " ( regs )
: " %ebx " , " %ecx " , " %edx " , " %esi " , " %edi " , " memory " ) ;
2008-02-07 11:16:27 +03:00
# endif
2005-06-26 01:54:27 +04:00
if ( rc ! = 0 | | ( regs - > eax & 0xffff ) = = 0xffff | | regs - > eax = = eax )
2005-06-26 01:54:23 +04:00
return - EINVAL ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the bios version . Return the version as an integer corresponding
* to the ascii value , for example " A17 " is returned as 0x00413137 .
*/
static int i8k_get_bios_version ( void )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_BIOS_VERSION , } ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:27 +04:00
return i8k_smm ( & regs ) ? : regs . eax ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the Fn key status .
*/
static int i8k_get_fn_status ( void )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_FN_STATUS , } ;
2005-06-26 01:54:23 +04:00
int rc ;
2005-06-26 01:54:27 +04:00
if ( ( rc = i8k_smm ( & regs ) ) < 0 )
2005-06-26 01:54:23 +04:00
return rc ;
switch ( ( regs . eax > > I8K_FN_SHIFT ) & I8K_FN_MASK ) {
case I8K_FN_UP :
return I8K_VOL_UP ;
case I8K_FN_DOWN :
return I8K_VOL_DOWN ;
case I8K_FN_MUTE :
return I8K_VOL_MUTE ;
default :
return 0 ;
}
2005-04-17 02:20:36 +04:00
}
/*
* Read the power status .
*/
static int i8k_get_power_status ( void )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_POWER_STATUS , } ;
2005-06-26 01:54:23 +04:00
int rc ;
2005-06-26 01:54:27 +04:00
if ( ( rc = i8k_smm ( & regs ) ) < 0 )
2005-06-26 01:54:23 +04:00
return rc ;
2005-06-26 01:54:27 +04:00
return ( regs . eax & 0xff ) = = I8K_POWER_AC ? I8K_AC : I8K_BATTERY ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the fan status .
*/
static int i8k_get_fan_status ( int fan )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_GET_FAN , } ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
regs . ebx = fan & 0xff ;
2005-06-26 01:54:27 +04:00
return i8k_smm ( & regs ) ? : regs . eax & 0xff ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the fan speed in RPM .
*/
static int i8k_get_fan_speed ( int fan )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_GET_SPEED , } ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
regs . ebx = fan & 0xff ;
2008-05-01 15:34:58 +04:00
return i8k_smm ( & regs ) ? : ( regs . eax & 0xffff ) * fan_mult ;
2005-04-17 02:20:36 +04:00
}
/*
* Set the fan speed ( off , low , high ) . Returns the new fan status .
*/
static int i8k_set_fan ( int fan , int speed )
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_SET_FAN , } ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
speed = ( speed < 0 ) ? 0 : ( ( speed > I8K_FAN_MAX ) ? I8K_FAN_MAX : speed ) ;
regs . ebx = ( fan & 0xff ) | ( speed < < 8 ) ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:27 +04:00
return i8k_smm ( & regs ) ? : i8k_get_fan_status ( fan ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Read the cpu temperature .
*/
2005-06-26 01:54:28 +04:00
static int i8k_get_temp ( int sensor )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:27 +04:00
struct smm_regs regs = { . eax = I8K_SMM_GET_TEMP , } ;
2005-06-26 01:54:23 +04:00
int rc ;
int temp ;
2005-04-17 02:20:36 +04:00
# ifdef I8K_TEMPERATURE_BUG
2005-06-26 01:54:27 +04:00
static int prev ;
2005-04-17 02:20:36 +04:00
# endif
2005-06-26 01:54:28 +04:00
regs . ebx = sensor & 0xff ;
2005-06-26 01:54:27 +04:00
if ( ( rc = i8k_smm ( & regs ) ) < 0 )
2005-06-26 01:54:23 +04:00
return rc ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
temp = regs . eax & 0xff ;
2005-04-17 02:20:36 +04:00
# ifdef I8K_TEMPERATURE_BUG
2005-06-26 01:54:23 +04:00
/*
* Sometimes the temperature sensor returns 0x99 , which is out of range .
* In this case we return ( once ) the previous cached value . For example :
# 1003655137 00000058 00005a4b
# 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
# 1003655139 00000054 00005c52
*/
if ( temp > I8K_MAX_TEMP ) {
temp = prev ;
prev = I8K_MAX_TEMP ;
} else {
prev = temp ;
}
2005-04-17 02:20:36 +04:00
# endif
2005-06-26 01:54:23 +04:00
return temp ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:28 +04:00
static int i8k_get_dell_signature ( int req_fn )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:28 +04:00
struct smm_regs regs = { . eax = req_fn , } ;
2005-06-26 01:54:23 +04:00
int rc ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:27 +04:00
if ( ( rc = i8k_smm ( & regs ) ) < 0 )
2005-06-26 01:54:23 +04:00
return rc ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:27 +04:00
return regs . eax = = 1145651527 & & regs . edx = = 1145392204 ? 0 : - 1 ;
2005-04-17 02:20:36 +04:00
}
static int i8k_ioctl ( struct inode * ip , struct file * fp , unsigned int cmd ,
unsigned long arg )
{
2005-06-26 01:54:25 +04:00
int val = 0 ;
2005-06-26 01:54:23 +04:00
int speed ;
unsigned char buff [ 16 ] ;
int __user * argp = ( int __user * ) arg ;
if ( ! argp )
return - EINVAL ;
switch ( cmd ) {
case I8K_BIOS_VERSION :
val = i8k_get_bios_version ( ) ;
break ;
case I8K_MACHINE_ID :
memset ( buff , 0 , 16 ) ;
2005-06-26 01:54:25 +04:00
strlcpy ( buff , i8k_get_dmi_data ( DMI_PRODUCT_SERIAL ) , sizeof ( buff ) ) ;
2005-06-26 01:54:23 +04:00
break ;
case I8K_FN_STATUS :
val = i8k_get_fn_status ( ) ;
break ;
case I8K_POWER_STATUS :
val = i8k_get_power_status ( ) ;
break ;
case I8K_GET_TEMP :
2005-06-26 01:54:28 +04:00
val = i8k_get_temp ( 0 ) ;
2005-06-26 01:54:23 +04:00
break ;
case I8K_GET_SPEED :
2005-06-26 01:54:27 +04:00
if ( copy_from_user ( & val , argp , sizeof ( int ) ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
val = i8k_get_fan_speed ( val ) ;
break ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
case I8K_GET_FAN :
2005-06-26 01:54:27 +04:00
if ( copy_from_user ( & val , argp , sizeof ( int ) ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
val = i8k_get_fan_status ( val ) ;
break ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
case I8K_SET_FAN :
2005-06-26 01:54:27 +04:00
if ( restricted & & ! capable ( CAP_SYS_ADMIN ) )
2005-06-26 01:54:23 +04:00
return - EPERM ;
2005-06-26 01:54:27 +04:00
if ( copy_from_user ( & val , argp , sizeof ( int ) ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
if ( copy_from_user ( & speed , argp + 1 , sizeof ( int ) ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
val = i8k_set_fan ( val , speed ) ;
break ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
default :
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:27 +04:00
if ( val < 0 )
2005-06-26 01:54:23 +04:00
return val ;
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
switch ( cmd ) {
case I8K_BIOS_VERSION :
2005-06-26 01:54:27 +04:00
if ( copy_to_user ( argp , & val , 4 ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
break ;
case I8K_MACHINE_ID :
2005-06-26 01:54:27 +04:00
if ( copy_to_user ( argp , buff , 16 ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
break ;
default :
2005-06-26 01:54:27 +04:00
if ( copy_to_user ( argp , & val , sizeof ( int ) ) )
2005-06-26 01:54:23 +04:00
return - EFAULT ;
2005-06-26 01:54:27 +04:00
2005-06-26 01:54:23 +04:00
break ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
/*
* Print the information for / proc / i8k .
*/
2005-06-26 01:54:26 +04:00
static int i8k_proc_show ( struct seq_file * seq , void * offset )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:26 +04:00
int fn_key , cpu_temp , ac_power ;
2005-06-26 01:54:23 +04:00
int left_fan , right_fan , left_speed , right_speed ;
2007-10-20 01:21:04 +04:00
cpu_temp = i8k_get_temp ( 0 ) ; /* 11100 µs */
left_fan = i8k_get_fan_status ( I8K_FAN_LEFT ) ; /* 580 µs */
right_fan = i8k_get_fan_status ( I8K_FAN_RIGHT ) ; /* 580 µs */
left_speed = i8k_get_fan_speed ( I8K_FAN_LEFT ) ; /* 580 µs */
right_speed = i8k_get_fan_speed ( I8K_FAN_RIGHT ) ; /* 580 µs */
fn_key = i8k_get_fn_status ( ) ; /* 750 µs */
2005-06-26 01:54:27 +04:00
if ( power_status )
2007-10-20 01:21:04 +04:00
ac_power = i8k_get_power_status ( ) ; /* 14700 µs */
2005-06-26 01:54:27 +04:00
else
2005-06-26 01:54:23 +04:00
ac_power = - 1 ;
/*
* Info :
*
* 1 ) Format version ( this will change if format changes )
* 2 ) BIOS version
* 3 ) BIOS machine ID
* 4 ) Cpu temperature
* 5 ) Left fan status
* 6 ) Right fan status
* 7 ) Left fan speed
* 8 ) Right fan speed
* 9 ) AC power
* 10 ) Fn Key status
*/
2005-06-26 01:54:26 +04:00
return seq_printf ( seq , " %s %s %s %d %d %d %d %d %d %d \n " ,
I8K_PROC_FMT ,
bios_version ,
2005-11-12 08:55:15 +03:00
i8k_get_dmi_data ( DMI_PRODUCT_SERIAL ) ,
2005-06-26 01:54:26 +04:00
cpu_temp ,
left_fan , right_fan , left_speed , right_speed ,
ac_power , fn_key ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:26 +04:00
static int i8k_open_fs ( struct inode * inode , struct file * file )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:26 +04:00
return single_open ( file , i8k_proc_show , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:25 +04:00
static struct dmi_system_id __initdata i8k_dmi_table [ ] = {
{
. ident = " Dell Inspiron " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Computer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Inspiron " ) ,
} ,
} ,
{
. ident = " Dell Latitude " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Computer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Latitude " ) ,
} ,
} ,
2005-06-26 01:54:28 +04:00
{
. ident = " Dell Inspiron 2 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Inspiron " ) ,
} ,
} ,
{
. ident = " Dell Latitude 2 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Latitude " ) ,
} ,
} ,
2008-02-06 12:37:47 +03:00
{ /* UK Inspiron 6400 */
. ident = " Dell Inspiron 3 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " MM061 " ) ,
} ,
} ,
2008-02-07 11:16:31 +03:00
{
. ident = " Dell Inspiron 3 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " MP061 " ) ,
} ,
} ,
2009-01-02 19:19:13 +03:00
{
. ident = " Dell Precision " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Precision " ) ,
} ,
} ,
2009-01-02 19:19:23 +03:00
{
. ident = " Dell Vostro " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Vostro " ) ,
} ,
} ,
{ }
2005-06-26 01:54:25 +04:00
} ;
2005-04-17 02:20:36 +04:00
/*
* Probe for the presence of a supported laptop .
*/
static int __init i8k_probe ( void )
{
2005-06-26 01:54:23 +04:00
char buff [ 4 ] ;
int version ;
2005-04-17 02:20:36 +04:00
/*
2005-06-26 01:54:23 +04:00
* Get DMI information
2005-04-17 02:20:36 +04:00
*/
2005-06-26 01:54:25 +04:00
if ( ! dmi_check_system ( i8k_dmi_table ) ) {
if ( ! ignore_dmi & & ! force )
return - ENODEV ;
printk ( KERN_INFO " i8k: not running on a supported Dell system. \n " ) ;
2005-06-26 01:54:23 +04:00
printk ( KERN_INFO " i8k: vendor=%s, model=%s, version=%s \n " ,
2005-06-26 01:54:25 +04:00
i8k_get_dmi_data ( DMI_SYS_VENDOR ) ,
i8k_get_dmi_data ( DMI_PRODUCT_NAME ) ,
i8k_get_dmi_data ( DMI_BIOS_VERSION ) ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:23 +04:00
2005-06-26 01:54:25 +04:00
strlcpy ( bios_version , i8k_get_dmi_data ( DMI_BIOS_VERSION ) , sizeof ( bios_version ) ) ;
2005-04-17 02:20:36 +04:00
/*
2005-06-26 01:54:23 +04:00
* Get SMM Dell signature
2005-04-17 02:20:36 +04:00
*/
2005-06-26 01:54:28 +04:00
if ( i8k_get_dell_signature ( I8K_SMM_GET_DELL_SIG1 ) & &
i8k_get_dell_signature ( I8K_SMM_GET_DELL_SIG2 ) ) {
2005-06-26 01:54:25 +04:00
printk ( KERN_ERR " i8k: unable to get SMM Dell signature \n " ) ;
if ( ! force )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:23 +04:00
/*
* Get SMM BIOS version .
*/
version = i8k_get_bios_version ( ) ;
if ( version < = 0 ) {
2005-06-26 01:54:25 +04:00
printk ( KERN_WARNING " i8k: unable to get SMM BIOS version \n " ) ;
2005-06-26 01:54:23 +04:00
} else {
buff [ 0 ] = ( version > > 16 ) & 0xff ;
buff [ 1 ] = ( version > > 8 ) & 0xff ;
buff [ 2 ] = ( version ) & 0xff ;
buff [ 3 ] = ' \0 ' ;
/*
* If DMI BIOS version is unknown use SMM BIOS version .
*/
2005-06-26 01:54:25 +04:00
if ( ! dmi_get_system_info ( DMI_BIOS_VERSION ) )
strlcpy ( bios_version , buff , sizeof ( bios_version ) ) ;
2005-06-26 01:54:23 +04:00
/*
* Check if the two versions match .
*/
2005-06-26 01:54:25 +04:00
if ( strncmp ( buff , bios_version , sizeof ( bios_version ) ) ! = 0 )
printk ( KERN_WARNING " i8k: BIOS version mismatch: %s != %s \n " ,
buff , bios_version ) ;
2005-06-26 01:54:23 +04:00
}
2005-04-17 02:20:36 +04:00
2005-06-26 01:54:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:27 +04:00
static int __init i8k_init ( void )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:23 +04:00
struct proc_dir_entry * proc_i8k ;
/* Are we running on an supported laptop? */
2005-06-26 01:54:25 +04:00
if ( i8k_probe ( ) )
2005-06-26 01:54:23 +04:00
return - ENODEV ;
/* Register the proc entry */
2008-04-29 12:02:34 +04:00
proc_i8k = proc_create ( " i8k " , 0 , NULL , & i8k_fops ) ;
2005-06-26 01:54:26 +04:00
if ( ! proc_i8k )
2005-06-26 01:54:23 +04:00
return - ENOENT ;
2005-06-26 01:54:26 +04:00
2005-06-26 01:54:23 +04:00
printk ( KERN_INFO
" Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org) \n " ,
I8K_VERSION ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:27 +04:00
static void __exit i8k_exit ( void )
2005-04-17 02:20:36 +04:00
{
2005-06-26 01:54:23 +04:00
remove_proc_entry ( " i8k " , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2005-06-26 01:54:27 +04:00
module_init ( i8k_init ) ;
module_exit ( i8k_exit ) ;