OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [char/] [toshiba.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/* toshiba.c -- Linux driver for accessing the SMM on Toshiba laptops
2
 *
3
 * Copyright (c) 1996-2001  Jonathan A. Buzzard (jonathan@buzzard.org.uk)
4
 *
5
 * Valuable assistance and patches from:
6
 *     Tom May <tom@you-bastards.com>
7
 *     Rob Napier <rnapier@employees.org>
8
 *
9
 * Fn status port numbers for machine ID's courtesy of
10
 *     0xfc02: Scott Eisert <scott.e@sky-eye.com>
11
 *     0xfc04: Steve VanDevender <stevev@efn.org>
12
 *     0xfc08: Garth Berry <garth@itsbruce.net>
13
 *     0xfc0a: Egbert Eich <eich@xfree86.org>
14
 *     0xfc10: Andrew Lofthouse <Andrew.Lofthouse@robins.af.mil>
15
 *     0xfc11: Spencer Olson <solson@novell.com>
16
 *     0xfc13: Claudius Frankewitz <kryp@gmx.de>
17
 *     0xfc15: Tom May <tom@you-bastards.com>
18
 *     0xfc17: Dave Konrad <konrad@xenia.it>
19
 *     0xfc1a: George Betzos <betzos@engr.colostate.edu>
20
 *     0xfc1b: Munemasa Wada <munemasa@jnovel.co.jp>
21
 *     0xfc1d: Arthur Liu <armie@slap.mine.nu>
22
 *     0xfc5a: Jacques L'helgoualc'h <lhh@free.fr>
23
 *     0xfcd1: Mr. Dave Konrad <konrad@xenia.it>
24
 *
25
 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
26
 *
27
 *   This code is covered by the GNU GPL and you are free to make any
28
 *   changes you wish to it under the terms of the license. However the
29
 *   code has the potential to render your computer and/or someone else's
30
 *   unusable. Please proceed with care when modifying the code.
31
 *
32
 * Note: Unfortunately the laptop hardware can close the System Configuration
33
 *       Interface on it's own accord. It is therefore necessary for *all*
34
 *       programs using this driver to be aware that *any* SCI call can fail at
35
 *       *any* time. It is up to any program to be aware of this eventuality
36
 *       and take appropriate steps.
37
 *
38
 * This program is free software; you can redistribute it and/or modify it
39
 * under the terms of the GNU General Public License as published by the
40
 * Free Software Foundation; either version 2, or (at your option) any
41
 * later version.
42
 *
43
 * This program is distributed in the hope that it will be useful, but
44
 * WITHOUT ANY WARRANTY; without even the implied warranty of
45
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
46
 * General Public License for more details.
47
 *
48
 * The information used to write this driver has been obtained by reverse
49
 * engineering the software supplied by Toshiba for their portable computers in
50
 * strict accordance with the European Council Directive 92/250/EEC on the legal
51
 * protection of computer programs, and it's implementation into English Law by
52
 * the Copyright (Computer Programs) Regulations 1992 (S.I. 1992 No.3233).
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
 
72
#include <linux/toshiba.h>
73
 
74
#define TOSH_MINOR_DEV 181
75
 
76
MODULE_LICENSE("GPL");
77
MODULE_AUTHOR("Jonathan Buzzard <jonathan@buzzard.org.uk>");
78
MODULE_DESCRIPTION("Toshiba laptop SMM driver");
79
MODULE_SUPPORTED_DEVICE("toshiba");
80
 
81
static int tosh_fn;
82
module_param_named(fn, tosh_fn, int, 0);
83
MODULE_PARM_DESC(fn, "User specified Fn key detection port");
84
 
85
static int tosh_id;
86
static int tosh_bios;
87
static int tosh_date;
88
static int tosh_sci;
89
static int tosh_fan;
90
 
91
static int tosh_ioctl(struct inode *, struct file *, unsigned int,
92
        unsigned long);
93
 
94
 
95
static const struct file_operations tosh_fops = {
96
        .owner          = THIS_MODULE,
97
        .ioctl          = tosh_ioctl,
98
};
99
 
100
static struct miscdevice tosh_device = {
101
        TOSH_MINOR_DEV,
102
        "toshiba",
103
        &tosh_fops
104
};
105
 
106
/*
107
 * Read the Fn key status
108
 */
109
#ifdef CONFIG_PROC_FS
110
static int tosh_fn_status(void)
111
{
112
        unsigned char scan;
113
        unsigned long flags;
114
 
115
        if (tosh_fn!=0) {
116
                scan = inb(tosh_fn);
117
        } else {
118
                local_irq_save(flags);
119
                outb(0x8e, 0xe4);
120
                scan = inb(0xe5);
121
                local_irq_restore(flags);
122
        }
123
 
124
        return (int) scan;
125
}
126
#endif
127
 
128
 
129
/*
130
 * For the Portage 610CT and the Tecra 700CS/700CDT emulate the HCI fan function
131
 */
132
static int tosh_emulate_fan(SMMRegisters *regs)
133
{
134
        unsigned long eax,ecx,flags;
135
        unsigned char al;
136
 
137
        eax = regs->eax & 0xff00;
138
        ecx = regs->ecx & 0xffff;
139
 
140
        /* Portage 610CT */
141
 
142
        if (tosh_id==0xfccb) {
143
                if (eax==0xfe00) {
144
                        /* fan status */
145
                        local_irq_save(flags);
146
                        outb(0xbe, 0xe4);
147
                        al = inb(0xe5);
148
                        local_irq_restore(flags);
149
                        regs->eax = 0x00;
150
                        regs->ecx = (unsigned int) (al & 0x01);
151
                }
152
                if ((eax==0xff00) && (ecx==0x0000)) {
153
                        /* fan off */
154
                        local_irq_save(flags);
155
                        outb(0xbe, 0xe4);
156
                        al = inb(0xe5);
157
                        outb(0xbe, 0xe4);
158
                        outb (al | 0x01, 0xe5);
159
                        local_irq_restore(flags);
160
                        regs->eax = 0x00;
161
                        regs->ecx = 0x00;
162
                }
163
                if ((eax==0xff00) && (ecx==0x0001)) {
164
                        /* fan on */
165
                        local_irq_save(flags);
166
                        outb(0xbe, 0xe4);
167
                        al = inb(0xe5);
168
                        outb(0xbe, 0xe4);
169
                        outb(al & 0xfe, 0xe5);
170
                        local_irq_restore(flags);
171
                        regs->eax = 0x00;
172
                        regs->ecx = 0x01;
173
                }
174
        }
175
 
176
        /* Tecra 700CS/CDT */
177
 
178
        if (tosh_id==0xfccc) {
179
                if (eax==0xfe00) {
180
                        /* fan status */
181
                        local_irq_save(flags);
182
                        outb(0xe0, 0xe4);
183
                        al = inb(0xe5);
184
                        local_irq_restore(flags);
185
                        regs->eax = 0x00;
186
                        regs->ecx = al & 0x01;
187
                }
188
                if ((eax==0xff00) && (ecx==0x0000)) {
189
                        /* fan off */
190
                        local_irq_save(flags);
191
                        outb(0xe0, 0xe4);
192
                        al = inb(0xe5);
193
                        outw(0xe0 | ((al & 0xfe) << 8), 0xe4);
194
                        local_irq_restore(flags);
195
                        regs->eax = 0x00;
196
                        regs->ecx = 0x00;
197
                }
198
                if ((eax==0xff00) && (ecx==0x0001)) {
199
                        /* fan on */
200
                        local_irq_save(flags);
201
                        outb(0xe0, 0xe4);
202
                        al = inb(0xe5);
203
                        outw(0xe0 | ((al | 0x01) << 8), 0xe4);
204
                        local_irq_restore(flags);
205
                        regs->eax = 0x00;
206
                        regs->ecx = 0x01;
207
                }
208
        }
209
 
210
        return 0;
211
}
212
 
213
 
214
/*
215
 * Put the laptop into System Management Mode
216
 */
217
int tosh_smm(SMMRegisters *regs)
218
{
219
        int eax;
220
 
221
        asm ("# load the values into the registers\n\t" \
222
                "pushl %%eax\n\t" \
223
                "movl 0(%%eax),%%edx\n\t" \
224
                "push %%edx\n\t" \
225
                "movl 4(%%eax),%%ebx\n\t" \
226
                "movl 8(%%eax),%%ecx\n\t" \
227
                "movl 12(%%eax),%%edx\n\t" \
228
                "movl 16(%%eax),%%esi\n\t" \
229
                "movl 20(%%eax),%%edi\n\t" \
230
                "popl %%eax\n\t" \
231
                "# call the System Management mode\n\t" \
232
                "inb $0xb2,%%al\n\t"
233
                "# fill out the memory with the values in the registers\n\t" \
234
                "xchgl %%eax,(%%esp)\n\t"
235
                "movl %%ebx,4(%%eax)\n\t" \
236
                "movl %%ecx,8(%%eax)\n\t" \
237
                "movl %%edx,12(%%eax)\n\t" \
238
                "movl %%esi,16(%%eax)\n\t" \
239
                "movl %%edi,20(%%eax)\n\t" \
240
                "popl %%edx\n\t" \
241
                "movl %%edx,0(%%eax)\n\t" \
242
                "# setup the return value to the carry flag\n\t" \
243
                "lahf\n\t" \
244
                "shrl $8,%%eax\n\t" \
245
                "andl $1,%%eax\n" \
246
                : "=a" (eax)
247
                : "a" (regs)
248
                : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
249
 
250
        return eax;
251
}
252
EXPORT_SYMBOL(tosh_smm);
253
 
254
 
255
static int tosh_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
256
        unsigned long arg)
257
{
258
        SMMRegisters regs;
259
        SMMRegisters __user *argp = (SMMRegisters __user *)arg;
260
        unsigned short ax,bx;
261
        int err;
262
 
263
        if (!argp)
264
                return -EINVAL;
265
 
266
        if (copy_from_user(&regs, argp, sizeof(SMMRegisters)))
267
                return -EFAULT;
268
 
269
        switch (cmd) {
270
                case TOSH_SMM:
271
                        ax = regs.eax & 0xff00;
272
                        bx = regs.ebx & 0xffff;
273
                        /* block HCI calls to read/write memory & PCI devices */
274
                        if (((ax==0xff00) || (ax==0xfe00)) && (bx>0x0069))
275
                                return -EINVAL;
276
 
277
                        /* do we need to emulate the fan ? */
278
                        if (tosh_fan==1) {
279
                                if (((ax==0xf300) || (ax==0xf400)) && (bx==0x0004)) {
280
                                        err = tosh_emulate_fan(&regs);
281
                                        break;
282
                                }
283
                        }
284
                        err = tosh_smm(&regs);
285
                        break;
286
                default:
287
                        return -EINVAL;
288
        }
289
 
290
        if (copy_to_user(argp, &regs, sizeof(SMMRegisters)))
291
                return -EFAULT;
292
 
293
        return (err==0) ? 0:-EINVAL;
294
}
295
 
296
 
297
/*
298
 * Print the information for /proc/toshiba
299
 */
300
#ifdef CONFIG_PROC_FS
301
static int proc_toshiba_show(struct seq_file *m, void *v)
302
{
303
        int key;
304
 
305
        key = tosh_fn_status();
306
 
307
        /* Arguments
308
             0) Linux driver version (this will change if format changes)
309
             1) Machine ID
310
             2) SCI version
311
             3) BIOS version (major, minor)
312
             4) BIOS date (in SCI date format)
313
             5) Fn Key status
314
        */
315
        seq_printf(m, "1.1 0x%04x %d.%d %d.%d 0x%04x 0x%02x\n",
316
                tosh_id,
317
                (tosh_sci & 0xff00)>>8,
318
                tosh_sci & 0xff,
319
                (tosh_bios & 0xff00)>>8,
320
                tosh_bios & 0xff,
321
                tosh_date,
322
                key);
323
        return 0;
324
}
325
 
326
static int proc_toshiba_open(struct inode *inode, struct file *file)
327
{
328
        return single_open(file, proc_toshiba_show, NULL);
329
}
330
 
331
static const struct file_operations proc_toshiba_fops = {
332
        .owner          = THIS_MODULE,
333
        .open           = proc_toshiba_open,
334
        .read           = seq_read,
335
        .llseek         = seq_lseek,
336
        .release        = single_release,
337
};
338
#endif
339
 
340
 
341
/*
342
 * Determine which port to use for the Fn key status
343
 */
344
static void tosh_set_fn_port(void)
345
{
346
        switch (tosh_id) {
347
                case 0xfc02: case 0xfc04: case 0xfc09: case 0xfc0a: case 0xfc10:
348
                case 0xfc11: case 0xfc13: case 0xfc15: case 0xfc1a: case 0xfc1b:
349
                case 0xfc5a:
350
                        tosh_fn = 0x62;
351
                        break;
352
                case 0xfc08: case 0xfc17: case 0xfc1d: case 0xfcd1: case 0xfce0:
353
                case 0xfce2:
354
                        tosh_fn = 0x68;
355
                        break;
356
                default:
357
                        tosh_fn = 0x00;
358
                        break;
359
        }
360
 
361
        return;
362
}
363
 
364
 
365
/*
366
 * Get the machine identification number of the current model
367
 */
368
static int tosh_get_machine_id(void __iomem *bios)
369
{
370
        int id;
371
        SMMRegisters regs;
372
        unsigned short bx,cx;
373
        unsigned long address;
374
 
375
        id = (0x100*(int) readb(bios+0xfffe))+((int) readb(bios+0xfffa));
376
 
377
        /* do we have a SCTTable machine identication number on our hands */
378
 
379
        if (id==0xfc2f) {
380
 
381
                /* start by getting a pointer into the BIOS */
382
 
383
                regs.eax = 0xc000;
384
                regs.ebx = 0x0000;
385
                regs.ecx = 0x0000;
386
                tosh_smm(&regs);
387
                bx = (unsigned short) (regs.ebx & 0xffff);
388
 
389
                /* At this point in the Toshiba routines under MS Windows
390
                   the bx register holds 0xe6f5. However my code is producing
391
                   a different value! For the time being I will just fudge the
392
                   value. This has been verified on a Satellite Pro 430CDT,
393
                   Tecra 750CDT, Tecra 780DVD and Satellite 310CDT. */
394
#if TOSH_DEBUG
395
                printk("toshiba: debugging ID ebx=0x%04x\n", regs.ebx);
396
#endif
397
                bx = 0xe6f5;
398
 
399
                /* now twiddle with our pointer a bit */
400
 
401
                address = bx;
402
                cx = readw(bios + address);
403
                address = 9+bx+cx;
404
                cx = readw(bios + address);
405
                address = 0xa+cx;
406
                cx = readw(bios + address);
407
 
408
                /* now construct our machine identification number */
409
 
410
                id = ((cx & 0xff)<<8)+((cx & 0xff00)>>8);
411
        }
412
 
413
        return id;
414
}
415
 
416
 
417
/*
418
 * Probe for the presence of a Toshiba laptop
419
 *
420
 *   returns and non-zero if unable to detect the presence of a Toshiba
421
 *   laptop, otherwise zero and determines the Machine ID, BIOS version and
422
 *   date, and SCI version.
423
 */
424
static int tosh_probe(void)
425
{
426
        int i,major,minor,day,year,month,flag;
427
        unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 };
428
        SMMRegisters regs;
429
        void __iomem *bios = ioremap(0xf0000, 0x10000);
430
 
431
        if (!bios)
432
                return -ENOMEM;
433
 
434
        /* extra sanity check for the string "TOSHIBA" in the BIOS because
435
           some machines that are not Toshiba's pass the next test */
436
 
437
        for (i=0;i<7;i++) {
438
                if (readb(bios+0xe010+i)!=signature[i]) {
439
                        printk("toshiba: not a supported Toshiba laptop\n");
440
                        iounmap(bios);
441
                        return -ENODEV;
442
                }
443
        }
444
 
445
        /* call the Toshiba SCI support check routine */
446
 
447
        regs.eax = 0xf0f0;
448
        regs.ebx = 0x0000;
449
        regs.ecx = 0x0000;
450
        flag = tosh_smm(&regs);
451
 
452
        /* if this is not a Toshiba laptop carry flag is set and ah=0x86 */
453
 
454
        if ((flag==1) || ((regs.eax & 0xff00)==0x8600)) {
455
                printk("toshiba: not a supported Toshiba laptop\n");
456
                iounmap(bios);
457
                return -ENODEV;
458
        }
459
 
460
        /* if we get this far then we are running on a Toshiba (probably)! */
461
 
462
        tosh_sci = regs.edx & 0xffff;
463
 
464
        /* next get the machine ID of the current laptop */
465
 
466
        tosh_id = tosh_get_machine_id(bios);
467
 
468
        /* get the BIOS version */
469
 
470
        major = readb(bios+0xe009)-'0';
471
        minor = ((readb(bios+0xe00b)-'0')*10)+(readb(bios+0xe00c)-'0');
472
        tosh_bios = (major*0x100)+minor;
473
 
474
        /* get the BIOS date */
475
 
476
        day = ((readb(bios+0xfff5)-'0')*10)+(readb(bios+0xfff6)-'0');
477
        month = ((readb(bios+0xfff8)-'0')*10)+(readb(bios+0xfff9)-'0');
478
        year = ((readb(bios+0xfffb)-'0')*10)+(readb(bios+0xfffc)-'0');
479
        tosh_date = (((year-90) & 0x1f)<<10) | ((month & 0xf)<<6)
480
                | ((day & 0x1f)<<1);
481
 
482
 
483
        /* in theory we should check the ports we are going to use for the
484
           fn key detection (and the fan on the Portage 610/Tecra700), and
485
           then request them to stop other drivers using them. However as
486
           the keyboard driver grabs 0x60-0x6f and the pic driver grabs
487
           0xa0-0xbf we can't. We just have to live dangerously and use the
488
           ports anyway, oh boy! */
489
 
490
        /* do we need to emulate the fan? */
491
 
492
        if ((tosh_id==0xfccb) || (tosh_id==0xfccc))
493
                tosh_fan = 1;
494
 
495
        iounmap(bios);
496
 
497
        return 0;
498
}
499
 
