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 * Hwmon integration: 9 * Copyright (C) 2011 Jean Delvare <khali@linux-fr.org> 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the 13 * Free Software Foundation; either version 2, or (at your option) any 14 * later version. 15 * 16 * This program is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 * General Public License for more details. 20 */ 21 22#include <linux/module.h> 23#include <linux/types.h> 24#include <linux/init.h> 25#include <linux/proc_fs.h> 26#include <linux/seq_file.h> 27#include <linux/dmi.h> 28#include <linux/capability.h> 29#include <linux/mutex.h> 30#include <linux/hwmon.h> 31#include <linux/hwmon-sysfs.h> 32#include <asm/uaccess.h> 33#include <asm/io.h> 34 35#include <linux/i8k.h> 36 37#define I8K_VERSION "1.14 21/02/2005" 38 39#define I8K_SMM_FN_STATUS 0x0025 40#define I8K_SMM_POWER_STATUS 0x0069 41#define I8K_SMM_SET_FAN 0x01a3 42#define I8K_SMM_GET_FAN 0x00a3 43#define I8K_SMM_GET_SPEED 0x02a3 44#define I8K_SMM_GET_TEMP 0x10a3 45#define I8K_SMM_GET_DELL_SIG1 0xfea3 46#define I8K_SMM_GET_DELL_SIG2 0xffa3 47#define I8K_SMM_BIOS_VERSION 0x00a6 48 49#define I8K_FAN_MULT 30 50#define I8K_MAX_TEMP 127 51 52#define I8K_FN_NONE 0x00 53#define I8K_FN_UP 0x01 54#define I8K_FN_DOWN 0x02 55#define I8K_FN_MUTE 0x04 56#define I8K_FN_MASK 0x07 57#define I8K_FN_SHIFT 8 58 59#define I8K_POWER_AC 0x05 60#define I8K_POWER_BATTERY 0x01 61 62#define I8K_TEMPERATURE_BUG 1 63 64static DEFINE_MUTEX(i8k_mutex); 65static char bios_version[4]; 66static struct device *i8k_hwmon_dev; 67 68MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); 69MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops"); 70MODULE_LICENSE("GPL"); 71 72static bool force; 73module_param(force, bool, 0); 74MODULE_PARM_DESC(force, "Force loading without checking for supported models"); 75 76static bool ignore_dmi; 77module_param(ignore_dmi, bool, 0); 78MODULE_PARM_DESC(ignore_dmi, "Continue probing hardware even if DMI data does not match"); 79 80static bool restricted; 81module_param(restricted, bool, 0); 82MODULE_PARM_DESC(restricted, "Allow fan control if SYS_ADMIN capability set"); 83 84static bool power_status; 85module_param(power_status, bool, 0600); 86MODULE_PARM_DESC(power_status, "Report power status in /proc/i8k"); 87 88static int fan_mult = I8K_FAN_MULT; 89module_param(fan_mult, int, 0); 90MODULE_PARM_DESC(fan_mult, "Factor to multiply fan speed with"); 91 92static int i8k_open_fs(struct inode *inode, struct file *file); 93static long i8k_ioctl(struct file *, unsigned int, unsigned long); 94 95static const struct file_operations i8k_fops = { 96 .owner = THIS_MODULE, 97 .open = i8k_open_fs, 98 .read = seq_read, 99 .llseek = seq_lseek, 100 .release = single_release, 101 .unlocked_ioctl = i8k_ioctl, 102}; 103 104struct smm_regs { 105 unsigned int eax; 106 unsigned int ebx __attribute__ ((packed)); 107 unsigned int ecx __attribute__ ((packed)); 108 unsigned int edx __attribute__ ((packed)); 109 unsigned int esi __attribute__ ((packed)); 110 unsigned int edi __attribute__ ((packed)); 111}; 112 113static inline const char *i8k_get_dmi_data(int field) 114{ 115 const char *dmi_data = dmi_get_system_info(field); 116 117 return dmi_data && *dmi_data ? dmi_data : "?"; 118} 119 120/* 121 * Call the System Management Mode BIOS. Code provided by Jonathan Buzzard. 122 */ 123static int i8k_smm(struct smm_regs *regs) 124{ 125 int rc; 126 int eax = regs->eax; 127 128#if defined(CONFIG_X86_64) 129 asm volatile("pushq %%rax\n\t" 130 "movl 0(%%rax),%%edx\n\t" 131 "pushq %%rdx\n\t" 132 "movl 4(%%rax),%%ebx\n\t" 133 "movl 8(%%rax),%%ecx\n\t" 134 "movl 12(%%rax),%%edx\n\t" 135 "movl 16(%%rax),%%esi\n\t" 136 "movl 20(%%rax),%%edi\n\t" 137 "popq %%rax\n\t" 138 "out %%al,$0xb2\n\t" 139 "out %%al,$0x84\n\t" 140 "xchgq %%rax,(%%rsp)\n\t" 141 "movl %%ebx,4(%%rax)\n\t" 142 "movl %%ecx,8(%%rax)\n\t" 143 "movl %%edx,12(%%rax)\n\t" 144 "movl %%esi,16(%%rax)\n\t" 145 "movl %%edi,20(%%rax)\n\t" 146 "popq %%rdx\n\t" 147 "movl %%edx,0(%%rax)\n\t" 148 "pushfq\n\t" 149 "popq %%rax\n\t" 150 "andl $1,%%eax\n" 151 :"=a"(rc) 152 : "a"(regs) 153 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 154#else 155 asm volatile("pushl %%eax\n\t" 156 "movl 0(%%eax),%%edx\n\t" 157 "push %%edx\n\t" 158 "movl 4(%%eax),%%ebx\n\t" 159 "movl 8(%%eax),%%ecx\n\t" 160 "movl 12(%%eax),%%edx\n\t" 161 "movl 16(%%eax),%%esi\n\t" 162 "movl 20(%%eax),%%edi\n\t" 163 "popl %%eax\n\t" 164 "out %%al,$0xb2\n\t" 165 "out %%al,$0x84\n\t" 166 "xchgl %%eax,(%%esp)\n\t" 167 "movl %%ebx,4(%%eax)\n\t" 168 "movl %%ecx,8(%%eax)\n\t" 169 "movl %%edx,12(%%eax)\n\t" 170 "movl %%esi,16(%%eax)\n\t" 171 "movl %%edi,20(%%eax)\n\t" 172 "popl %%edx\n\t" 173 "movl %%edx,0(%%eax)\n\t" 174 "lahf\n\t" 175 "shrl $8,%%eax\n\t" 176 "andl $1,%%eax\n" 177 :"=a"(rc) 178 : "a"(regs) 179 : "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory"); 180#endif 181 if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) 182 return -EINVAL; 183 184 return 0; 185} 186 187/* 188 * Read the bios version. Return the version as an integer corresponding 189 * to the ascii value, for example "A17" is returned as 0x00413137. 190 */ 191static int i8k_get_bios_version(void) 192{ 193 struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, }; 194 195 return i8k_smm(®s) ? : regs.eax; 196} 197 198/* 199 * Read the Fn key status. 200 */ 201static int i8k_get_fn_status(void) 202{ 203 struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, }; 204 int rc; 205 206 if ((rc = i8k_smm(®s)) < 0) 207 return rc; 208 209 switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) { 210 case I8K_FN_UP: 211 return I8K_VOL_UP; 212 case I8K_FN_DOWN: 213 return I8K_VOL_DOWN; 214 case I8K_FN_MUTE: 215 return I8K_VOL_MUTE; 216 default: 217 return 0; 218 } 219} 220 221/* 222 * Read the power status. 223 */ 224static int i8k_get_power_status(void) 225{ 226 struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, }; 227 int rc; 228 229 if ((rc = i8k_smm(®s)) < 0) 230 return rc; 231 232 return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY; 233} 234 235/* 236 * Read the fan status. 237 */ 238static int i8k_get_fan_status(int fan) 239{ 240 struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; 241 242 regs.ebx = fan & 0xff; 243 return i8k_smm(®s) ? : regs.eax & 0xff; 244} 245 246/* 247 * Read the fan speed in RPM. 248 */ 249static int i8k_get_fan_speed(int fan) 250{ 251 struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; 252 253 regs.ebx = fan & 0xff; 254 return i8k_smm(®s) ? : (regs.eax & 0xffff) * fan_mult; 255} 256 257/* 258 * Set the fan speed (off, low, high). Returns the new fan status. 259 */ 260static int i8k_set_fan(int fan, int speed) 261{ 262 struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; 263 264 speed = (speed < 0) ? 0 : ((speed > I8K_FAN_MAX) ? I8K_FAN_MAX : speed); 265 regs.ebx = (fan & 0xff) | (speed << 8); 266 267 return i8k_smm(®s) ? : i8k_get_fan_status(fan); 268} 269 270/* 271 * Read the cpu temperature. 272 */ 273static int i8k_get_temp(int sensor) 274{ 275 struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP, }; 276 int rc; 277 int temp; 278 279#ifdef I8K_TEMPERATURE_BUG 280 static int prev; 281#endif 282 regs.ebx = sensor & 0xff; 283 if ((rc = i8k_smm(®s)) < 0) 284 return rc; 285 286 temp = regs.eax & 0xff; 287 288#ifdef I8K_TEMPERATURE_BUG 289 /* 290 * Sometimes the temperature sensor returns 0x99, which is out of range. 291 * In this case we return (once) the previous cached value. For example: 292 # 1003655137 00000058 00005a4b 293 # 1003655138 00000099 00003a80 <--- 0x99 = 153 degrees 294 # 1003655139 00000054 00005c52 295 */ 296 if (temp > I8K_MAX_TEMP) { 297 temp = prev; 298 prev = I8K_MAX_TEMP; 299 } else { 300 prev = temp; 301 } 302#endif 303 304 return temp; 305} 306 307static int i8k_get_dell_signature(int req_fn) 308{ 309 struct smm_regs regs = { .eax = req_fn, }; 310 int rc; 311 312 if ((rc = i8k_smm(®s)) < 0) 313 return rc; 314 315 return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1; 316} 317 318static int 319i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) 320{ 321 int val = 0; 322 int speed; 323 unsigned char buff[16]; 324 int __user *argp = (int __user *)arg; 325 326 if (!argp) 327 return -EINVAL; 328 329 switch (cmd) { 330 case I8K_BIOS_VERSION: 331 val = i8k_get_bios_version(); 332 break; 333 334 case I8K_MACHINE_ID: 335 memset(buff, 0, 16); 336 strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff)); 337 break; 338 339 case I8K_FN_STATUS: 340 val = i8k_get_fn_status(); 341 break; 342 343 case I8K_POWER_STATUS: 344 val = i8k_get_power_status(); 345 break; 346 347 case I8K_GET_TEMP: 348 val = i8k_get_temp(0); 349 break; 350 351 case I8K_GET_SPEED: 352 if (copy_from_user(&val, argp, sizeof(int))) 353 return -EFAULT; 354 355 val = i8k_get_fan_speed(val); 356 break; 357 358 case I8K_GET_FAN: 359 if (copy_from_user(&val, argp, sizeof(int))) 360 return -EFAULT; 361 362 val = i8k_get_fan_status(val); 363 break; 364 365 case I8K_SET_FAN: 366 if (restricted && !capable(CAP_SYS_ADMIN)) 367 return -EPERM; 368 369 if (copy_from_user(&val, argp, sizeof(int))) 370 return -EFAULT; 371 372 if (copy_from_user(&speed, argp + 1, sizeof(int))) 373 return -EFAULT; 374 375 val = i8k_set_fan(val, speed); 376 break; 377 378 default: 379 return -EINVAL; 380 } 381 382 if (val < 0) 383 return val; 384 385 switch (cmd) { 386 case I8K_BIOS_VERSION: 387 if (copy_to_user(argp, &val, 4)) 388 return -EFAULT; 389 390 break; 391 case I8K_MACHINE_ID: 392 if (copy_to_user(argp, buff, 16)) 393 return -EFAULT; 394 395 break; 396 default: 397 if (copy_to_user(argp, &val, sizeof(int))) 398 return -EFAULT; 399 400 break; 401 } 402 403 return 0; 404} 405 406static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 407{ 408 long ret; 409 410 mutex_lock(&i8k_mutex); 411 ret = i8k_ioctl_unlocked(fp, cmd, arg); 412 mutex_unlock(&i8k_mutex); 413 414 return ret; 415} 416 417/* 418 * Print the information for /proc/i8k. 419 */ 420static int i8k_proc_show(struct seq_file *seq, void *offset) 421{ 422 int fn_key, cpu_temp, ac_power; 423 int left_fan, right_fan, left_speed, right_speed; 424 425 cpu_temp = i8k_get_temp(0); /* 11100 µs */ 426 left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ 427 right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ 428 left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ 429 right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ 430 fn_key = i8k_get_fn_status(); /* 750 µs */ 431 if (power_status) 432 ac_power = i8k_get_power_status(); /* 14700 µs */ 433 else 434 ac_power = -1; 435 436 /* 437 * Info: 438 * 439 * 1) Format version (this will change if format changes) 440 * 2) BIOS version 441 * 3) BIOS machine ID 442 * 4) Cpu temperature 443 * 5) Left fan status 444 * 6) Right fan status 445 * 7) Left fan speed 446 * 8) Right fan speed 447 * 9) AC power 448 * 10) Fn Key status 449 */ 450 return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", 451 I8K_PROC_FMT, 452 bios_version, 453 i8k_get_dmi_data(DMI_PRODUCT_SERIAL), 454 cpu_temp, 455 left_fan, right_fan, left_speed, right_speed, 456 ac_power, fn_key); 457} 458 459static int i8k_open_fs(struct inode *inode, struct file *file) 460{ 461 return single_open(file, i8k_proc_show, NULL); 462} 463 464 465/* 466 * Hwmon interface 467 */ 468 469static ssize_t i8k_hwmon_show_temp(struct device *dev, 470 struct device_attribute *devattr, 471 char *buf) 472{ 473 int cpu_temp; 474 475 cpu_temp = i8k_get_temp(0); 476 if (cpu_temp < 0) 477 return cpu_temp; 478 return sprintf(buf, "%d\n", cpu_temp * 1000); 479} 480 481static ssize_t i8k_hwmon_show_fan(struct device *dev, 482 struct device_attribute *devattr, 483 char *buf) 484{ 485 int index = to_sensor_dev_attr(devattr)->index; 486 int fan_speed; 487 488 fan_speed = i8k_get_fan_speed(index); 489 if (fan_speed < 0) 490 return fan_speed; 491 return sprintf(buf, "%d\n", fan_speed); 492} 493 494static ssize_t i8k_hwmon_show_label(struct device *dev, 495 struct device_attribute *devattr, 496 char *buf) 497{ 498 static const char *labels[4] = { 499 "i8k", 500 "CPU", 501 "Left Fan", 502 "Right Fan", 503 }; 504 int index = to_sensor_dev_attr(devattr)->index; 505 506 return sprintf(buf, "%s\n", labels[index]); 507} 508 509static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL); 510static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 511 I8K_FAN_LEFT); 512static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, 513 I8K_FAN_RIGHT); 514static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0); 515static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); 516static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); 517static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3); 518 519static void i8k_hwmon_remove_files(struct device *dev) 520{ 521 device_remove_file(dev, &dev_attr_temp1_input); 522 device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr); 523 device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr); 524 device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr); 525 device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr); 526 device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr); 527 device_remove_file(dev, &sensor_dev_attr_name.dev_attr); 528} 529 530static int __init i8k_init_hwmon(void) 531{ 532 int err; 533 534 i8k_hwmon_dev = hwmon_device_register(NULL); 535 if (IS_ERR(i8k_hwmon_dev)) { 536 err = PTR_ERR(i8k_hwmon_dev); 537 i8k_hwmon_dev = NULL; 538 printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err); 539 return err; 540 } 541 542 /* Required name attribute */ 543 err = device_create_file(i8k_hwmon_dev, 544 &sensor_dev_attr_name.dev_attr); 545 if (err) 546 goto exit_unregister; 547 548 /* CPU temperature attributes, if temperature reading is OK */ 549 err = i8k_get_temp(0); 550 if (err < 0) { 551 dev_dbg(i8k_hwmon_dev, 552 "Not creating temperature attributes (%d)\n", err); 553 } else { 554 err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input); 555 if (err) 556 goto exit_remove_files; 557 err = device_create_file(i8k_hwmon_dev, 558 &sensor_dev_attr_temp1_label.dev_attr); 559 if (err) 560 goto exit_remove_files; 561 } 562 563 /* Left fan attributes, if left fan is present */ 564 err = i8k_get_fan_status(I8K_FAN_LEFT); 565 if (err < 0) { 566 dev_dbg(i8k_hwmon_dev, 567 "Not creating %s fan attributes (%d)\n", "left", err); 568 } else { 569 err = device_create_file(i8k_hwmon_dev, 570 &sensor_dev_attr_fan1_input.dev_attr); 571 if (err) 572 goto exit_remove_files; 573 err = device_create_file(i8k_hwmon_dev, 574 &sensor_dev_attr_fan1_label.dev_attr); 575 if (err) 576 goto exit_remove_files; 577 } 578 579 /* Right fan attributes, if right fan is present */ 580 err = i8k_get_fan_status(I8K_FAN_RIGHT); 581 if (err < 0) { 582 dev_dbg(i8k_hwmon_dev, 583 "Not creating %s fan attributes (%d)\n", "right", err); 584 } else { 585 err = device_create_file(i8k_hwmon_dev, 586 &sensor_dev_attr_fan2_input.dev_attr); 587 if (err) 588 goto exit_remove_files; 589 err = device_create_file(i8k_hwmon_dev, 590 &sensor_dev_attr_fan2_label.dev_attr); 591 if (err) 592 goto exit_remove_files; 593 } 594 595 return 0; 596 597 exit_remove_files: 598 i8k_hwmon_remove_files(i8k_hwmon_dev); 599 exit_unregister: 600 hwmon_device_unregister(i8k_hwmon_dev); 601 return err; 602} 603 604static void __exit i8k_exit_hwmon(void) 605{ 606 i8k_hwmon_remove_files(i8k_hwmon_dev); 607 hwmon_device_unregister(i8k_hwmon_dev); 608} 609 610static struct dmi_system_id __initdata i8k_dmi_table[] = { 611 { 612 .ident = "Dell Inspiron", 613 .matches = { 614 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 615 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 616 }, 617 }, 618 { 619 .ident = "Dell Latitude", 620 .matches = { 621 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer"), 622 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 623 }, 624 }, 625 { 626 .ident = "Dell Inspiron 2", 627 .matches = { 628 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 629 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron"), 630 }, 631 }, 632 { 633 .ident = "Dell Latitude 2", 634 .matches = { 635 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 636 DMI_MATCH(DMI_PRODUCT_NAME, "Latitude"), 637 }, 638 }, 639 { /* UK Inspiron 6400 */ 640 .ident = "Dell Inspiron 3", 641 .matches = { 642 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 643 DMI_MATCH(DMI_PRODUCT_NAME, "MM061"), 644 }, 645 }, 646 { 647 .ident = "Dell Inspiron 3", 648 .matches = { 649 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 650 DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), 651 }, 652 }, 653 { 654 .ident = "Dell Precision", 655 .matches = { 656 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 657 DMI_MATCH(DMI_PRODUCT_NAME, "Precision"), 658 }, 659 }, 660 { 661 .ident = "Dell Vostro", 662 .matches = { 663 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 664 DMI_MATCH(DMI_PRODUCT_NAME, "Vostro"), 665 }, 666 }, 667 { } 668}; 669 670/* 671 * Probe for the presence of a supported laptop. 672 */ 673static int __init i8k_probe(void) 674{ 675 char buff[4]; 676 int version; 677 678 /* 679 * Get DMI information 680 */ 681 if (!dmi_check_system(i8k_dmi_table)) { 682 if (!ignore_dmi && !force) 683 return -ENODEV; 684 685 printk(KERN_INFO "i8k: not running on a supported Dell system.\n"); 686 printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n", 687 i8k_get_dmi_data(DMI_SYS_VENDOR), 688 i8k_get_dmi_data(DMI_PRODUCT_NAME), 689 i8k_get_dmi_data(DMI_BIOS_VERSION)); 690 } 691 692 strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version)); 693 694 /* 695 * Get SMM Dell signature 696 */ 697 if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && 698 i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { 699 printk(KERN_ERR "i8k: unable to get SMM Dell signature\n"); 700 if (!force) 701 return -ENODEV; 702 } 703 704 /* 705 * Get SMM BIOS version. 706 */ 707 version = i8k_get_bios_version(); 708 if (version <= 0) { 709 printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n"); 710 } else { 711 buff[0] = (version >> 16) & 0xff; 712 buff[1] = (version >> 8) & 0xff; 713 buff[2] = (version) & 0xff; 714 buff[3] = '\0'; 715 /* 716 * If DMI BIOS version is unknown use SMM BIOS version. 717 */ 718 if (!dmi_get_system_info(DMI_BIOS_VERSION)) 719 strlcpy(bios_version, buff, sizeof(bios_version)); 720 721 /* 722 * Check if the two versions match. 723 */ 724 if (strncmp(buff, bios_version, sizeof(bios_version)) != 0) 725 printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n", 726 buff, bios_version); 727 } 728 729 return 0; 730} 731 732static int __init i8k_init(void) 733{ 734 struct proc_dir_entry *proc_i8k; 735 int err; 736 737 /* Are we running on an supported laptop? */ 738 if (i8k_probe()) 739 return -ENODEV; 740 741 /* Register the proc entry */ 742 proc_i8k = proc_create("i8k", 0, NULL, &i8k_fops); 743 if (!proc_i8k) 744 return -ENOENT; 745 746 err = i8k_init_hwmon(); 747 if (err) 748 goto exit_remove_proc; 749 750 printk(KERN_INFO 751 "Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n", 752 I8K_VERSION); 753 754 return 0; 755 756 exit_remove_proc: 757 remove_proc_entry("i8k", NULL); 758 return err; 759} 760 761static void __exit i8k_exit(void) 762{ 763 i8k_exit_hwmon(); 764 remove_proc_entry("i8k", NULL); 765} 766 767module_init(i8k_init); 768module_exit(i8k_exit); 769