⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 applesmc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -