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