500
static int __init toshiba_init(void)
501
{
502
        int retval;
503
        /* are we running on a Toshiba laptop */
504
 
505
        if (tosh_probe())
506
                return -ENODEV;
507
 
508
        printk(KERN_INFO "Toshiba System Managment Mode driver v" TOSH_VERSION "\n");
509
 
510
        /* set the port to use for Fn status if not specified as a parameter */
511
        if (tosh_fn==0x00)
512
                tosh_set_fn_port();
513
 
514
        /* register the device file */
515
        retval = misc_register(&tosh_device);
516
        if (retval < 0)
517
                return retval;
518
 
519
#ifdef CONFIG_PROC_FS
520
        {
521
                struct proc_dir_entry *pde;
522
 
523
                pde = create_proc_entry("toshiba", 0, NULL);
524
                if (!pde) {
525
                        misc_deregister(&tosh_device);
526
                        return -ENOMEM;
527
                }
528
                pde->proc_fops = &proc_toshiba_fops;
529
        }
530
#endif
531
 
532
        return 0;
533
}
534
 
535
static void __exit toshiba_exit(void)
536
{
537
        remove_proc_entry("toshiba", NULL);
538
        misc_deregister(&tosh_device);
539
}
540
 
541
module_init(toshiba_init);
542
module_exit(toshiba_exit);
543
 

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.