PageRenderTime 85ms CodeModel.GetById 19ms app.highlight 60ms RepoModel.GetById 2ms app.codeStats 0ms

/drivers/char/i8k.c

https://bitbucket.org/evzijst/gittest
C | 788 lines | 559 code | 114 blank | 115 comment | 91 complexity | 74486b5afb7eaee4ae94c61d66927c4d MD5 | raw file
  1/*
  2 * i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
  3 *	    See http://www.debian.org/~dz/i8k/ for more information
  4 *	    and for latest version of this driver.
  5 *
  6 * Copyright (C) 2001  Massimo Dal Zotto <dz@debian.org>
  7 *
  8 * This program is free software; you can redistribute it and/or modify it
  9 * under the terms of the GNU General Public License as published by the
 10 * Free Software Foundation; either version 2, or (at your option) any
 11 * later version.
 12 *
 13 * This program is distributed in the hope that it will be useful, but
 14 * WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * General Public License for more details.
 17 */
 18
 19#include <linux/module.h>
 20#include <linux/types.h>
 21#include <linux/init.h>
 22#include <linux/proc_fs.h>
 23#include <linux/apm_bios.h>
 24#include <asm/uaccess.h>
 25#include <asm/io.h>
 26
 27#include <linux/i8k.h>
 28
 29#define I8K_VERSION		"1.13 14/05/2002"
 30
 31#define I8K_SMM_FN_STATUS	0x0025
 32#define I8K_SMM_POWER_STATUS	0x0069
 33#define I8K_SMM_SET_FAN		0x01a3
 34#define I8K_SMM_GET_FAN		0x00a3
 35#define I8K_SMM_GET_SPEED	0x02a3
 36#define I8K_SMM_GET_TEMP	0x10a3
 37#define I8K_SMM_GET_DELL_SIG	0xffa3
 38#define I8K_SMM_BIOS_VERSION	0x00a6
 39
 40#define I8K_FAN_MULT		30
 41#define I8K_MAX_TEMP		127
 42
 43#define I8K_FN_NONE		0x00
 44#define I8K_FN_UP		0x01
 45#define I8K_FN_DOWN		0x02
 46#define I8K_FN_MUTE		0x04
 47#define I8K_FN_MASK		0x07
 48#define I8K_FN_SHIFT		8
 49
 50#define I8K_POWER_AC		0x05
 51#define I8K_POWER_BATTERY	0x01
 52
 53#define I8K_TEMPERATURE_BUG	1
 54
 55#define DELL_SIGNATURE		"Dell Computer"
 56
 57static char *supported_models[] = {
 58    "Inspiron",
 59    "Latitude",
 60    NULL
 61};
 62
 63static char system_vendor[48] = "?";
 64static char product_name [48] = "?";
 65static char bios_version [4]  = "?";
 66static char serial_number[16] = "?";
 67
 68MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
 69MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
 70MODULE_LICENSE("GPL");
 71
 72static int force;
 73module_param(force, bool, 0);
 74MODULE_PARM_DESC(force, "Force loading without checking for supported models");
 75
 76static int restricted;
 77module_param(restricted, bool, 0);
 78MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set");
 79
 80static int power_status;
 81module_param(power_status, bool, 0600);
 82MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k");
 83
 84static ssize_t i8k_read(struct file *, char __user *, size_t, loff_t *);
 85static int i8k_ioctl(struct inode *, struct file *, unsigned int,
 86		     unsigned long);
 87
 88static struct file_operations i8k_fops = {
 89    .read	= i8k_read,
 90    .ioctl	= i8k_ioctl,
 91};
 92
 93typedef struct {
 94    unsigned int eax;
 95    unsigned int ebx __attribute__ ((packed));
 96    unsigned int ecx __attribute__ ((packed));
 97    unsigned int edx __attribute__ ((packed));
 98    unsigned int esi __attribute__ ((packed));
 99    unsigned int edi __attribute__ ((packed));
100} SMMRegisters;
101
102typedef struct {
103    u8	type;
104    u8	length;
105    u16	handle;
106} DMIHeader;
107
108/*
109 * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard.
110 */
111static int i8k_smm(SMMRegisters *regs)
112{
113    int rc;
114    int eax = regs->eax;
115
116    asm("pushl %%eax\n\t" \
117	"movl 0(%%eax),%%edx\n\t" \
118	"push %%edx\n\t" \
119	"movl 4(%%eax),%%ebx\n\t" \
120	"movl 8(%%eax),%%ecx\n\t" \
121	"movl 12(%%eax),%%edx\n\t" \
122	"movl 16(%%eax),%%esi\n\t" \
123	"movl 20(%%eax),%%edi\n\t" \
124	"popl %%eax\n\t" \
125	"out %%al,$0xb2\n\t" \
126	"out %%al,$0x84\n\t" \
127	"xchgl %%eax,(%%esp)\n\t"
128	"movl %%ebx,4(%%eax)\n\t" \
129	"movl %%ecx,8(%%eax)\n\t" \
130	"movl %%edx,12(%%eax)\n\t" \
131	"movl %%esi,16(%%eax)\n\t" \
132	"movl %%edi,20(%%eax)\n\t" \
133	"popl %%edx\n\t" \
134	"movl %%edx,0(%%eax)\n\t" \
135	"lahf\n\t" \
136	"shrl $8,%%eax\n\t" \
137	"andl $1,%%eax\n" \
138	: "=a" (rc)
139	: "a" (regs)
140	: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
141
142    if ((rc != 0) || ((regs->eax & 0xffff) == 0xffff) || (regs->eax == eax)) {
143	return -EINVAL;
144    }
145
146    return 0;
147}
148
149/*
150 * Read the bios version. Return the version as an integer corresponding
151 * to the ascii value, for example "A17" is returned as 0x00413137.
152 */
153static int i8k_get_bios_version(void)
154{
155    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
156    int rc;
157
158    regs.eax = I8K_SMM_BIOS_VERSION;
159    if ((rc=i8k_smm(&regs)) < 0) {
160	return rc;
161    }
162
163    return regs.eax;
164}
165
166/*
167 * Read the machine id.
168 */
169static int i8k_get_serial_number(unsigned char *buff)
170{
171    strlcpy(buff, serial_number, sizeof(serial_number));
172    return 0;
173}
174
175/*
176 * Read the Fn key status.
177 */
178static int i8k_get_fn_status(void)
179{
180    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
181    int rc;
182
183    regs.eax = I8K_SMM_FN_STATUS;
184    if ((rc=i8k_smm(&regs)) < 0) {
185	return rc;
186    }
187
188    switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
189    case I8K_FN_UP:
190	return I8K_VOL_UP;
191    case I8K_FN_DOWN:
192	return I8K_VOL_DOWN;
193    case I8K_FN_MUTE:
194	return I8K_VOL_MUTE;
195    default:
196	return 0;
197    }
198}
199
200/*
201 * Read the power status.
202 */
203static int i8k_get_power_status(void)
204{
205    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
206    int rc;
207
208    regs.eax = I8K_SMM_POWER_STATUS;
209    if ((rc=i8k_smm(&regs)) < 0) {
210	return rc;
211    }
212
213    switch (regs.eax & 0xff) {
214    case I8K_POWER_AC:
215	return I8K_AC;
216    default:
217	return I8K_BATTERY;
218    }
219}
220
221/*
222 * Read the fan status.
223 */
224static int i8k_get_fan_status(int fan)
225{
226    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
227    int rc;
228
229    regs.eax = I8K_SMM_GET_FAN;
230    regs.ebx = fan & 0xff;
231    if ((rc=i8k_smm(&regs)) < 0) {
232	return rc;
233    }
234
235    return (regs.eax & 0xff);
236}
237
238/*
239 * Read the fan speed in RPM.
240 */
241static int i8k_get_fan_speed(int fan)
242{
243    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
244    int rc;
245
246    regs.eax = I8K_SMM_GET_SPEED;
247    regs.ebx = fan & 0xff;
248    if ((rc=i8k_smm(&regs)) < 0) {
249	return rc;
250    }
251
252    return (regs.eax & 0xffff) * I8K_FAN_MULT;
253}
254
255/*
256 * Set the fan speed (off, low, high). Returns the new fan status.
257 */
258static int i8k_set_fan(int fan, int speed)
259{
260    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
261    int rc;
262
263    speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed);
264
265    regs.eax = I8K_SMM_SET_FAN;
266    regs.ebx = (fan & 0xff) | (speed << 8);
267    if ((rc=i8k_smm(&regs)) < 0) {
268	return rc;
269    }
270
271    return (i8k_get_fan_status(fan));
272}
273
274/*
275 * Read the cpu temperature.
276 */
277static int i8k_get_cpu_temp(void)
278{
279    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
280    int rc;
281    int temp;
282
283#ifdef I8K_TEMPERATURE_BUG
284    static int prev = 0;
285#endif
286
287    regs.eax = I8K_SMM_GET_TEMP;
288    if ((rc=i8k_smm(&regs)) < 0) {
289	return rc;
290    }
291    temp = regs.eax & 0xff;
292
293#ifdef I8K_TEMPERATURE_BUG
294    /*
295     * Sometimes the temperature sensor returns 0x99, which is out of range.
296     * In this case we return (once) the previous cached value. For example:
297     # 1003655137 00000058 00005a4b
298     # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees
299     # 1003655139 00000054 00005c52
300     */
301    if (temp > I8K_MAX_TEMP) {
302	temp = prev;
303	prev = I8K_MAX_TEMP;
304    } else {
305	prev = temp;
306    }
307#endif
308
309    return temp;
310}
311
312static int i8k_get_dell_signature(void)
313{
314    SMMRegisters regs = { 0, 0, 0, 0, 0, 0 };
315    int rc;
316
317    regs.eax = I8K_SMM_GET_DELL_SIG;
318    if ((rc=i8k_smm(&regs)) < 0) {
319	return rc;
320    }
321
322    if ((regs.eax == 1145651527) && (regs.edx == 1145392204)) {
323	return 0;
324    } else {
325	return -1;
326    }
327}
328
329static int i8k_ioctl(struct inode *ip, struct file *fp, unsigned int cmd,
330		     unsigned long arg)
331{
332    int val;
333    int speed;
334    unsigned char buff[16];
335    int __user *argp = (int __user *)arg;
336
337    if (!argp)
338	return -EINVAL;
339
340    switch (cmd) {
341    case I8K_BIOS_VERSION:
342	val = i8k_get_bios_version();
343	break;
344
345    case I8K_MACHINE_ID:
346	memset(buff, 0, 16);
347	val = i8k_get_serial_number(buff);
348	break;
349
350    case I8K_FN_STATUS:
351	val = i8k_get_fn_status();
352	break;
353
354    case I8K_POWER_STATUS:
355	val = i8k_get_power_status();
356	break;
357
358    case I8K_GET_TEMP:
359	val = i8k_get_cpu_temp();
360	break;
361
362    case I8K_GET_SPEED:
363	if (copy_from_user(&val, argp, sizeof(int))) {
364	    return -EFAULT;
365	}
366	val = i8k_get_fan_speed(val);
367	break;
368
369    case I8K_GET_FAN:
370	if (copy_from_user(&val, argp, sizeof(int))) {
371	    return -EFAULT;
372	}
373	val = i8k_get_fan_status(val);
374	break;
375
376    case I8K_SET_FAN:
377	if (restricted && !capable(CAP_SYS_ADMIN)) {
378	    return -EPERM;
379	}
380	if (copy_from_user(&val, argp, sizeof(int))) {
381	    return -EFAULT;
382	}
383	if (copy_from_user(&speed, argp+1, sizeof(int))) {
384	    return -EFAULT;
385	}
386	val = i8k_set_fan(val, speed);
387	break;
388
389    default:
390	return -EINVAL;
391    }
392
393    if (val < 0) {
394	return val;
395    }
396
397    switch (cmd) {
398    case I8K_BIOS_VERSION:
399	if (copy_to_user(argp, &val, 4)) {
400	    return -EFAULT;
401	}
402	break;
403    case I8K_MACHINE_ID:
404	if (copy_to_user(argp, buff, 16)) {
405	    return -EFAULT;
406	}
407	break;
408    default:
409	if (copy_to_user(argp, &val, sizeof(int))) {
410	    return -EFAULT;
411	}
412	break;
413    }
414
415    return 0;
416}
417
418/*
419 * Print the information for /proc/i8k.
420 */
421static int i8k_get_info(char *buffer, char **start, off_t fpos, int length)
422{
423    int n, fn_key, cpu_temp, ac_power;
424    int left_fan, right_fan, left_speed, right_speed;
425
426    cpu_temp     = i8k_get_cpu_temp();			/* 11100 �s */
427    left_fan     = i8k_get_fan_status(I8K_FAN_LEFT);	/*   580 �s */
428    right_fan    = i8k_get_fan_status(I8K_FAN_RIGHT);	/*   580 �s */
429    left_speed   = i8k_get_fan_speed(I8K_FAN_LEFT);	/*   580 �s */
430    right_speed  = i8k_get_fan_speed(I8K_FAN_RIGHT);	/*   580 �s */
431    fn_key       = i8k_get_fn_status();			/*   750 �s */
432    if (power_status) {
433	ac_power = i8k_get_power_status();		/* 14700 �s */
434    } else {
435	ac_power = -1;
436    }
437
438    /*
439     * Info:
440     *
441     * 1)  Format version (this will change if format changes)
442     * 2)  BIOS version
443     * 3)  BIOS machine ID
444     * 4)  Cpu temperature
445     * 5)  Left fan status
446     * 6)  Right fan status
447     * 7)  Left fan speed
448     * 8)  Right fan speed
449     * 9)  AC power
450     * 10) Fn Key status
451     */
452    n = sprintf(buffer, "%s %s %s %d %d %d %d %d %d %d\n",
453		I8K_PROC_FMT,
454		bios_version,
455		serial_number,
456		cpu_temp,
457		left_fan,
458		right_fan,
459		left_speed,
460		right_speed,
461		ac_power,
462		fn_key);
463
464    return n;
465}
466
467static ssize_t i8k_read(struct file *f, char __user *buffer, size_t len, loff_t *fpos)
468{
469    int n;
470    char info[128];
471
472    n = i8k_get_info(info, NULL, 0, 128);
473    if (n <= 0) {
474	return n;
475    }
476
477    if (*fpos >= n) {
478	return 0;
479    }
480
481    if ((*fpos + len) >= n) {
482	len = n - *fpos;
483    }
484
485    if (copy_to_user(buffer, info, len) != 0) {
486	return -EFAULT;
487    }
488
489    *fpos += len;
490    return len;
491}
492
493static char* __init string_trim(char *s, int size)
494{
495    int len;
496    char *p;
497
498    if ((len = strlen(s)) > size) {
499	len = size;
500    }
501
502    for (p=s+len-1; len && (*p==' '); len--,p--) {
503	*p = '\0';
504    }
505
506    return s;
507}
508
509/* DMI code, stolen from arch/i386/kernel/dmi_scan.c */
510
511/*
512 * |<-- dmi->length -->|
513 * |                   |
514 * |dmi header    s=N  | string1,\0, ..., stringN,\0, ..., \0
515 *                |                       |
516 *                +-----------------------+
517 */
518static char* __init dmi_string(DMIHeader *dmi, u8 s)
519{
520    u8 *p;
521
522    if (!s) {
523	return "";
524    }
525    s--;
526
527    p = (u8 *)dmi + dmi->length;
528    while (s > 0) {
529	p += strlen(p);
530	p++;
531	s--;
532    }
533
534    return p;
535}
536
537static void __init dmi_decode(DMIHeader *dmi)
538{
539    u8 *data = (u8 *) dmi;
540    char *p;
541
542#ifdef I8K_DEBUG
543    int i;
544    printk("%08x ", (int)data);
545    for (i=0; i<data[1] && i<64; i++) {
546	printk("%02x ", data[i]);
547    }
548    printk("\n");
549#endif
550
551    switch (dmi->type) {
552    case  0:	/* BIOS Information */
553	p = dmi_string(dmi,data[5]);
554	if (*p) {
555	    strlcpy(bios_version, p, sizeof(bios_version));
556	    string_trim(bios_version, sizeof(bios_version));
557	}
558	break;	
559    case 1:	/* System Information */
560	p = dmi_string(dmi,data[4]);
561	if (*p) {
562	    strlcpy(system_vendor, p, sizeof(system_vendor));
563	    string_trim(system_vendor, sizeof(system_vendor));
564	}
565	p = dmi_string(dmi,data[5]);
566	if (*p) {
567	    strlcpy(product_name, p, sizeof(product_name));
568	    string_trim(product_name, sizeof(product_name));
569	}
570	p = dmi_string(dmi,data[7]);
571	if (*p) {
572	    strlcpy(serial_number, p, sizeof(serial_number));
573	    string_trim(serial_number, sizeof(serial_number));
574	}
575	break;
576    }
577}
578
579static int __init dmi_table(u32 base, int len, int num, void (*fn)(DMIHeader*))
580{
581    u8 *buf;
582    u8 *data;
583    DMIHeader *dmi;
584    int i = 1;
585
586    buf = ioremap(base, len);
587    if (buf == NULL) {
588	return -1;
589    }
590    data = buf;
591
592    /*
593     * Stop when we see al the items the table claimed to have
594     * or we run off the end of the table (also happens)
595     */
596    while ((i<num) && ((data-buf) < len)) {
597	dmi = (DMIHeader *)data;
598	/*
599	 * Avoid misparsing crud if the length of the last
600	 * record is crap
601	 */
602	if ((data-buf+dmi->length) >= len) {
603	    break;
604	}
605	fn(dmi);
606	data += dmi->length;
607	/*
608	 * Don't go off the end of the data if there is
609	 * stuff looking like string fill past the end
610	 */
611	while (((data-buf) < len) && (*data || data[1])) {
612	    data++;
613	}
614	data += 2;
615	i++;
616    }
617    iounmap(buf);
618
619    return 0;
620}
621
622static int __init dmi_iterate(void (*decode)(DMIHeader *))
623{
624	unsigned char buf[20];
625	void __iomem *p = ioremap(0xe0000, 0x20000), *q;
626
627	if (!p)
628		return -1;
629
630	for (q = p; q < p + 0x20000; q += 16) {
631		memcpy_fromio(buf, q, 20);
632		if (memcmp(buf, "_DMI_", 5)==0) {
633			u16 num  = buf[13]<<8  | buf[12];
634			u16 len  = buf [7]<<8  | buf [6];
635			u32 base = buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8];
636#ifdef I8K_DEBUG
637			printk(KERN_INFO "DMI %d.%d present.\n",
638			   buf[14]>>4, buf[14]&0x0F);
639			printk(KERN_INFO "%d structures occupying %d bytes.\n",
640			   buf[13]<<8 | buf[12],
641			   buf [7]<<8 | buf[6]);
642			printk(KERN_INFO "DMI table at 0x%08X.\n",
643			   buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8]);
644#endif
645			if (dmi_table(base, len, num, decode)==0) {
646				iounmap(p);
647				return 0;
648			}
649		}
650	}
651	iounmap(p);
652	return -1;
653}
654/* end of DMI code */
655
656/*
657 * Get DMI information.
658 */
659static int __init i8k_dmi_probe(void)
660{
661    char **p;
662
663    if (dmi_iterate(dmi_decode) != 0) {
664	printk(KERN_INFO "i8k: unable to get DMI information\n");
665	return -ENODEV;
666    }
667
668    if (strncmp(system_vendor,DELL_SIGNATURE,strlen(DELL_SIGNATURE)) != 0) {
669	printk(KERN_INFO "i8k: not running on a Dell system\n");
670	return -ENODEV;
671    }
672
673    for (p=supported_models; ; p++) {
674	if (!*p) {
675	    printk(KERN_INFO "i8k: unsupported model: %s\n", product_name);
676	    return -ENODEV;
677	}
678	if (strncmp(product_name,*p,strlen(*p)) == 0) {
679	    break;
680	}
681    }
682
683    return 0;
684}
685
686/*
687 * Probe for the presence of a supported laptop.
688 */
689static int __init i8k_probe(void)
690{
691    char buff[4];
692    int version;
693    int smm_found = 0;
694
695    /*
696     * Get DMI information
697     */
698    if (i8k_dmi_probe() != 0) {
699	printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
700	       system_vendor, product_name, bios_version);
701    }
702
703    /*
704     * Get SMM Dell signature
705     */
706    if (i8k_get_dell_signature() != 0) {
707	printk(KERN_INFO "i8k: unable to get SMM Dell signature\n");
708    } else {
709	smm_found = 1;
710    }
711
712    /*
713     * Get SMM BIOS version.
714     */
715    version = i8k_get_bios_version();
716    if (version <= 0) {
717	printk(KERN_INFO "i8k: unable to get SMM BIOS version\n");
718    } else {
719	smm_found = 1;
720	buff[0] = (version >> 16) & 0xff;
721	buff[1] = (version >>  8) & 0xff;
722	buff[2] = (version)       & 0xff;
723	buff[3] = '\0';
724	/*
725	 * If DMI BIOS version is unknown use SMM BIOS version.
726	 */
727	if (bios_version[0] == '?') {
728	    strcpy(bios_version, buff);
729	}
730	/*
731	 * Check if the two versions match.
732	 */
733	if (strncmp(buff,bios_version,sizeof(bios_version)) != 0) {
734	    printk(KERN_INFO "i8k: BIOS version mismatch: %s != %s\n",
735		   buff, bios_version);
736	}
737    }
738
739    if (!smm_found && !force) {
740	return -ENODEV;
741    }
742
743    return 0;
744}
745
746#ifdef MODULE
747static
748#endif
749int __init i8k_init(void)
750{
751    struct proc_dir_entry *proc_i8k;
752
753    /* Are we running on an supported laptop? */
754    if (i8k_probe() != 0) {
755	return -ENODEV;
756    }
757
758    /* Register the proc entry */
759    proc_i8k = create_proc_info_entry("i8k", 0, NULL, i8k_get_info);
760    if (!proc_i8k) {
761	return -ENOENT;
762    }
763    proc_i8k->proc_fops = &i8k_fops;
764    proc_i8k->owner = THIS_MODULE;
765
766    printk(KERN_INFO
767	   "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
768	   I8K_VERSION);
769
770    return 0;
771}
772
773#ifdef MODULE
774int init_module(void)
775{
776    return i8k_init();
777}
778
779void cleanup_module(void)
780{
781    /* Remove the proc entry */
782    remove_proc_entry("i8k", NULL);
783
784    printk(KERN_INFO "i8k: module unloaded\n");
785}
786#endif
787
788/* end of file */