📄 applesmc.c
字号:
/* * drivers/hwmon/applesmc.c - driver for Apple's SMC (accelerometer, temperature * sensors, fan control, keyboard backlight control) used in Intel-based Apple * computers. * * Copyright (C) 2007 Nicolas Boichat <nicolas@boichat.ch> * * Based on hdaps.c driver: * Copyright (C) 2005 Robert Love <rml@novell.com> * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com> * * Fan control based on smcFanControl: * Copyright (C) 2006 Hendrik Holtmann <holtmann@mac.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License v2 as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/input-polldev.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/timer.h>#include <linux/dmi.h>#include <linux/mutex.h>#include <linux/hwmon-sysfs.h>#include <asm/io.h>#include <linux/leds.h>#include <linux/hwmon.h>#include <linux/workqueue.h>/* data port used by Apple SMC */#define APPLESMC_DATA_PORT 0x300/* command/status port used by Apple SMC */#define APPLESMC_CMD_PORT 0x304#define APPLESMC_NR_PORTS 32 /* 0x300-0x31f */#define APPLESMC_MAX_DATA_LENGTH 32#define APPLESMC_STATUS_MASK 0x0f#define APPLESMC_READ_CMD 0x10#define APPLESMC_WRITE_CMD 0x11#define APPLESMC_GET_KEY_BY_INDEX_CMD 0x12#define APPLESMC_GET_KEY_TYPE_CMD 0x13#define KEY_COUNT_KEY "#KEY" /* r-o ui32 */#define LIGHT_SENSOR_LEFT_KEY "ALV0" /* r-o {alv (6 bytes) */#define LIGHT_SENSOR_RIGHT_KEY "ALV1" /* r-o {alv (6 bytes) */#define BACKLIGHT_KEY "LKSB" /* w-o {lkb (2 bytes) */#define CLAMSHELL_KEY "MSLD" /* r-o ui8 (unused) */#define MOTION_SENSOR_X_KEY "MO_X" /* r-o sp78 (2 bytes) */#define MOTION_SENSOR_Y_KEY "MO_Y" /* r-o sp78 (2 bytes) */#define MOTION_SENSOR_Z_KEY "MO_Z" /* r-o sp78 (2 bytes) */#define MOTION_SENSOR_KEY "MOCN" /* r/w ui16 */#define FANS_COUNT "FNum" /* r-o ui8 */#define FANS_MANUAL "FS! " /* r-w ui16 */#define FAN_ACTUAL_SPEED "F0Ac" /* r-o fpe2 (2 bytes) */#define FAN_MIN_SPEED "F0Mn" /* r-o fpe2 (2 bytes) */#define FAN_MAX_SPEED "F0Mx" /* r-o fpe2 (2 bytes) */#define FAN_SAFE_SPEED "F0Sf" /* r-o fpe2 (2 bytes) */#define FAN_TARGET_SPEED "F0Tg" /* r-w fpe2 (2 bytes) */#define FAN_POSITION "F0ID" /* r-o char[16] *//* * Temperature sensors keys (sp78 - 2 bytes). */static const char* temperature_sensors_sets[][36] = {/* Set 0: Macbook Pro */ { "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H", "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL },/* Set 1: Macbook set */ { "TB0T", "TC0D", "TC0P", "TM0P", "TN0P", "TN1P", "Th0H", "Th0S", "Th1H", "Ts0P", NULL },/* Set 2: Macmini set */ { "TC0D", "TC0P", NULL },/* Set 3: Mac Pro (2 x Quad-Core) */ { "TA0P", "TCAG", "TCAH", "TCBG", "TCBH", "TC0C", "TC0D", "TC0P", "TC1C", "TC1D", "TC2C", "TC2D", "TC3C", "TC3D", "THTG", "TH0P", "TH1P", "TH2P", "TH3P", "TMAP", "TMAS", "TMBS", "TM0P", "TM0S", "TM1P", "TM1S", "TM2P", "TM2S", "TM3S", "TM8P", "TM8S", "TM9P", "TM9S", "TN0H", "TS0C", NULL },};/* List of keys used to read/write fan speeds */static const char* fan_speed_keys[] = { FAN_ACTUAL_SPEED, FAN_MIN_SPEED, FAN_MAX_SPEED, FAN_SAFE_SPEED, FAN_TARGET_SPEED};#define INIT_TIMEOUT_MSECS 5000 /* wait up to 5s for device init ... */#define INIT_WAIT_MSECS 50 /* ... in 50ms increments */#define APPLESMC_POLL_INTERVAL 50 /* msecs */#define APPLESMC_INPUT_FUZZ 4 /* input event threshold */#define APPLESMC_INPUT_FLAT 4#define SENSOR_X 0#define SENSOR_Y 1#define SENSOR_Z 2/* Structure to be passed to DMI_MATCH function */struct dmi_match_data {/* Indicates whether this computer has an accelerometer. */ int accelerometer;/* Indicates whether this computer has light sensors and keyboard backlight. */ int light;/* Indicates which temperature sensors set to use. */ int temperature_set;};static const int debug;static struct platform_device *pdev;static s16 rest_x;static s16 rest_y;static struct device *hwmon_dev;static struct input_polled_dev *applesmc_idev;/* Indicates whether this computer has an accelerometer. */static unsigned int applesmc_accelerometer;/* Indicates whether this computer has light sensors and keyboard backlight. */static unsigned int applesmc_light;/* Indicates which temperature sensors set to use. */static unsigned int applesmc_temperature_set;static DEFINE_MUTEX(applesmc_lock);/* * Last index written to key_at_index sysfs file, and value to use for all other * key_at_index_* sysfs files. */static unsigned int key_at_index;static struct workqueue_struct *applesmc_led_wq;/* * __wait_status - Wait up to 2ms for the status port to get a certain value * (masked with 0x0f), returning zero if the value is obtained. Callers must * hold applesmc_lock. */static int __wait_status(u8 val){ unsigned int i; val = val & APPLESMC_STATUS_MASK; for (i = 0; i < 200; i++) { if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val) { if (debug) printk(KERN_DEBUG "Waited %d us for status %x\n", i*10, val); return 0; } udelay(10); } printk(KERN_WARNING "applesmc: wait status failed: %x != %x\n", val, inb(APPLESMC_CMD_PORT)); return -EIO;}/* * applesmc_read_key - reads len bytes from a given key, and put them in buffer. * Returns zero on success or a negative error on failure. Callers must * hold applesmc_lock. */static int applesmc_read_key(const char* key, u8* buffer, u8 len){ int i; if (len > APPLESMC_MAX_DATA_LENGTH) { printk(KERN_ERR "applesmc_read_key: cannot read more than " "%d bytes\n", APPLESMC_MAX_DATA_LENGTH); return -EINVAL; } outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT); if (__wait_status(0x0c)) return -EIO; for (i = 0; i < 4; i++) { outb(key[i], APPLESMC_DATA_PORT); if (__wait_status(0x04)) return -EIO; } if (debug) printk(KERN_DEBUG "<%s", key); outb(len, APPLESMC_DATA_PORT); if (debug) printk(KERN_DEBUG ">%x", len); for (i = 0; i < len; i++) { if (__wait_status(0x05)) return -EIO; buffer[i] = inb(APPLESMC_DATA_PORT); if (debug) printk(KERN_DEBUG "<%x", buffer[i]); } if (debug) printk(KERN_DEBUG "\n"); return 0;}/* * applesmc_write_key - writes len bytes from buffer to a given key. * Returns zero on success or a negative error on failure. Callers must * hold applesmc_lock. */static int applesmc_write_key(const char* key, u8* buffer, u8 len){ int i; if (len > APPLESMC_MAX_DATA_LENGTH) { printk(KERN_ERR "applesmc_write_key: cannot write more than " "%d bytes\n", APPLESMC_MAX_DATA_LENGTH); return -EINVAL; } outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT); if (__wait_status(0x0c)) return -EIO; for (i = 0; i < 4; i++) { outb(key[i], APPLESMC_DATA_PORT); if (__wait_status(0x04)) return -EIO; } outb(len, APPLESMC_DATA_PORT); for (i = 0; i < len; i++) { if (__wait_status(0x04)) return -EIO; outb(buffer[i], APPLESMC_DATA_PORT); } return 0;}/* * applesmc_get_key_at_index - get key at index, and put the result in key * (char[6]). Returns zero on success or a negative error on failure. Callers * must hold applesmc_lock. */static int applesmc_get_key_at_index(int index, char* key){ int i; u8 readkey[4]; readkey[0] = index >> 24; readkey[1] = index >> 16; readkey[2] = index >> 8; readkey[3] = index; outb(APPLESMC_GET_KEY_BY_INDEX_CMD, APPLESMC_CMD_PORT); if (__wait_status(0x0c)) return -EIO; for (i = 0; i < 4; i++) { outb(readkey[i], APPLESMC_DATA_PORT); if (__wait_status(0x04)) return -EIO; } outb(4, APPLESMC_DATA_PORT); for (i = 0; i < 4; i++) { if (__wait_status(0x05)) return -EIO; key[i] = inb(APPLESMC_DATA_PORT); } key[4] = 0; return 0;}/* * applesmc_get_key_type - get key type, and put the result in type (char[6]). * Returns zero on success or a negative error on failure. Callers must * hold applesmc_lock. */static int applesmc_get_key_type(char* key, char* type){ int i; outb(APPLESMC_GET_KEY_TYPE_CMD, APPLESMC_CMD_PORT); if (__wait_status(0x0c)) return -EIO; for (i = 0; i < 4; i++) { outb(key[i], APPLESMC_DATA_PORT); if (__wait_status(0x04)) return -EIO; } outb(5, APPLESMC_DATA_PORT); for (i = 0; i < 6; i++) { if (__wait_status(0x05)) return -EIO; type[i] = inb(APPLESMC_DATA_PORT); } type[5] = 0; return 0;}/* * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must * hold applesmc_lock. */static int applesmc_read_motion_sensor(int index, s16* value){ u8 buffer[2]; int ret; switch (index) { case SENSOR_X: ret = applesmc_read_key(MOTION_SENSOR_X_KEY, buffer, 2); break; case SENSOR_Y: ret = applesmc_read_key(MOTION_SENSOR_Y_KEY, buffer, 2); break; case SENSOR_Z: ret = applesmc_read_key(MOTION_SENSOR_Z_KEY, buffer, 2); break; default: ret = -EINVAL; } *value = ((s16)buffer[0] << 8) | buffer[1]; return ret;}/* * applesmc_device_init - initialize the accelerometer. Returns zero on success * and negative error code on failure. Can sleep. */static int applesmc_device_init(void){ int total, ret = -ENXIO; u8 buffer[2]; if (!applesmc_accelerometer) return 0; mutex_lock(&applesmc_lock); for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) { if (debug) printk(KERN_DEBUG "applesmc try %d\n", total); if (!applesmc_read_key(MOTION_SENSOR_KEY, buffer, 2) && (buffer[0] != 0x00 || buffer[1] != 0x00)) { if (total == INIT_TIMEOUT_MSECS) { printk(KERN_DEBUG "applesmc: device has" " already been initialized" " (0x%02x, 0x%02x).\n", buffer[0], buffer[1]); } else { printk(KERN_DEBUG "applesmc: device" " successfully initialized" " (0x%02x, 0x%02x).\n", buffer[0], buffer[1]); } ret = 0; goto out; } buffer[0] = 0xe0; buffer[1] = 0x00; applesmc_write_key(MOTION_SENSOR_KEY, buffer, 2); msleep(INIT_WAIT_MSECS); } printk(KERN_WARNING "applesmc: failed to init the device\n");out: mutex_unlock(&applesmc_lock); return ret;}/* * applesmc_get_fan_count - get the number of fans. Callers must NOT hold * applesmc_lock. */static int applesmc_get_fan_count(void){ int ret; u8 buffer[1]; mutex_lock(&applesmc_lock); ret = applesmc_read_key(FANS_COUNT, buffer, 1); mutex_unlock(&applesmc_lock); if (ret) return ret; else return buffer[0];}/* Device model stuff */static int applesmc_probe(struct platform_device *dev){ int ret; ret = applesmc_device_init(); if (ret) return ret; printk(KERN_INFO "applesmc: device successfully initialized.\n"); return 0;}static int applesmc_resume(struct platform_device *dev){ return applesmc_device_init();}static struct platform_driver applesmc_driver = { .probe = applesmc_probe, .resume = applesmc_resume, .driver = { .name = "applesmc", .owner = THIS_MODULE, },};/* * applesmc_calibrate - Set our "resting" values. Callers must * hold applesmc_lock. */static void applesmc_calibrate(void){ applesmc_read_motion_sensor(SENSOR_X, &rest_x); applesmc_read_motion_sensor(SENSOR_Y, &rest_y); rest_x = -rest_x;}static void applesmc_idev_poll(struct input_polled_dev *dev){ struct input_dev *idev = dev->input; s16 x, y; mutex_lock(&applesmc_lock); if (applesmc_read_motion_sensor(SENSOR_X, &x)) goto out; if (applesmc_read_motion_sensor(SENSOR_Y, &y)) goto out; x = -x; input_report_abs(idev, ABS_X, x - rest_x); input_report_abs(idev, ABS_Y, y - rest_y); input_sync(idev);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -