0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046 #define TOSH_VERSION "1.11 26/9/2001"
0047 #define TOSH_DEBUG 0
0048
0049 #include <linux/module.h>
0050 #include <linux/kernel.h>
0051 #include <linux/types.h>
0052 #include <linux/fcntl.h>
0053 #include <linux/miscdevice.h>
0054 #include <linux/ioport.h>
0055 #include <asm/io.h>
0056 #include <linux/uaccess.h>
0057 #include <linux/init.h>
0058 #include <linux/stat.h>
0059 #include <linux/proc_fs.h>
0060 #include <linux/seq_file.h>
0061 #include <linux/mutex.h>
0062 #include <linux/toshiba.h>
0063
0064 MODULE_LICENSE("GPL");
0065 MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
0066 MODULE_DESCRIPTION("Toshiba laptop SMM driver");
0067
0068 static DEFINE_MUTEX(tosh_mutex);
0069 static int tosh_fn;
0070 module_param_named(fn, tosh_fn, int, 0);
0071 MODULE_PARM_DESC(fn, "User specified Fn key detection port");
0072
0073 static int tosh_id;
0074 static int tosh_bios;
0075 static int tosh_date;
0076 static int tosh_sci;
0077 static int tosh_fan;
0078
0079 static long tosh_ioctl(struct file *, unsigned int,
0080 unsigned long);
0081
0082
0083 static const struct file_operations tosh_fops = {
0084 .owner = THIS_MODULE,
0085 .unlocked_ioctl = tosh_ioctl,
0086 .llseek = noop_llseek,
0087 };
0088
0089 static struct miscdevice tosh_device = {
0090 TOSH_MINOR_DEV,
0091 "toshiba",
0092 &tosh_fops
0093 };
0094
0095
0096
0097
0098 #ifdef CONFIG_PROC_FS
0099 static int tosh_fn_status(void)
0100 {
0101 unsigned char scan;
0102 unsigned long flags;
0103
0104 if (tosh_fn!=0) {
0105 scan = inb(tosh_fn);
0106 } else {
0107 local_irq_save(flags);
0108 outb(0x8e, 0xe4);
0109 scan = inb(0xe5);
0110 local_irq_restore(flags);
0111 }
0112
0113 return (int) scan;
0114 }
0115 #endif
0116
0117
0118
0119
0120
0121 static int tosh_emulate_fan(SMMRegisters *regs)
0122 {
0123 unsigned long eax,ecx,flags;
0124 unsigned char al;
0125
0126 eax = regs->eax & 0xff00;
0127 ecx = regs->ecx & 0xffff;
0128
0129
0130
0131 if (tosh_id==0xfccb) {
0132 if (eax==0xfe00) {
0133
0134 local_irq_save(flags);
0135 outb(0xbe, 0xe4);
0136 al = inb(0xe5);
0137 local_irq_restore(flags);
0138 regs->eax = 0x00;
0139 regs->ecx = (unsigned int) (al & 0x01);
0140 }
0141 if ((eax==0xff00) && (ecx==0x0000)) {
0142
0143 local_irq_save(flags);
0144 outb(0xbe, 0xe4);
0145 al = inb(0xe5);
0146 outb(0xbe, 0xe4);
0147 outb (al | 0x01, 0xe5);
0148 local_irq_restore(flags);
0149 regs->eax = 0x00;
0150 regs->ecx = 0x00;
0151 }
0152 if ((eax==0xff00) && (ecx==0x0001)) {
0153
0154 local_irq_save(flags);
0155 outb(0xbe, 0xe4);
0156 al = inb(0xe5);
0157 outb(0xbe, 0xe4);
0158 outb(al & 0xfe, 0xe5);
0159 local_irq_restore(flags);
0160 regs->eax = 0x00;
0161 regs->ecx = 0x01;
0162 }
0163 }
0164
0165
0166
0167 if (tosh_id==0xfccc) {
0168 if (eax==0xfe00) {
0169
0170 local_irq_save(flags);
0171 outb(0xe0, 0xe4);
0172 al = inb(0xe5);
0173 local_irq_restore(flags);
0174 regs->eax = 0x00;
0175 regs->ecx = al & 0x01;
0176 }
0177 if ((eax==0xff00) && (ecx==0x0000)) {
0178
0179 local_irq_save(flags);
0180 outb(0xe0, 0xe4);
0181 al = inb(0xe5);
0182 outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
0183 local_irq_restore(flags);
0184 regs->eax = 0x00;
0185 regs->ecx = 0x00;
0186 }
0187 if ((eax==0xff00) && (ecx==0x0001)) {
0188
0189 local_irq_save(flags);
0190 outb(0xe0, 0xe4);
0191 al = inb(0xe5);
0192 outw(0xe0 | ((al | 0x01) << 8), 0xe4);
0193 local_irq_restore(flags);
0194 regs->eax = 0x00;
0195 regs->ecx = 0x01;
0196 }
0197 }
0198
0199 return 0;
0200 }
0201
0202
0203
0204
0205
0206 int tosh_smm(SMMRegisters *regs)
0207 {
0208 int eax;
0209
0210 asm ("# load the values into the registers\n\t" \
0211 "pushl %%eax\n\t" \
0212 "movl 0(%%eax),%%edx\n\t" \
0213 "push %%edx\n\t" \
0214 "movl 4(%%eax),%%ebx\n\t" \
0215 "movl 8(%%eax),%%ecx\n\t" \
0216 "movl 12(%%eax),%%edx\n\t" \
0217 "movl 16(%%eax),%%esi\n\t" \
0218 "movl 20(%%eax),%%edi\n\t" \
0219 "popl %%eax\n\t" \
0220 "# call the System Management mode\n\t" \
0221 "inb $0xb2,%%al\n\t"
0222 "# fill out the memory with the values in the registers\n\t" \
0223 "xchgl %%eax,(%%esp)\n\t"
0224 "movl %%ebx,4(%%eax)\n\t" \
0225 "movl %%ecx,8(%%eax)\n\t" \
0226 "movl %%edx,12(%%eax)\n\t" \
0227 "movl %%esi,16(%%eax)\n\t" \
0228 "movl %%edi,20(%%eax)\n\t" \
0229 "popl %%edx\n\t" \
0230 "movl %%edx,0(%%eax)\n\t" \
0231 "# setup the return value to the carry flag\n\t" \
0232 "lahf\n\t" \
0233 "shrl $8,%%eax\n\t" \
0234 "andl $1,%%eax\n" \
0235 : "=a" (eax)
0236 : "a" (regs)
0237 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
0238
0239 return eax;
0240 }
0241 EXPORT_SYMBOL(tosh_smm);
0242
0243
0244 static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
0245 {
0246 SMMRegisters regs;
0247 SMMRegisters __user *argp = (SMMRegisters __user *)arg;
0248 unsigned short ax,bx;
0249 int err;
0250
0251 if (!argp)
0252 return -EINVAL;
0253
0254 if (copy_from_user(®s, argp, sizeof(SMMRegisters)))
0255 return -EFAULT;
0256
0257 switch (cmd) {
0258 case TOSH_SMM:
0259 ax = regs.eax & 0xff00;
0260 bx = regs.ebx & 0xffff;
0261
0262 if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
0263 return -EINVAL;
0264
0265
0266 mutex_lock(&tosh_mutex);
0267 if (tosh_fan==1) {
0268 if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
0269 err = tosh_emulate_fan(®s);
0270 mutex_unlock(&tosh_mutex);
0271 break;
0272 }
0273 }
0274 err = tosh_smm(®s);
0275 mutex_unlock(&tosh_mutex);
0276 break;
0277 default:
0278 return -EINVAL;
0279 }
0280
0281 if (copy_to_user(argp, ®s, sizeof(SMMRegisters)))
0282 return -EFAULT;
0283
0284 return (err==0) ? 0:-EINVAL;
0285 }
0286
0287
0288
0289
0290
0291 #ifdef CONFIG_PROC_FS
0292 static int proc_toshiba_show(struct seq_file *m, void *v)
0293 {
0294 int key;
0295
0296 key = tosh_fn_status();
0297
0298
0299
0300
0301
0302
0303
0304
0305
0306 seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
0307 tosh_id,
0308 (tosh_sci & 0xff00)>>8,
0309 tosh_sci & 0xff,
0310 (tosh_bios & 0xff00)>>8,
0311 tosh_bios & 0xff,
0312 tosh_date,
0313 key);
0314 return 0;
0315 }
0316 #endif
0317
0318
0319
0320
0321
0322 static void tosh_set_fn_port(void)
0323 {
0324 switch (tosh_id) {
0325 case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
0326 case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
0327 case 0xfc5a:
0328 tosh_fn = 0x62;
0329 break;
0330 case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
0331 case 0xfce2:
0332 tosh_fn = 0x68;
0333 break;
0334 default:
0335 tosh_fn = 0x00;
0336 break;
0337 }
0338
0339 return;
0340 }
0341
0342
0343
0344
0345
0346 static int tosh_get_machine_id(void __iomem *bios)
0347 {
0348 int id;
0349 SMMRegisters regs;
0350 unsigned short bx,cx;
0351 unsigned long address;
0352
0353 id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
0354
0355
0356
0357 if (id==0xfc2f) {
0358
0359
0360
0361 regs.eax = 0xc000;
0362 regs.ebx = 0x0000;
0363 regs.ecx = 0x0000;
0364 tosh_smm(®s);
0365 bx = (unsigned short) (regs.ebx & 0xffff);
0366
0367
0368
0369
0370
0371
0372 #if TOSH_DEBUG
0373 pr_debug("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
0374 #endif
0375 bx = 0xe6f5;
0376
0377
0378
0379 address = bx;
0380 cx = readw(bios + address);
0381 address = 9+bx+cx;
0382 cx = readw(bios + address);
0383 address = 0xa+cx;
0384 cx = readw(bios + address);
0385
0386
0387
0388 id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
0389 }
0390
0391 return id;
0392 }
0393
0394
0395
0396
0397
0398
0399
0400
0401
0402 static int tosh_probe(void)
0403 {
0404 int i,major,minor,day,year,month,flag;
0405 unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
0406 SMMRegisters regs;
0407 void __iomem *bios = ioremap(0xf0000, 0x10000);
0408
0409 if (!bios)
0410 return -ENOMEM;
0411
0412
0413
0414
0415 for (i=0;i<7;i++) {
0416 if (readb(bios+0xe010+i)!=signature[i]) {
0417 pr_err("toshiba: not a supported Toshiba laptop\n");
0418 iounmap(bios);
0419 return -ENODEV;
0420 }
0421 }
0422
0423
0424
0425 regs.eax = 0xf0f0;
0426 regs.ebx = 0x0000;
0427 regs.ecx = 0x0000;
0428 flag = tosh_smm(®s);
0429
0430
0431
0432 if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
0433 pr_err("toshiba: not a supported Toshiba laptop\n");
0434 iounmap(bios);
0435 return -ENODEV;
0436 }
0437
0438
0439
0440 tosh_sci = regs.edx & 0xffff;
0441
0442
0443
0444 tosh_id = tosh_get_machine_id(bios);
0445
0446
0447
0448 major = readb(bios+0xe009)-'0';
0449 minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
0450 tosh_bios = (major*0x100)+minor;
0451
0452
0453
0454 day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
0455 month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
0456 year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
0457 tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
0458 | ((day & 0x1f)<<1);
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469
0470 if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
0471 tosh_fan = 1;
0472
0473 iounmap(bios);
0474
0475 return 0;
0476 }
0477
0478 static int __init toshiba_init(void)
0479 {
0480 int retval;
0481
0482
0483 if (tosh_probe())
0484 return -ENODEV;
0485
0486 pr_info("Toshiba System Management Mode driver v" TOSH_VERSION "\n");
0487
0488
0489 if (tosh_fn==0x00)
0490 tosh_set_fn_port();
0491
0492
0493 retval = misc_register(&tosh_device);
0494 if (retval < 0)
0495 return retval;
0496
0497 #ifdef CONFIG_PROC_FS
0498 {
0499 struct proc_dir_entry *pde;
0500
0501 pde = proc_create_single("toshiba", 0, NULL, proc_toshiba_show);
0502 if (!pde) {
0503 misc_deregister(&tosh_device);
0504 return -ENOMEM;
0505 }
0506 }
0507 #endif
0508
0509 return 0;
0510 }
0511
0512 static void __exit toshiba_exit(void)
0513 {
0514 remove_proc_entry("toshiba", NULL);
0515 misc_deregister(&tosh_device);
0516 }
0517
0518 module_init(toshiba_init);
0519 module_exit(toshiba_exit);
0520