123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520 |
- #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
- };
- #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(0x8e, 0xe4);
- scan = inb(0xe5);
- local_irq_restore(flags);
- }
- return (int) scan;
- }
- #endif
- static int tosh_emulate_fan(SMMRegisters *regs)
- {
- unsigned long eax,ecx,flags;
- unsigned char al;
- eax = regs->eax & 0xff00;
- ecx = regs->ecx & 0xffff;
-
- if (tosh_id==0xfccb) {
- if (eax==0xfe00) {
-
- local_irq_save(flags);
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = (unsigned int) (al & 0x01);
- }
- if ((eax==0xff00) && (ecx==0x0000)) {
-
- local_irq_save(flags);
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- outb(0xbe, 0xe4);
- outb (al | 0x01, 0xe5);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = 0x00;
- }
- if ((eax==0xff00) && (ecx==0x0001)) {
-
- local_irq_save(flags);
- outb(0xbe, 0xe4);
- al = inb(0xe5);
- outb(0xbe, 0xe4);
- outb(al & 0xfe, 0xe5);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = 0x01;
- }
- }
-
- if (tosh_id==0xfccc) {
- if (eax==0xfe00) {
-
- local_irq_save(flags);
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = al & 0x01;
- }
- if ((eax==0xff00) && (ecx==0x0000)) {
-
- local_irq_save(flags);
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = 0x00;
- }
- if ((eax==0xff00) && (ecx==0x0001)) {
-
- local_irq_save(flags);
- outb(0xe0, 0xe4);
- al = inb(0xe5);
- outw(0xe0 | ((al | 0x01) << 8), 0xe4);
- local_irq_restore(flags);
- regs->eax = 0x00;
- regs->ecx = 0x01;
- }
- }
- return 0;
- }
- 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 & 0xff00;
- bx = regs.ebx & 0xffff;
-
- if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
- return -EINVAL;
-
- mutex_lock(&tosh_mutex);
- if (tosh_fan==1) {
- if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
- 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;
- }
- #ifdef CONFIG_PROC_FS
- static int proc_toshiba_show(struct seq_file *m, void *v)
- {
- int key;
- key = tosh_fn_status();
-
- seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
- tosh_id,
- (tosh_sci & 0xff00)>>8,
- tosh_sci & 0xff,
- (tosh_bios & 0xff00)>>8,
- tosh_bios & 0xff,
- tosh_date,
- key);
- return 0;
- }
- #endif
- 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;
- }
- static int tosh_get_machine_id(void __iomem *bios)
- {
- int id;
- SMMRegisters regs;
- unsigned short bx,cx;
- unsigned long address;
- id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
-
- if (id==0xfc2f) {
-
- regs.eax = 0xc000;
- regs.ebx = 0x0000;
- regs.ecx = 0x0000;
- tosh_smm(®s);
- bx = (unsigned short) (regs.ebx & 0xffff);
-
- #if TOSH_DEBUG
- pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
- #endif
- bx = 0xe6f5;
-
- address = bx;
- cx = readw(bios + address);
- address = 9+bx+cx;
- cx = readw(bios + address);
- address = 0xa+cx;
- cx = readw(bios + address);
-
- id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
- }
- return id;
- }
- static 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;
- void __iomem *bios = ioremap(0xf0000, 0x10000);
- if (!bios)
- return -ENOMEM;
-
- for (i=0;i<7;i++) {
- if (readb(bios+0xe010+i)!=signature[i]) {
- pr_err("toshiba: not a supported Toshiba laptop\n");
- iounmap(bios);
- return -ENODEV;
- }
- }
-
- regs.eax = 0xf0f0;
- regs.ebx = 0x0000;
- regs.ecx = 0x0000;
- flag = tosh_smm(®s);
-
- if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
- pr_err("toshiba: not a supported Toshiba laptop\n");
- iounmap(bios);
- return -ENODEV;
- }
-
- tosh_sci = regs.edx & 0xffff;
-
- tosh_id = tosh_get_machine_id(bios);
-
- major = readb(bios+0xe009)-'0';
- minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
- tosh_bios = (major*0x100)+minor;
-
- day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
- month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
- year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
- tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
- | ((day & 0x1f)<<1);
-
-
- if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
- tosh_fan = 1;
- iounmap(bios);
- return 0;
- }
- static int __init toshiba_init(void)
- {
- int retval;
-
- if (tosh_probe())
- return -ENODEV;
- pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
-
- if (tosh_fn==0x00)
- tosh_set_fn_port();
-
- 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);
|