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