Quelle toshiba.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-or-later
/* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops
*
* Copyright ( c ) 1996 - 2001 Jonathan A . Buzzard ( jonathan @ buzzard . org . uk )
*
* Valuable assistance and patches from :
* Tom May < tom @ you - bastards . com >
* Rob Napier < rnapier @ employees . org >
*
* Fn status port numbers for machine ID ' s courtesy of
* 0 xfc02 : Scott Eisert < scott . e @ sky - eye . com >
* 0 xfc04 : Steve VanDevender < stevev @ efn . org >
* 0 xfc08 : Garth Berry < garth @ itsbruce . net >
* 0 xfc0a : Egbert Eich < eich @ xfree86 . org >
* 0 xfc10 : Andrew Lofthouse < Andrew . Lofthouse @ robins . af . mil >
* 0 xfc11 : Spencer Olson < solson @ novell . com >
* 0 xfc13 : Claudius Frankewitz < kryp @ gmx . de >
* 0 xfc15 : Tom May < tom @ you - bastards . com >
* 0 xfc17 : Dave Konrad < konrad @ xenia . it >
* 0 xfc1a : George Betzos < betzos @ engr . colostate . edu >
* 0 xfc1b : Munemasa Wada < munemasa @ jnovel . co . jp >
* 0 xfc1d : Arthur Liu < armie @ slap . mine . nu >
* 0 xfc5a : Jacques L ' helgoualc ' h < lhh @ free . fr >
* 0 xfcd1 : Mr . Dave Konrad < konrad @ xenia . it >
*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
*
* This code is covered by the GNU GPL and you are free to make any
* changes you wish to it under the terms of the license . However the
* code has the potential to render your computer and / or someone else ' s
* unusable . Please proceed with care when modifying the code .
*
* Note : Unfortunately the laptop hardware can close the System Configuration
* Interface on it ' s own accord . It is therefore necessary for * all *
* programs using this driver to be aware that * any * SCI call can fail at
* * any * time . It is up to any program to be aware of this eventuality
* and take appropriate steps .
*
* The information used to write this driver has been obtained by reverse
* engineering the software supplied by Toshiba for their portable computers in
* strict accordance with the European Council Directive 92 / 250 / EEC on the legal
* protection of computer programs , and it ' s implementation into English Law by
* the Copyright ( Computer Programs ) Regulations 1992 ( S . I . 1992 No . 3233 ) .
*/
#define TOSH_VERSION "1.11 26/9/2001"
#define TOSH_DEBUG 0
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <asm /io.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/mutex.h>
#include <linux/toshiba.h>
MODULE_LICENSE("GPL" );
MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>" );
MODULE_DESCRIPTION("Toshiba laptop SMM driver" );
static DEFINE_MUTEX(tosh_mutex);
static int tosh_fn;
module_param_named(fn, tosh_fn, int , 0 );
MODULE_PARM_DESC(fn, "User specified Fn key detection port" );
static int tosh_id;
static int tosh_bios;
static int tosh_date;
static int tosh_sci;
static int tosh_fan;
static long tosh_ioctl(struct file *, unsigned int ,
unsigned long );
static const struct file_operations tosh_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = tosh_ioctl,
.llseek = noop_llseek,
};
static struct miscdevice tosh_device = {
TOSH_MINOR_DEV,
"toshiba" ,
&tosh_fops
};
/*
* Read the Fn key status
*/
#ifdef CONFIG_PROC_FS
static int tosh_fn_status(void )
{
unsigned char scan;
unsigned long flags;
if (tosh_fn!=0 ) {
scan = inb(tosh_fn);
} else {
local_irq_save(flags);
outb(0 x8e, 0 xe4);
scan = inb(0 xe5);
local_irq_restore(flags);
}
return (int ) scan;
}
#endif
/*
* For the Portage 610 CT and the Tecra 700 CS / 700 CDT emulate the HCI fan function
*/
static int tosh_emulate_fan(SMMRegisters *regs)
{
unsigned long eax,ecx,flags;
unsigned char al;
eax = regs->eax & 0 xff00;
ecx = regs->ecx & 0 xffff;
/* Portage 610CT */
if (tosh_id==0 xfccb) {
if (eax==0 xfe00) {
/* fan status */
local_irq_save(flags);
outb(0 xbe, 0 xe4);
al = inb(0 xe5);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = (unsigned int ) (al & 0 x01);
}
if ((eax==0 xff00) && (ecx==0 x0000)) {
/* fan off */
local_irq_save(flags);
outb(0 xbe, 0 xe4);
al = inb(0 xe5);
outb(0 xbe, 0 xe4);
outb (al | 0 x01, 0 xe5);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = 0 x00;
}
if ((eax==0 xff00) && (ecx==0 x0001)) {
/* fan on */
local_irq_save(flags);
outb(0 xbe, 0 xe4);
al = inb(0 xe5);
outb(0 xbe, 0 xe4);
outb(al & 0 xfe, 0 xe5);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = 0 x01;
}
}
/* Tecra 700CS/CDT */
if (tosh_id==0 xfccc) {
if (eax==0 xfe00) {
/* fan status */
local_irq_save(flags);
outb(0 xe0, 0 xe4);
al = inb(0 xe5);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = al & 0 x01;
}
if ((eax==0 xff00) && (ecx==0 x0000)) {
/* fan off */
local_irq_save(flags);
outb(0 xe0, 0 xe4);
al = inb(0 xe5);
outw(0 xe0 | ((al & 0 xfe) << 8 ), 0 xe4);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = 0 x00;
}
if ((eax==0 xff00) && (ecx==0 x0001)) {
/* fan on */
local_irq_save(flags);
outb(0 xe0, 0 xe4);
al = inb(0 xe5);
outw(0 xe0 | ((al | 0 x01) << 8 ), 0 xe4);
local_irq_restore(flags);
regs->eax = 0 x00;
regs->ecx = 0 x01;
}
}
return 0 ;
}
/*
* Put the laptop into System Management Mode
*/
int tosh_smm(SMMRegisters *regs)
{
int eax;
asm ("# load the values into the registers\n\t" \
"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" \
"# call the System Management mode\n\t" \
"inb $0xb2,%%al\n\t"
"# fill out the memory with the values in the registers\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" \
"# setup the return value to the carry flag\n\t" \
"lahf\n\t" \
"shrl $8,%%eax\n\t" \
"andl $1,%%eax\n" \
: "=a" (eax)
: "a" (regs)
: "%ebx" , "%ecx" , "%edx" , "%esi" , "%edi" , "memory" );
return eax;
}
EXPORT_SYMBOL(tosh_smm);
static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
SMMRegisters regs;
SMMRegisters __user *argp = (SMMRegisters __user *)arg;
unsigned short ax,bx;
int err;
if (!argp)
return -EINVAL;
if (copy_from_user(®s, argp, sizeof (SMMRegisters)))
return -EFAULT;
switch (cmd) {
case TOSH_SMM:
ax = regs.eax & 0 xff00;
bx = regs.ebx & 0 xffff;
/* block HCI calls to read/write memory & PCI devices */
if (((ax==0 xff00) || (ax==0 xfe00)) && (bx>0 x0069))
return -EINVAL;
/* do we need to emulate the fan ? */
mutex_lock(&tosh_mutex);
if (tosh_fan==1 ) {
if (((ax==0 xf300) || (ax==0 xf400)) && (bx==0 x0004)) {
err = tosh_emulate_fan(®s);
mutex_unlock(&tosh_mutex);
break ;
}
}
err = tosh_smm(®s);
mutex_unlock(&tosh_mutex);
break ;
default :
return -EINVAL;
}
if (copy_to_user(argp, ®s, sizeof (SMMRegisters)))
return -EFAULT;
return (err==0 ) ? 0 :-EINVAL;
}
/*
* Print the information for / proc / toshiba
*/
#ifdef CONFIG_PROC_FS
static int proc_toshiba_show(struct seq_file *m, void *v)
{
int key;
key = tosh_fn_status();
/* Arguments
0 ) Linux driver version ( this will change if format changes )
1 ) Machine ID
2 ) SCI version
3 ) BIOS version ( major , minor )
4 ) BIOS date ( in SCI date format )
5 ) Fn Key status
*/
seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n" ,
tosh_id,
(tosh_sci & 0 xff00)>>8 ,
tosh_sci & 0 xff,
(tosh_bios & 0 xff00)>>8 ,
tosh_bios & 0 xff,
tosh_date,
key);
return 0 ;
}
#endif
/*
* Determine which port to use for the Fn key status
*/
static void tosh_set_fn_port(void )
{
switch (tosh_id) {
case 0 xfc02: case 0 xfc04: case 0 xfc09: case 0 xfc0a: case 0 xfc10:
case 0 xfc11: case 0 xfc13: case 0 xfc15: case 0 xfc1a: case 0 xfc1b:
case 0 xfc5a:
tosh_fn = 0 x62;
break ;
case 0 xfc08: case 0 xfc17: case 0 xfc1d: case 0 xfcd1: case 0 xfce0:
case 0 xfce2:
tosh_fn = 0 x68;
break ;
default :
tosh_fn = 0 x00;
break ;
}
return ;
}
/*
* Get the machine identification number of the current model
*/
static int tosh_get_machine_id(void __iomem *bios)
{
int id;
SMMRegisters regs;
unsigned short bx,cx;
unsigned long address;
id = (0 x100*(int ) readb(bios+0 xfffe))+((int ) readb(bios+0 xfffa));
/* do we have a SCTTable machine identication number on our hands */
if (id==0 xfc2f) {
/* start by getting a pointer into the BIOS */
regs.eax = 0 xc000;
regs.ebx = 0 x0000;
regs.ecx = 0 x0000;
tosh_smm(®s);
bx = (unsigned short ) (regs.ebx & 0 xffff);
/* At this point in the Toshiba routines under MS Windows
the bx register holds 0 xe6f5 . However my code is producing
a different value ! For the time being I will just fudge the
value . This has been verified on a Satellite Pro 430 CDT ,
Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
#if TOSH_DEBUG
pr_debug("toshiba: debugging ID ebx=0x%04x\n" , regs.ebx);
#endif
bx = 0 xe6f5;
/* now twiddle with our pointer a bit */
address = bx;
cx = readw(bios + address);
address = 9 +bx+cx;
cx = readw(bios + address);
address = 0 xa+cx;
cx = readw(bios + address);
/* now construct our machine identification number */
id = ((cx & 0 xff)<<8 )+((cx & 0 xff00)>>8 );
}
return id;
}
/*
* Probe for the presence of a Toshiba laptop
*
* returns and non - zero if unable to detect the presence of a Toshiba
* laptop , otherwise zero and determines the Machine ID , BIOS version and
* date , and SCI version .
*/
static int tosh_probe(void )
{
int i,major,minor,day,year,month,flag;
unsigned char signature[7 ] = { 0 x54,0 x4f,0 x53,0 x48,0 x49,0 x42,0 x41 };
SMMRegisters regs;
void __iomem *bios = ioremap(0 xf0000, 0 x10000);
if (!bios)
return -ENOMEM;
/* extra sanity check for the string "TOSHIBA" in the BIOS because
some machines that are not Toshiba's pass the next test */
for (i=0 ;i<7 ;i++) {
if (readb(bios+0 xe010+i)!=signature[i]) {
pr_err("toshiba: not a supported Toshiba laptop\n" );
iounmap(bios);
return -ENODEV;
}
}
/* call the Toshiba SCI support check routine */
regs.eax = 0 xf0f0;
regs.ebx = 0 x0000;
regs.ecx = 0 x0000;
flag = tosh_smm(®s);
/* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
if ((flag==1 ) || ((regs.eax & 0 xff00)==0 x8600)) {
pr_err("toshiba: not a supported Toshiba laptop\n" );
iounmap(bios);
return -ENODEV;
}
/* if we get this far then we are running on a Toshiba (probably)! */
tosh_sci = regs.edx & 0 xffff;
/* next get the machine ID of the current laptop */
tosh_id = tosh_get_machine_id(bios);
/* get the BIOS version */
major = readb(bios+0 xe009)-'0' ;
minor = ((readb(bios+0 xe00b)-'0' )*10 )+(readb(bios+0 xe00c)-'0' );
tosh_bios = (major*0 x100)+minor;
/* get the BIOS date */
day = ((readb(bios+0 xfff5)-'0' )*10 )+(readb(bios+0 xfff6)-'0' );
month = ((readb(bios+0 xfff8)-'0' )*10 )+(readb(bios+0 xfff9)-'0' );
year = ((readb(bios+0 xfffb)-'0' )*10 )+(readb(bios+0 xfffc)-'0' );
tosh_date = (((year-90 ) & 0 x1f)<<10 ) | ((month & 0 xf)<<6 )
| ((day & 0 x1f)<<1 );
/* in theory we should check the ports we are going to use for the
fn key detection ( and the fan on the Portage 610 / Tecra700 ) , and
then request them to stop other drivers using them . However as
the keyboard driver grabs 0 x60 - 0 x6f and the pic driver grabs
0 xa0 - 0 xbf we can ' t . We just have to live dangerously and use the
ports anyway, oh boy! */
/* do we need to emulate the fan? */
if ((tosh_id==0 xfccb) || (tosh_id==0 xfccc))
tosh_fan = 1 ;
iounmap(bios);
return 0 ;
}
static int __init toshiba_init(void )
{
int retval;
/* are we running on a Toshiba laptop */
if (tosh_probe())
return -ENODEV;
pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n" );
/* set the port to use for Fn status if not specified as a parameter */
if (tosh_fn==0 x00)
tosh_set_fn_port();
/* register the device file */
retval = misc_register(&tosh_device);
if (retval < 0 )
return retval;
#ifdef CONFIG_PROC_FS
{
struct proc_dir_entry *pde;
pde = proc_create_single("toshiba" , 0 , NULL, proc_toshiba_show);
if (!pde) {
misc_deregister(&tosh_device);
return -ENOMEM;
}
}
#endif
return 0 ;
}
static void __exit toshiba_exit(void )
{
remove_proc_entry("toshiba" , NULL);
misc_deregister(&tosh_device);
}
module_init(toshiba_init);
module_exit(toshiba_exit);
Messung V0.5 in Prozent C=94 H=97 G=95
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1897691