1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56#define TOSH_VERSION "1.11 26/9/2001"
57#define TOSH_DEBUG 0
58
59#include <linux/module.h>
60#include <linux/kernel.h>
61#include <linux/types.h>
62#include <linux/fcntl.h>
63#include <linux/miscdevice.h>
64#include <linux/ioport.h>
65#include <asm/io.h>
66#include <asm/uaccess.h>
67#include <linux/init.h>
68#include <linux/stat.h>
69#include <linux/proc_fs.h>
70#include <linux/seq_file.h>
71#include <linux/mutex.h>
72#include <linux/toshiba.h>
73
74#define TOSH_MINOR_DEV 181
75
76MODULE_LICENSE("GPL");
77MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
78MODULE_DESCRIPTION("Toshiba laptop SMM driver");
79MODULE_SUPPORTED_DEVICE("toshiba");
80
81static DEFINE_MUTEX(tosh_mutex);
82static int tosh_fn;
83module_param_named(fn, tosh_fn, int, 0);
84MODULE_PARM_DESC(fn, "User specified Fn key detection port");
85
86static int tosh_id;
87static int tosh_bios;
88static int tosh_date;
89static int tosh_sci;
90static int tosh_fan;
91
92static long tosh_ioctl(struct file *, unsigned int,
93 unsigned long);
94
95
96static const struct file_operations tosh_fops = {
97 .owner = THIS_MODULE,
98 .unlocked_ioctl = tosh_ioctl,
99 .llseek = noop_llseek,
100};
101
102static struct miscdevice tosh_device = {
103 TOSH_MINOR_DEV,
104 "toshiba",
105 &tosh_fops
106};
107
108
109
110
111#ifdef CONFIG_PROC_FS
112static int tosh_fn_status(void)
113{
114 unsigned char scan;
115 unsigned long flags;
116
117 if (tosh_fn!=0) {
118 scan = inb(tosh_fn);
119 } else {
120 local_irq_save(flags);
121 outb(0x8e, 0xe4);
122 scan = inb(0xe5);
123 local_irq_restore(flags);
124 }
125
126 return (int) scan;
127}
128#endif
129
130
131
132
133
134static int tosh_emulate_fan(SMMRegisters *regs)
135{
136 unsigned long eax,ecx,flags;
137 unsigned char al;
138
139 eax = regs->eax & 0xff00;
140 ecx = regs->ecx & 0xffff;
141
142
143
144 if (tosh_id==0xfccb) {
145 if (eax==0xfe00) {
146
147 local_irq_save(flags);
148 outb(0xbe, 0xe4);
149 al = inb(0xe5);
150 local_irq_restore(flags);
151 regs->eax = 0x00;
152 regs->ecx = (unsigned int) (al & 0x01);
153 }
154 if ((eax==0xff00) && (ecx==0x0000)) {
155
156 local_irq_save(flags);
157 outb(0xbe, 0xe4);
158 al = inb(0xe5);
159 outb(0xbe, 0xe4);
160 outb (al | 0x01, 0xe5);
161 local_irq_restore(flags);
162 regs->eax = 0x00;
163 regs->ecx = 0x00;
164 }
165 if ((eax==0xff00) && (ecx==0x0001)) {
166
167 local_irq_save(flags);
168 outb(0xbe, 0xe4);
169 al = inb(0xe5);
170 outb(0xbe, 0xe4);
171 outb(al & 0xfe, 0xe5);
172 local_irq_restore(flags);
173 regs->eax = 0x00;
174 regs->ecx = 0x01;
175 }
176 }
177
178
179
180 if (tosh_id==0xfccc) {
181 if (eax==0xfe00) {
182
183 local_irq_save(flags);
184 outb(0xe0, 0xe4);
185 al = inb(0xe5);
186 local_irq_restore(flags);
187 regs->eax = 0x00;
188 regs->ecx = al & 0x01;
189 }
190 if ((eax==0xff00) && (ecx==0x0000)) {
191
192 local_irq_save(flags);
193 outb(0xe0, 0xe4);
194 al = inb(0xe5);
195 outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
196 local_irq_restore(flags);
197 regs->eax = 0x00;
198 regs->ecx = 0x00;
199 }
200 if ((eax==0xff00) && (ecx==0x0001)) {
201
202 local_irq_save(flags);
203 outb(0xe0, 0xe4);
204 al = inb(0xe5);
205 outw(0xe0 | ((al | 0x01) << 8), 0xe4);
206 local_irq_restore(flags);
207 regs->eax = 0x00;
208 regs->ecx = 0x01;
209 }
210 }
211
212 return 0;
213}
214
215
216
217
218
219int tosh_smm(SMMRegisters *regs)
220{
221 int eax;
222
223 asm ("# load the values into the registers\n\t" \
224 "pushl %%eax\n\t" \
225 "movl 0(%%eax),%%edx\n\t" \
226 "push %%edx\n\t" \
227 "movl 4(%%eax),%%ebx\n\t" \
228 "movl 8(%%eax),%%ecx\n\t" \
229 "movl 12(%%eax),%%edx\n\t" \
230 "movl 16(%%eax),%%esi\n\t" \
231 "movl 20(%%eax),%%edi\n\t" \
232 "popl %%eax\n\t" \
233 "# call the System Management mode\n\t" \
234 "inb $0xb2,%%al\n\t"
235 "# fill out the memory with the values in the registers\n\t" \
236 "xchgl %%eax,(%%esp)\n\t"
237 "movl %%ebx,4(%%eax)\n\t" \
238 "movl %%ecx,8(%%eax)\n\t" \
239 "movl %%edx,12(%%eax)\n\t" \
240 "movl %%esi,16(%%eax)\n\t" \
241 "movl %%edi,20(%%eax)\n\t" \
242 "popl %%edx\n\t" \
243 "movl %%edx,0(%%eax)\n\t" \
244 "# setup the return value to the carry flag\n\t" \
245 "lahf\n\t" \
246 "shrl $8,%%eax\n\t" \
247 "andl $1,%%eax\n" \
248 : "=a" (eax)
249 : "a" (regs)
250 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
251
252 return eax;
253}
254EXPORT_SYMBOL(tosh_smm);
255
256
257static long tosh_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
258{
259 SMMRegisters regs;
260 SMMRegisters __user *argp = (SMMRegisters __user *)arg;
261 unsigned short ax,bx;
262 int err;
263
264 if (!argp)
265 return -EINVAL;
266
267 if (copy_from_user(®s, argp, sizeof(SMMRegisters)))
268 return -EFAULT;
269
270 switch (cmd) {
271 case TOSH_SMM:
272 ax = regs.eax & 0xff00;
273 bx = regs.ebx & 0xffff;
274
275 if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
276 return -EINVAL;
277
278
279 mutex_lock(&tosh_mutex);
280 if (tosh_fan==1) {
281 if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
282 err = tosh_emulate_fan(®s);
283 mutex_unlock(&tosh_mutex);
284 break;
285 }
286 }
287 err = tosh_smm(®s);
288 mutex_unlock(&tosh_mutex);
289 break;
290 default:
291 return -EINVAL;
292 }
293
294 if (copy_to_user(argp, ®s, sizeof(SMMRegisters)))
295 return -EFAULT;
296
297 return (err==0) ? 0:-EINVAL;
298}
299
300
301
302
303
304#ifdef CONFIG_PROC_FS
305static int proc_toshiba_show(struct seq_file *m, void *v)
306{
307 int key;
308
309 key = tosh_fn_status();
310
311
312
313
314
315
316
317
318
319 seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
320 tosh_id,
321 (tosh_sci & 0xff00)>>8,
322 tosh_sci & 0xff,
323 (tosh_bios & 0xff00)>>8,
324 tosh_bios & 0xff,
325 tosh_date,
326 key);
327 return 0;
328}
329
330static int proc_toshiba_open(struct inode *inode, struct file *file)
331{
332 return single_open(file, proc_toshiba_show, NULL);
333}
334
335static const struct file_operations proc_toshiba_fops = {
336 .owner = THIS_MODULE,
337 .open = proc_toshiba_open,
338 .read = seq_read,
339 .llseek = seq_lseek,
340 .release = single_release,
341};
342#endif
343
344
345
346
347
348static void tosh_set_fn_port(void)
349{
350 switch (tosh_id) {
351 case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
352 case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
353 case 0xfc5a:
354 tosh_fn = 0x62;
355 break;
356 case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
357 case 0xfce2:
358 tosh_fn = 0x68;
359 break;
360 default:
361 tosh_fn = 0x00;
362 break;
363 }
364
365 return;
366}
367
368
369
370
371
372static int tosh_get_machine_id(void __iomem *bios)
373{
374 int id;
375 SMMRegisters regs;
376 unsigned short bx,cx;
377 unsigned long address;
378
379 id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
380
381
382
383 if (id==0xfc2f) {
384
385
386
387 regs.eax = 0xc000;
388 regs.ebx = 0x0000;
389 regs.ecx = 0x0000;
390 tosh_smm(®s);
391 bx = (unsigned short) (regs.ebx & 0xffff);
392
393
394
395
396
397
398#if TOSH_DEBUG
399 printk("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
400#endif
401 bx = 0xe6f5;
402
403
404
405 address = bx;
406 cx = readw(bios + address);
407 address = 9+bx+cx;
408 cx = readw(bios + address);
409 address = 0xa+cx;
410 cx = readw(bios + address);
411
412
413
414 id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
415 }
416
417 return id;
418}
419
420
421
422
423
424
425
426
427
428static int tosh_probe(void)
429{
430 int i,major,minor,day,year,month,flag;
431 unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
432 SMMRegisters regs;
433 void __iomem *bios = ioremap(0xf0000, 0x10000);
434
435 if (!bios)
436 return -ENOMEM;
437
438
439
440
441 for (i=0;i<7;i++) {
442 if (readb(bios+0xe010+i)!=signature[i]) {
443 printk("toshiba: not a supported Toshiba laptop\n");
444 iounmap(bios);
445 return -ENODEV;
446 }
447 }
448
449
450
451 regs.eax = 0xf0f0;
452 regs.ebx = 0x0000;
453 regs.ecx = 0x0000;
454 flag = tosh_smm(®s);
455
456
457
458 if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
459 printk("toshiba: not a supported Toshiba laptop\n");
460 iounmap(bios);
461 return -ENODEV;
462 }
463
464
465
466 tosh_sci = regs.edx & 0xffff;
467
468
469
470 tosh_id = tosh_get_machine_id(bios);
471
472
473
474 major = readb(bios+0xe009)-'0';
475 minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
476 tosh_bios = (major*0x100)+minor;
477
478
479
480 day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
481 month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
482 year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
483 tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
484 | ((day & 0x1f)<<1);
485
486
487
488
489
490
491
492
493
494
495
496 if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
497 tosh_fan = 1;
498
499 iounmap(bios);
500
501 return 0;
502}
503
504static int __init toshiba_init(void)
505{
506 int retval;
507
508
509 if (tosh_probe())
510 return -ENODEV;
511
512 printk(KERN_INFO "Toshiba System Management Mode driver v" TOSH_VERSION "\n");
513
514
515 if (tosh_fn==0x00)
516 tosh_set_fn_port();
517
518
519 retval = misc_register(&tosh_device);
520 if (retval < 0)
521 return retval;
522
523#ifdef CONFIG_PROC_FS
524 {
525 struct proc_dir_entry *pde;
526
527 pde = proc_create("toshiba", 0, NULL, &proc_toshiba_fops);
528 if (!pde) {
529 misc_deregister(&tosh_device);
530 return -ENOMEM;
531 }
532 }
533#endif
534
535 return 0;
536}
537
538static void __exit toshiba_exit(void)
539{
540 remove_proc_entry("toshiba", NULL);
541 misc_deregister(&tosh_device);
542}
543
544module_init(toshiba_init);
545module_exit(toshiba_exit);
546
547