toshiba.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
- /* 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
- * 0xfc02: Scott Eisert <scott.e@sky-eye.com>
- * 0xfc04: Steve VanDevender <stevev@efn.org>
- * 0xfc08: Garth Berry <garth@itsbruce.net>
- * 0xfc0a: Egbert Eich <eich@xfree86.org>
- * 0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil>
- * 0xfc11: Spencer Olson <solson@novell.com>
- * 0xfc13: Claudius Frankewitz <kryp@gmx.de>
- * 0xfc15: Tom May <tom@you-bastards.com>
- * 0xfc17: Dave Konrad <konrad@xenia.it>
- * 0xfc1a: George Betzos <betzos@engr.colostate.edu>
- * 0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp>
- * 0xfc1d: Arthur Liu <armie@slap.mine.nu>
- * 0xfc5a: Jacques L'helgoualc'h <lhh@free.fr>
- * 0xfcd1: 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.
- *
- * 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.
- *
- * 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/version.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/types.h>
- #include <linux/fcntl.h>
- #include <linux/miscdevice.h>
- #include <linux/ioport.h>
- #include <asm/io.h>
- #include <asm/uaccess.h>
- #include <linux/init.h>
- #include <linux/stat.h>
- #include <linux/proc_fs.h>
- #include <linux/toshiba.h>
- #define TOSH_MINOR_DEV 181
- static int tosh_id = 0x0000;
- static int tosh_bios = 0x0000;
- static int tosh_date = 0x0000;
- static int tosh_sci = 0x0000;
- static int tosh_fan = 0;
- static int tosh_fn = 0;
- MODULE_PARM(tosh_fn, "i");
- MODULE_LICENSE("GPL");
- static int tosh_get_info(char *, char **, off_t, int);
- static int tosh_ioctl(struct inode *, struct file *, unsigned int,
- unsigned long);
- static struct file_operations tosh_fops = {
- owner: THIS_MODULE,
- ioctl: tosh_ioctl,
- };
- static struct miscdevice tosh_device = {
- TOSH_MINOR_DEV,
- "toshiba",
- &tosh_fops
- };
- /*
- * Read the Fn key status
- */
- static int tosh_fn_status(void)
- {
- unsigned char scan;
- unsigned long flags;
- if (tosh_fn!=0) {
- scan = inb(tosh_fn);
- } else {
- save_flags(flags);
- cli();
- outb(0x8e, 0xe4);
- scan = inb(0xe5);
- restore_flags(flags);
- }
- return (int) scan;
- }
- /*
- * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function
- */
- static int tosh_emulate_fan(SMMRegisters *regs)
- {
- unsigned long eax,ecx,flags;
- unsigned char al;
- eax = regs->eax & 0xff00;
- ecx = regs->ecx & 0xffff;
- /* Portage 610CT */
- if (tosh_id==0xfccb) {
- if (eax==0xfe00) {
- /* fan status */
- save_flags(flags);
- cli();
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = (unsigned int) (al & 0x01);
- }
- if ((eax==0xff00) && (ecx==0x0000)) {
- /* fan off */
- save_flags(flags);
- cli();
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- outb(0xbe, 0xe4);
- outb (al | 0x01, 0xe5);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = 0x00;
- }
- if ((eax==0xff00) && (ecx==0x0001)) {
- /* fan on */
- save_flags(flags);
- cli();
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- outb(0xbe, 0xe4);
- outb(al & 0xfe, 0xe5);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = 0x01;
- }
- }
- /* Tecra 700CS/CDT */
- if (tosh_id==0xfccc) {
- if (eax==0xfe00) {
- /* fan status */
- save_flags(flags);
- cli();
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = al & 0x01;
- }
- if ((eax==0xff00) && (ecx==0x0000)) {
- /* fan off */
- save_flags(flags);
- cli();
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = 0x00;
- }
- if ((eax==0xff00) && (ecx==0x0001)) {
- /* fan on */
- save_flags(flags);
- cli();
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- outw(0xe0 | ((al | 0x01) << 8), 0xe4);
- restore_flags(flags);
- regs->eax = 0x00;
- regs->ecx = 0x01;
- }
- }
- return 0;
- }
- /*
- * Put the laptop into System Management Mode
- */
- static int tosh_smm(SMMRegisters *regs)
- {
- int eax;
- asm ("# load the values into the registersnt"
- "pushl %%eaxnt"
- "movl 0(%%eax),%%edxnt"
- "push %%edxnt"
- "movl 4(%%eax),%%ebxnt"
- "movl 8(%%eax),%%ecxnt"
- "movl 12(%%eax),%%edxnt"
- "movl 16(%%eax),%%esint"
- "movl 20(%%eax),%%edint"
- "popl %%eaxnt"
- "# call the System Management modent"
- "inb $0xb2,%%alnt"
- "# fill out the memory with the values in the registersnt"
- "xchgl %%eax,(%%esp)nt"
- "movl %%ebx,4(%%eax)nt"
- "movl %%ecx,8(%%eax)nt"
- "movl %%edx,12(%%eax)nt"
- "movl %%esi,16(%%eax)nt"
- "movl %%edi,20(%%eax)nt"
- "popl %%edxnt"
- "movl %%edx,0(%%eax)nt"
- "# setup the return value to the carry flagnt"
- "lahfnt"
- "shrl $8,%%eaxnt"
- "andl $1,%%eaxn"
- : "=a" (eax)
- : "a" (regs)
- : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
- return eax;
- }
- static int tosh_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
- unsigned long arg)
- {
- SMMRegisters regs;
- unsigned short ax,bx;
- int err;
- if (!arg)
- return -EINVAL;
- if (copy_from_user(®s, (SMMRegisters *) arg, sizeof(SMMRegisters)))
- return -EFAULT;
- switch (cmd) {
- case TOSH_SMM:
- ax = regs.eax & 0xff00;
- bx = regs.ebx & 0xffff;
- /* block HCI calls to read/write memory & PCI devices */
- if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
- return -EINVAL;
- /* do we need to emulate the fan ? */
- if (tosh_fan==1) {
- if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
- err = tosh_emulate_fan(®s);
- break;
- }
- }
- err = tosh_smm(®s);
- break;
- default:
- return -EINVAL;
- }
- if (copy_to_user((SMMRegisters *) arg, ®s, sizeof(SMMRegisters)))
- return -EFAULT;
- return (err==0) ? 0:-EINVAL;
- }
- /*
- * Print the information for /proc/toshiba
- */
- int tosh_get_info(char *buffer, char **start, off_t fpos, int length)
- {
- char *temp;
- int key;
- temp = buffer;
- 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
- */
- temp += sprintf(temp, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02xn",
- tosh_id,
- (tosh_sci & 0xff00)>>8,
- tosh_sci & 0xff,
- (tosh_bios & 0xff00)>>8,
- tosh_bios & 0xff,
- tosh_date,
- key);
- return temp-buffer;
- }
- /*
- * Determine which port to use for the Fn key status
- */
- static void tosh_set_fn_port(void)
- {
- switch (tosh_id) {
- case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
- case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
- case 0xfc5a:
- tosh_fn = 0x62;
- break;
- case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
- case 0xfce2:
- tosh_fn = 0x68;
- break;
- default:
- tosh_fn = 0x00;
- break;
- }
- return;
- }
- /*
- * Get the machine identification number of the current model
- */
- static int tosh_get_machine_id(void)
- {
- int id;
- SMMRegisters regs;
- unsigned short bx,cx;
- unsigned long address;
- id = (0x100*(int) isa_readb(0xffffe))+((int) isa_readb(0xffffa));
-
- /* do we have a SCTTable machine identication number on our hands */
- if (id==0xfc2f) {
- /* start by getting a pointer into the BIOS */
- regs.eax = 0xc000;
- regs.ebx = 0x0000;
- regs.ecx = 0x0000;
- tosh_smm(®s);
- bx = (unsigned short) (regs.ebx & 0xffff);
- /* At this point in the Toshiba routines under MS Windows
- the bx register holds 0xe6f5. 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 430CDT,
- Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
- #if TOSH_DEBUG
- printk("toshiba: debugging ID ebx=0x%04xn", regs.ebx);
- #endif
- bx = 0xe6f5;
- /* now twiddle with our pointer a bit */
- address = 0x000f0000+bx;
- cx = isa_readw(address);
- address = 0x000f0009+bx+cx;
- cx = isa_readw(address);
- address = 0x000f000a+cx;
- cx = isa_readw(address);
- /* now construct our machine identification number */
- id = ((cx & 0xff)<<8)+((cx & 0xff00)>>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.
- */
- int tosh_probe(void)
- {
- int i,major,minor,day,year,month,flag;
- unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
- SMMRegisters regs;
- /* 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 (isa_readb(0xfe010+i)!=signature[i]) {
- printk("toshiba: not a supported Toshiba laptopn");
- return -ENODEV;
- }
- }
- /* call the Toshiba SCI support check routine */
-
- regs.eax = 0xf0f0;
- regs.ebx = 0x0000;
- regs.ecx = 0x0000;
- flag = tosh_smm(®s);
- /* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
- if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
- printk("toshiba: not a supported Toshiba laptopn");
- return -ENODEV;
- }
- /* if we get this far then we are running on a Toshiba (probably)! */
- tosh_sci = regs.edx & 0xffff;
-
- /* next get the machine ID of the current laptop */
- tosh_id = tosh_get_machine_id();
- /* get the BIOS version */
- major = isa_readb(0xfe009)-'0';
- minor = ((isa_readb(0xfe00b)-'0')*10)+(isa_readb(0xfe00c)-'0');
- tosh_bios = (major*0x100)+minor;
- /* get the BIOS date */
- day = ((isa_readb(0xffff5)-'0')*10)+(isa_readb(0xffff6)-'0');
- month = ((isa_readb(0xffff8)-'0')*10)+(isa_readb(0xffff9)-'0');
- year = ((isa_readb(0xffffb)-'0')*10)+(isa_readb(0xffffc)-'0');
- tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
- | ((day & 0x1f)<<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 0x60-0x6f and the pic driver grabs
- 0xa0-0xbf 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==0xfccb) || (tosh_id==0xfccc))
- tosh_fan = 1;
- return 0;
- }
- int __init tosh_init(void)
- {
- /* are we running on a Toshiba laptop */
- if (tosh_probe()!=0)
- return -EIO;
- printk(KERN_INFO "Toshiba System Managment Mode driver v"
- TOSH_VERSION"n");
- /* set the port to use for Fn status if not specified as a parameter */
- if (tosh_fn==0x00)
- tosh_set_fn_port();
- /* register the device file */
- misc_register(&tosh_device);
- /* register the proc entry */
- create_proc_info_entry("toshiba", 0, NULL, tosh_get_info);
- return 0;
- }
- #ifdef MODULE
- int init_module(void)
- {
- return tosh_init();
- }
- void cleanup_module(void)
- {
- /* remove the proc entry */
- remove_proc_entry("toshiba", NULL);
- /* unregister the device file */
- misc_deregister(&tosh_device);
- }
- #endif