linux/drivers/char/i8k.c
<<
>>
Prefs
   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(&regs) ? : 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(&regs)) < 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(&regs)) < 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(&regs) ? : 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(&regs) ? : (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(&regs) ? : 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(&regs)) < 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(&regs)) < 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.