All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
@ 2007-03-14  9:29 ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-14  9:29 UTC (permalink / raw)
  To: linux-kernel; +Cc: lm-sensors, rlove, linux-kernel

Hello,

I developed, a while ago, a driver the Apple System Management
Controller, which provides an accelerometer (Apple Sudden Motion
Sensor), light sensors, temperature sensors, keyboard backlight control
and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
MacMini).

This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
months ago) by various users on different systems on the mactel-linux lists.

However, I'm not really satisfied with the way sysfs files are created:
I use a lot of preprocessor macros to avoid repetition of code.
The files created with these macros in /sys/devices/platform/applesmc are
the following (on a Macbook Pro):
fan0_actual_speed
fan0_manual
fan0_maximum_speed
fan0_minimum_speed
fan0_safe_speed
fan0_target_speed
fan1_actual_speed
fan1_manual
fan1_maximum_speed
fan1_minimum_speed
fan1_safe_speed
fan1_target_speed
temperature_0
temperature_1
temperature_2
temperature_3
temperature_4
temperature_5
temperature_6

(i.e. temperature_* is created by one macro, fan*_actual_speed by
another, ...)
Is it acceptable programming practice? Is there a way to create these
files in a more elegant manner?

Also, I never call any sysfs_remove_* function, as the files are
deleted when the module is unloaded. Is it safe to do so? Doesn't it
cause any memory leak?

This is my main concerns, however, I would be happy to have comments
on the other parts of the code. (Please cc me I'm not subscribed to
lkml)

Best regards,

Nicolas Boichat

From: Nicolas Boichat <nicolas@boichat.ch>


---

 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  964 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 989 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index c3d4856..798b91d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -593,6 +593,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..3bdd1a8
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,964 @@
+/*
+ * 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/dmi.h>
+#include <asm/io.h>
+#include <linux/leds.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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" //r-o length 6
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" //r-o length 6
+#define BACKLIGHT_KEY 		"LKSB" //w-o
+
+#define CLAMSHELL_KEY 		"MSLD" //r-o length 1 (unused)
+
+#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
+#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
+#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
+#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
+
+#define FANS_COUNT		"FNum" //r-o length 1
+#define FANS_MANUAL		"FS! " //r-w length 2
+#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
+#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
+#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
+#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
+#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", NULL }
+};
+
+#define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */
+#define INIT_WAIT_MSECS		50	/* ... in 50ms increments */
+
+#define APPLESMC_POLL_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_dev *applesmc_idev;
+
+/* Indicates whether this computer has an accelerometer. */
+static unsigned int applesmc_accelerometer = 0;
+
+/* Indicates whether this computer has light sensors and keyboard backlight. */
+static unsigned int applesmc_light = 0;
+
+/* Indicates which temperature sensors set to use. */
+static unsigned int applesmc_temperature_set = 0;
+
+static DECLARE_MUTEX(applesmc_sem);
+
+/*
+ * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * (masked with 0x0f), returning zero if the value is obtained.  Callers must
+ * hold applesmc_sem.
+ */
+static int __wait_status(u8 val)
+{
+	unsigned int i;
+
+	val = val & APPLESMC_STATUS_MASK;
+
+	for (i = 0; i < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 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_sem.
+ */
+static int applesmc_read_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug) printk(KERN_DEBUG "\n");
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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_sem.
+ */
+static int applesmc_write_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
+ * hold applesmc_sem.
+ */
+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;
+
+	down(&applesmc_sem);
+
+	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:
+	up(&applesmc_sem);
+	return ret;
+}
+
+/*
+ * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
+ * applesmc_sem.
+ */
+static int applesmc_get_fan_count(void)
+{
+	int ret;
+	u8 buffer[1];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_COUNT, buffer, 1);
+
+	up(&applesmc_sem);
+	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_sem.
+ */
+static void applesmc_calibrate(void)
+{
+	applesmc_read_motion_sensor(SENSOR_X, &rest_x);
+	applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (down_trylock(&applesmc_sem)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	up(&applesmc_sem);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d)\n", left, right);
+}
+
+/* Displays °C * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev, char *buf,
+								const char *key)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	up(&applesmc_sem);
+
+	if (ret)
+	return ret;
+	else
+	return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev, char *buf,
+						const char* key, int offset)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev, const char *buf,
+				size_t count, const char* key, int offset)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+
+	speed = simple_strtoul(buf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev, char *buf,
+								int offset)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev, const char *buf,
+						size_t count, int offset)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+
+	input = simple_strtoul(buf, NULL, 10);
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << offset);
+	else 
+		val = val & ~(0x01 << offset);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	down(&applesmc_sem);
+	applesmc_calibrate();
+	up(&applesmc_sem);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	down(&applesmc_sem);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	up(&applesmc_sem);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
+					show_fan_actual_speed_##offset, NULL); \
+\
+static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
+} \
+static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
+\
+static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
+				show_fan_maximum_speed_##offset, NULL); \
+\
+static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
+					show_fan_safe_speed_##offset, NULL); \
+\
+static ssize_t show_fan_target_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
+} \
+static ssize_t store_fan_target_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
+static ssize_t show_fan_manual_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_manual(dev, buf, offset); \
+} \
+static ssize_t store_fan_manual_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_manual(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+		   show_fan_manual_##offset, store_fan_manual_##offset);
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+/* Macro creating the sysfs entries for a fan */
+#define device_create_file_fan(ret, client, offset) \
+do { \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
+} while (0)
+
+/*
+ * Macro defining the helper function and DEVICE_ATTR for a temperature sensor
+ * sysfs entry.
+ */
+#define sysfs_temperature_offset(offset) \
+static ssize_t show_temperature_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_temperature(dev, buf, \
+		temperature_sensors_sets[applesmc_temperature_set][offset]);  \
+} \
+static DEVICE_ATTR(temperature_##offset, S_IRUGO, \
+					show_temperature_##offset, NULL);
+
+/*
+ * Create the needed functions for each temperature sensors using the macro
+ * defined above (7 temperature sensors are supported)
+ */
+sysfs_temperature_offset(0);
+sysfs_temperature_offset(1);
+sysfs_temperature_offset(2);
+sysfs_temperature_offset(3);
+sysfs_temperature_offset(4);
+sysfs_temperature_offset(5);
+sysfs_temperature_offset(6);
+
+/* Macro creating the sysfs entry for a temperature sensor */
+#define device_create_files_temperature(ret, client, offset) \
+{ \
+	ret = sysfs_create_file(client, &dev_attr_temperature_##offset.attr); \
+} while (0)
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data =
+					(struct dmi_match_data*)id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void) {
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void) {
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+
+	struct dmi_match_data applesmc_dmi_data[] = {
+	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
+	/* MacBook: accelerometer and temperature set 0 */
+	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
+	/* MacBook: temperature set 1 */
+	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
+	};
+
+	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+	struct dmi_system_id applesmc_whitelist[] = {
+		{ applesmc_dmi_match, "Apple MacBook Pro", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+			(void*)&applesmc_dmi_data[0]},
+		{ applesmc_dmi_match, "Apple MacBook", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+			(void*)&applesmc_dmi_data[1]},
+		{ applesmc_dmi_match, "Apple Macmini", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+			(void*)&applesmc_dmi_data[2]},
+		{ .ident = NULL }
+	};
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	}
+	else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			device_create_file_fan(ret, &pdev->dev.kobj, 1);
+			if (ret)
+				goto out_device;
+		case 1:
+			device_create_file_fan(ret, &pdev->dev.kobj, 0);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	count = 0;
+	while (temperature_sensors_sets[applesmc_temperature_set][count]
+									!= NULL)
+		count++;
+
+	switch (count) {
+	default:
+	case 7:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 6);
+		if (ret)
+			goto out_device;
+	case 6:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 5);
+		if (ret)
+			goto out_device;
+	case 5:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 4);
+		if (ret)
+			goto out_device;
+	case 4:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 3);
+		if (ret)
+			goto out_device;
+	case 3:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 2);
+		if (ret)
+			goto out_device;
+	case 2:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 1);
+		if (ret)
+			goto out_device;
+	case 1:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 0);
+		if (ret)
+			goto out_device;
+	case 0:
+		;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-14  9:29 ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-14  9:29 UTC (permalink / raw)
  To: linux-kernel; +Cc: lm-sensors, rlove, linux-kernel

Hello,

I developed, a while ago, a driver the Apple System Management
Controller, which provides an accelerometer (Apple Sudden Motion
Sensor), light sensors, temperature sensors, keyboard backlight control
and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
MacMini).

This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
months ago) by various users on different systems on the mactel-linux lists.

However, I'm not really satisfied with the way sysfs files are created:
I use a lot of preprocessor macros to avoid repetition of code.
The files created with these macros in /sys/devices/platform/applesmc are
the following (on a Macbook Pro):
fan0_actual_speed
fan0_manual
fan0_maximum_speed
fan0_minimum_speed
fan0_safe_speed
fan0_target_speed
fan1_actual_speed
fan1_manual
fan1_maximum_speed
fan1_minimum_speed
fan1_safe_speed
fan1_target_speed
temperature_0
temperature_1
temperature_2
temperature_3
temperature_4
temperature_5
temperature_6

(i.e. temperature_* is created by one macro, fan*_actual_speed by
another, ...)
Is it acceptable programming practice? Is there a way to create these
files in a more elegant manner?

Also, I never call any sysfs_remove_* function, as the files are
deleted when the module is unloaded. Is it safe to do so? Doesn't it
cause any memory leak?

This is my main concerns, however, I would be happy to have comments
on the other parts of the code. (Please cc me I'm not subscribed to
lkml)

Best regards,

Nicolas Boichat

From: Nicolas Boichat <nicolas at boichat.ch>


---

 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  964 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 989 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index c3d4856..798b91d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -593,6 +593,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..3bdd1a8
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,964 @@
+/*
+ * 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 at boichat.ch>
+ *
+ * Based on hdaps.c driver:
+ * Copyright (C) 2005 Robert Love <rml at novell.com>
+ * Copyright (C) 2005 Jesper Juhl <jesper.juhl at gmail.com>
+ *
+ * Fan control based on smcFanControl:
+ * Copyright (C) 2006 Hendrik Holtmann <holtmann at 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/dmi.h>
+#include <asm/io.h>
+#include <linux/leds.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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" //r-o length 6
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" //r-o length 6
+#define BACKLIGHT_KEY 		"LKSB" //w-o
+
+#define CLAMSHELL_KEY 		"MSLD" //r-o length 1 (unused)
+
+#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
+#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
+#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
+#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
+
+#define FANS_COUNT		"FNum" //r-o length 1
+#define FANS_MANUAL		"FS! " //r-w length 2
+#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
+#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
+#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
+#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
+#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", NULL }
+};
+
+#define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */
+#define INIT_WAIT_MSECS		50	/* ... in 50ms increments */
+
+#define APPLESMC_POLL_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_dev *applesmc_idev;
+
+/* Indicates whether this computer has an accelerometer. */
+static unsigned int applesmc_accelerometer = 0;
+
+/* Indicates whether this computer has light sensors and keyboard backlight. */
+static unsigned int applesmc_light = 0;
+
+/* Indicates which temperature sensors set to use. */
+static unsigned int applesmc_temperature_set = 0;
+
+static DECLARE_MUTEX(applesmc_sem);
+
+/*
+ * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * (masked with 0x0f), returning zero if the value is obtained.  Callers must
+ * hold applesmc_sem.
+ */
+static int __wait_status(u8 val)
+{
+	unsigned int i;
+
+	val = val & APPLESMC_STATUS_MASK;
+
+	for (i = 0; i < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) = 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_sem.
+ */
+static int applesmc_read_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug) printk(KERN_DEBUG "\n");
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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_sem.
+ */
+static int applesmc_write_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
+ * hold applesmc_sem.
+ */
+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;
+
+	down(&applesmc_sem);
+
+	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:
+	up(&applesmc_sem);
+	return ret;
+}
+
+/*
+ * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
+ * applesmc_sem.
+ */
+static int applesmc_get_fan_count(void)
+{
+	int ret;
+	u8 buffer[1];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_COUNT, buffer, 1);
+
+	up(&applesmc_sem);
+	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_sem.
+ */
+static void applesmc_calibrate(void)
+{
+	applesmc_read_motion_sensor(SENSOR_X, &rest_x);
+	applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (down_trylock(&applesmc_sem)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	up(&applesmc_sem);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d)\n", left, right);
+}
+
+/* Displays ?C * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev, char *buf,
+								const char *key)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	up(&applesmc_sem);
+
+	if (ret)
+	return ret;
+	else
+	return sprintf(buf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev, char *buf,
+						const char* key, int offset)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev, const char *buf,
+				size_t count, const char* key, int offset)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+
+	speed = simple_strtoul(buf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev, char *buf,
+								int offset)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev, const char *buf,
+						size_t count, int offset)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+
+	input = simple_strtoul(buf, NULL, 10);
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << offset);
+	else 
+		val = val & ~(0x01 << offset);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	down(&applesmc_sem);
+	applesmc_calibrate();
+	up(&applesmc_sem);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	down(&applesmc_sem);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	up(&applesmc_sem);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
+					show_fan_actual_speed_##offset, NULL); \
+\
+static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
+} \
+static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
+\
+static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
+				show_fan_maximum_speed_##offset, NULL); \
+\
+static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
+					show_fan_safe_speed_##offset, NULL); \
+\
+static ssize_t show_fan_target_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
+} \
+static ssize_t store_fan_target_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
+static ssize_t show_fan_manual_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_manual(dev, buf, offset); \
+} \
+static ssize_t store_fan_manual_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_manual(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+		   show_fan_manual_##offset, store_fan_manual_##offset);
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+/* Macro creating the sysfs entries for a fan */
+#define device_create_file_fan(ret, client, offset) \
+do { \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
+} while (0)
+
+/*
+ * Macro defining the helper function and DEVICE_ATTR for a temperature sensor
+ * sysfs entry.
+ */
+#define sysfs_temperature_offset(offset) \
+static ssize_t show_temperature_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_temperature(dev, buf, \
+		temperature_sensors_sets[applesmc_temperature_set][offset]);  \
+} \
+static DEVICE_ATTR(temperature_##offset, S_IRUGO, \
+					show_temperature_##offset, NULL);
+
+/*
+ * Create the needed functions for each temperature sensors using the macro
+ * defined above (7 temperature sensors are supported)
+ */
+sysfs_temperature_offset(0);
+sysfs_temperature_offset(1);
+sysfs_temperature_offset(2);
+sysfs_temperature_offset(3);
+sysfs_temperature_offset(4);
+sysfs_temperature_offset(5);
+sysfs_temperature_offset(6);
+
+/* Macro creating the sysfs entry for a temperature sensor */
+#define device_create_files_temperature(ret, client, offset) \
+{ \
+	ret = sysfs_create_file(client, &dev_attr_temperature_##offset.attr); \
+} while (0)
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data +					(struct dmi_match_data*)id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void) {
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void) {
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+
+	struct dmi_match_data applesmc_dmi_data[] = {
+	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
+	/* MacBook: accelerometer and temperature set 0 */
+	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
+	/* MacBook: temperature set 1 */
+	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
+	};
+
+	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+	struct dmi_system_id applesmc_whitelist[] = {
+		{ applesmc_dmi_match, "Apple MacBook Pro", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+			(void*)&applesmc_dmi_data[0]},
+		{ applesmc_dmi_match, "Apple MacBook", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+			(void*)&applesmc_dmi_data[1]},
+		{ applesmc_dmi_match, "Apple Macmini", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+			(void*)&applesmc_dmi_data[2]},
+		{ .ident = NULL }
+	};
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	}
+	else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			device_create_file_fan(ret, &pdev->dev.kobj, 1);
+			if (ret)
+				goto out_device;
+		case 1:
+			device_create_file_fan(ret, &pdev->dev.kobj, 0);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	count = 0;
+	while (temperature_sensors_sets[applesmc_temperature_set][count]
+									!= NULL)
+		count++;
+
+	switch (count) {
+	default:
+	case 7:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 6);
+		if (ret)
+			goto out_device;
+	case 6:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 5);
+		if (ret)
+			goto out_device;
+	case 5:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 4);
+		if (ret)
+			goto out_device;
+	case 4:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 3);
+		if (ret)
+			goto out_device;
+	case 3:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 2);
+		if (ret)
+			goto out_device;
+	case 2:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 1);
+		if (ret)
+			goto out_device;
+	case 1:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 0);
+		if (ret)
+			goto out_device;
+	case 0:
+		;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");




^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
  (?)
@ 2007-03-14 11:11 ` Cong WANG
  2007-03-14 14:00   ` Cong WANG
  -1 siblings, 1 reply; 47+ messages in thread
From: Cong WANG @ 2007-03-14 11:11 UTC (permalink / raw)
  To: linux-kernel

I am sorry. I forgot to CC to the list.

2007/3/14, Nicolas Boichat wrote:
> Hello,
>

<snip>

> +static ssize_t applesmc_show_fan_manual(struct device *dev, char *buf,
> +                                                               int offset)
> +{
> +       int ret;
> +       u16 manual = 0;
> +       u8 buffer[2];
> +
> +       down(&applesmc_sem);
> +
> +       ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
> +       manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
> +
> +       up(&applesmc_sem);
> +       if (ret)
> +               return ret;
> +       else
> +               return sprintf(buf, "%d\n", manual);
> +}
> +

I doubt about your last 'sprintf'. Your 'buf' just has only two 'u8's,
which maybe only has two bytes, and '\n' already consumes one. So only
one byte is left for the decimal vaule of 'manual'. Even it is just
less than 10, just as what you want, the final '\0' is omitted!

What's more, you can't get such information from the return value of
'sprintf'. So I suggest you to choose 'snprintf' instead.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14 11:11 ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Cong WANG
@ 2007-03-14 14:00   ` Cong WANG
  2007-03-15 11:31     ` Nicolas Boichat
  0 siblings, 1 reply; 47+ messages in thread
From: Cong WANG @ 2007-03-14 14:00 UTC (permalink / raw)
  To: linux-kernel, Nicolas Boichat

2007/3/14, Cong WANG wrote:
> I am sorry. I forgot to CC to the list.
>
> 2007/3/14, Nicolas Boichat wrote:
> > Hello,
> >
>
> <snip>
>
> > +static ssize_t applesmc_show_fan_manual(struct device *dev, char *buf,
> > +                                                               int offset)
> > +{
> > +       int ret;
> > +       u16 manual = 0;
> > +       u8 buffer[2];
> > +
> > +       down(&applesmc_sem);
> > +
> > +       ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
> > +       manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
> > +
> > +       up(&applesmc_sem);
> > +       if (ret)
> > +               return ret;
> > +       else
> > +               return sprintf(buf, "%d\n", manual);
> > +}
> > +
>
> I doubt about your last 'sprintf'. Your 'buf' just has only two 'u8's,
> which maybe only has two bytes, and '\n' already consumes one. So only
> one byte is left for the decimal vaule of 'manual'. Even it is just
> less than 10, just as what you want, the final '\0' is omitted!
>
> What's more, you can't get such information from the return value of
> 'sprintf'. So I suggest you to choose 'snprintf' instead.
>

Sorry. I thought his 'buffer' as 'buf'. But my suggestion is still
worthy your thinking.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14 14:00   ` Cong WANG
@ 2007-03-15 11:31     ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-15 11:31 UTC (permalink / raw)
  To: Cong WANG; +Cc: linux-kernel

Hello,

Cong WANG wrote:
> 2007/3/14, Cong WANG wrote:
>> I am sorry. I forgot to CC to the list.
>>
>> 2007/3/14, Nicolas Boichat wrote:
>> > Hello,
>> >
>>
>> <snip>
>>
>> > +static ssize_t applesmc_show_fan_manual(struct device *dev, char *buf,
>> > +                                                               int
>> offset)
>> > +{
>> > +       int ret;
>> > +       u16 manual = 0;
>> > +       u8 buffer[2];
>> > +
>> > +       down(&applesmc_sem);
>> > +
>> > +       ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
>> > +       manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
>> > +
>> > +       up(&applesmc_sem);
>> > +       if (ret)
>> > +               return ret;
>> > +       else
>> > +               return sprintf(buf, "%d\n", manual);
>> > +}
>> > +
>>
>> I doubt about your last 'sprintf'. Your 'buf' just has only two 'u8's,
>> which maybe only has two bytes, and '\n' already consumes one. So only
>> one byte is left for the decimal vaule of 'manual'. Even it is just
>> less than 10, just as what you want, the final '\0' is omitted!
>>
>> What's more, you can't get such information from the return value of
>> 'sprintf'. So I suggest you to choose 'snprintf' instead.
>>
> 
> Sorry. I thought his 'buffer' as 'buf'. But my suggestion is still
> worthy your thinking.

I'm quite sure using sprintf is ok. At least it's the way sysfs helper
functions are coded in other parts of the kernel too.

I agree that the variable names (buf and buffer) used are quite
confusing though... I'll fix that.

Best regards,

Nicolas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-03-19  5:19   ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-19  5:19 UTC (permalink / raw)
  To: Nicolas Boichat, Andrew Morton
  Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Hello,

Nicolas Boichat wrote:
> Hello,
>
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).
>
> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
> months ago) by various users on different systems on the mactel-linux lists.
>
> However, I'm not really satisfied with the way sysfs files are created:
> I use a lot of preprocessor macros to avoid repetition of code.
> The files created with these macros in /sys/devices/platform/applesmc are
> the following (on a Macbook Pro):
> fan0_actual_speed
> fan0_manual
> fan0_maximum_speed
> fan0_minimum_speed
> fan0_safe_speed
> fan0_target_speed
> fan1_actual_speed
> fan1_manual
> fan1_maximum_speed
> fan1_minimum_speed
> fan1_safe_speed
> fan1_target_speed
> temperature_0
> temperature_1
> temperature_2
> temperature_3
> temperature_4
> temperature_5
> temperature_6
>
> (i.e. temperature_* is created by one macro, fan*_actual_speed by
> another, ...)
> Is it acceptable programming practice? Is there a way to create these
> files in a more elegant manner?
>
> Also, I never call any sysfs_remove_* function, as the files are
> deleted when the module is unloaded. Is it safe to do so? Doesn't it
> cause any memory leak?
>
> This is my main concerns, however, I would be happy to have comments
> on the other parts of the code. (Please cc me I'm not subscribed to
> lkml)
>   

Here is an updated version. I added an entry in MAINTAINERS, and changed
some variables names which were confusing in some functions (the above
concerns still remains though).

Also, there is a bug I didn't mention before (I thought it disappeared
with the lastest kernel version). Sometimes there are errors when
reading/writing values, indicated by these dmesg warnings:
applesmc: wait status failed: c != 8

I don't know why it happens from time to time (I don't have any
documentation for the chip), but retrying will work almost for sure. Do
you think the kernel driver should retry by itself? Or is it to the
application to retry (the current way)?

If the current way is ok, maybe it would be good to merge this patch in
mm (it applies cleanly against the latest git).

Again, comments are welcome.

Best regards,

Nicolas

This driver provides support for the Apple System Management Controller, which
provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
temperature sensors, keyboard backlight control and fan control. Only
Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>


---

 MAINTAINERS              |    6 
 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  965 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 996 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 81bcc22..cbfdc3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -367,6 +367,12 @@ L:	linux-laptop@vger.kernel.org
 W:	http://www.canb.auug.org.au/~sfr/
 S:	Supported
 
+APPLE SMC DRIVER
+P:	Nicolas Boichat
+M:	nicolas@boichat.ch
+L:	mactel-linux-devel@lists.sourceforge.net
+S:	Maintained
+
 APPLETALK NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
 M:	acme@conectiva.com.br
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6d105a1..25b72a4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -594,6 +594,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..f596101
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,965 @@
+/*
+ * 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/dmi.h>
+#include <asm/io.h>
+#include <linux/leds.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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" //r-o length 6
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" //r-o length 6
+#define BACKLIGHT_KEY 		"LKSB" //w-o
+
+#define CLAMSHELL_KEY 		"MSLD" //r-o length 1 (unused)
+
+#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
+#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
+#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
+#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
+
+#define FANS_COUNT		"FNum" //r-o length 1
+#define FANS_MANUAL		"FS! " //r-w length 2
+#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
+#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
+#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
+#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
+#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", NULL }
+};
+
+#define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */
+#define INIT_WAIT_MSECS		50	/* ... in 50ms increments */
+
+#define APPLESMC_POLL_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_dev *applesmc_idev;
+
+/* Indicates whether this computer has an accelerometer. */
+static unsigned int applesmc_accelerometer = 0;
+
+/* Indicates whether this computer has light sensors and keyboard backlight. */
+static unsigned int applesmc_light = 0;
+
+/* Indicates which temperature sensors set to use. */
+static unsigned int applesmc_temperature_set = 0;
+
+static DECLARE_MUTEX(applesmc_sem);
+
+/*
+ * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * (masked with 0x0f), returning zero if the value is obtained.  Callers must
+ * hold applesmc_sem.
+ */
+static int __wait_status(u8 val)
+{
+	unsigned int i;
+
+	val = val & APPLESMC_STATUS_MASK;
+
+	for (i = 0; i < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 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_sem.
+ */
+static int applesmc_read_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug) printk(KERN_DEBUG "\n");
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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_sem.
+ */
+static int applesmc_write_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
+ * hold applesmc_sem.
+ */
+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;
+
+	down(&applesmc_sem);
+
+	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:
+	up(&applesmc_sem);
+	return ret;
+}
+
+/*
+ * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
+ * applesmc_sem.
+ */
+static int applesmc_get_fan_count(void)
+{
+	int ret;
+	u8 buffer[1];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_COUNT, buffer, 1);
+
+	up(&applesmc_sem);
+	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_sem.
+ */
+static void applesmc_calibrate(void)
+{
+	applesmc_read_motion_sensor(SENSOR_X, &rest_x);
+	applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (down_trylock(&applesmc_sem)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	up(&applesmc_sem);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+}
+
+/* Displays degree Celsius * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev, char *sysfsbuf,
+								const char *key)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	up(&applesmc_sem);
+
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev, char *sysfsbuf,
+						const char* key, int offset)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev,
+					const char *sysfsbuf, size_t count,
+					const char* key, int offset)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+
+	speed = simple_strtoul(sysfsbuf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev, char *sysfsbuf,
+								int offset)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev,
+				const char *sysfsbuf, size_t count, int offset)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+
+	input = simple_strtoul(sysfsbuf, NULL, 10);
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << offset);
+	else 
+		val = val & ~(0x01 << offset);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	down(&applesmc_sem);
+	applesmc_calibrate();
+	up(&applesmc_sem);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	down(&applesmc_sem);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	up(&applesmc_sem);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
+					show_fan_actual_speed_##offset, NULL); \
+\
+static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
+} \
+static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
+\
+static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
+				show_fan_maximum_speed_##offset, NULL); \
+\
+static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
+					show_fan_safe_speed_##offset, NULL); \
+\
+static ssize_t show_fan_target_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
+} \
+static ssize_t store_fan_target_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
+static ssize_t show_fan_manual_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_manual(dev, buf, offset); \
+} \
+static ssize_t store_fan_manual_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_manual(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+		   show_fan_manual_##offset, store_fan_manual_##offset);
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+/* Macro creating the sysfs entries for a fan */
+#define device_create_file_fan(ret, client, offset) \
+do { \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
+} while (0)
+
+/*
+ * Macro defining the helper function and DEVICE_ATTR for a temperature sensor
+ * sysfs entry.
+ */
+#define sysfs_temperature_offset(offset) \
+static ssize_t show_temperature_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_temperature(dev, buf, \
+		temperature_sensors_sets[applesmc_temperature_set][offset]);  \
+} \
+static DEVICE_ATTR(temperature_##offset, S_IRUGO, \
+					show_temperature_##offset, NULL);
+
+/*
+ * Create the needed functions for each temperature sensors using the macro
+ * defined above (7 temperature sensors are supported)
+ */
+sysfs_temperature_offset(0);
+sysfs_temperature_offset(1);
+sysfs_temperature_offset(2);
+sysfs_temperature_offset(3);
+sysfs_temperature_offset(4);
+sysfs_temperature_offset(5);
+sysfs_temperature_offset(6);
+
+/* Macro creating the sysfs entry for a temperature sensor */
+#define device_create_files_temperature(ret, client, offset) \
+{ \
+	ret = sysfs_create_file(client, &dev_attr_temperature_##offset.attr); \
+} while (0)
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data =
+					(struct dmi_match_data*)id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void) {
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void) {
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+
+	struct dmi_match_data applesmc_dmi_data[] = {
+	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
+	/* MacBook: accelerometer and temperature set 0 */
+	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
+	/* MacBook: temperature set 1 */
+	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
+	};
+
+	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+	struct dmi_system_id applesmc_whitelist[] = {
+		{ applesmc_dmi_match, "Apple MacBook Pro", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+			(void*)&applesmc_dmi_data[0]},
+		{ applesmc_dmi_match, "Apple MacBook", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+			(void*)&applesmc_dmi_data[1]},
+		{ applesmc_dmi_match, "Apple Macmini", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+			(void*)&applesmc_dmi_data[2]},
+		{ .ident = NULL }
+	};
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	}
+	else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			device_create_file_fan(ret, &pdev->dev.kobj, 1);
+			if (ret)
+				goto out_device;
+		case 1:
+			device_create_file_fan(ret, &pdev->dev.kobj, 0);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	count = 0;
+	while (temperature_sensors_sets[applesmc_temperature_set][count]
+									!= NULL)
+		count++;
+
+	switch (count) {
+	default:
+	case 7:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 6);
+		if (ret)
+			goto out_device;
+	case 6:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 5);
+		if (ret)
+			goto out_device;
+	case 5:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 4);
+		if (ret)
+			goto out_device;
+	case 4:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 3);
+		if (ret)
+			goto out_device;
+	case 3:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 2);
+		if (ret)
+			goto out_device;
+	case 2:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 1);
+		if (ret)
+			goto out_device;
+	case 1:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 0);
+		if (ret)
+			goto out_device;
+	case 0:
+		;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-19  5:19   ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-19  5:19 UTC (permalink / raw)
  To: Nicolas Boichat, Andrew Morton
  Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Hello,

Nicolas Boichat wrote:
> Hello,
>
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).
>
> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
> months ago) by various users on different systems on the mactel-linux lists.
>
> However, I'm not really satisfied with the way sysfs files are created:
> I use a lot of preprocessor macros to avoid repetition of code.
> The files created with these macros in /sys/devices/platform/applesmc are
> the following (on a Macbook Pro):
> fan0_actual_speed
> fan0_manual
> fan0_maximum_speed
> fan0_minimum_speed
> fan0_safe_speed
> fan0_target_speed
> fan1_actual_speed
> fan1_manual
> fan1_maximum_speed
> fan1_minimum_speed
> fan1_safe_speed
> fan1_target_speed
> temperature_0
> temperature_1
> temperature_2
> temperature_3
> temperature_4
> temperature_5
> temperature_6
>
> (i.e. temperature_* is created by one macro, fan*_actual_speed by
> another, ...)
> Is it acceptable programming practice? Is there a way to create these
> files in a more elegant manner?
>
> Also, I never call any sysfs_remove_* function, as the files are
> deleted when the module is unloaded. Is it safe to do so? Doesn't it
> cause any memory leak?
>
> This is my main concerns, however, I would be happy to have comments
> on the other parts of the code. (Please cc me I'm not subscribed to
> lkml)
>   

Here is an updated version. I added an entry in MAINTAINERS, and changed
some variables names which were confusing in some functions (the above
concerns still remains though).

Also, there is a bug I didn't mention before (I thought it disappeared
with the lastest kernel version). Sometimes there are errors when
reading/writing values, indicated by these dmesg warnings:
applesmc: wait status failed: c != 8

I don't know why it happens from time to time (I don't have any
documentation for the chip), but retrying will work almost for sure. Do
you think the kernel driver should retry by itself? Or is it to the
application to retry (the current way)?

If the current way is ok, maybe it would be good to merge this patch in
mm (it applies cleanly against the latest git).

Again, comments are welcome.

Best regards,

Nicolas

This driver provides support for the Apple System Management Controller, which
provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
temperature sensors, keyboard backlight control and fan control. Only
Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).

Signed-off-by: Nicolas Boichat <nicolas at boichat.ch>


---

 MAINTAINERS              |    6 
 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  965 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 996 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 81bcc22..cbfdc3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -367,6 +367,12 @@ L:	linux-laptop at vger.kernel.org
 W:	http://www.canb.auug.org.au/~sfr/
 S:	Supported
 
+APPLE SMC DRIVER
+P:	Nicolas Boichat
+M:	nicolas at boichat.ch
+L:	mactel-linux-devel at lists.sourceforge.net
+S:	Maintained
+
 APPLETALK NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
 M:	acme at conectiva.com.br
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6d105a1..25b72a4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -594,6 +594,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..f596101
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,965 @@
+/*
+ * 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 at boichat.ch>
+ *
+ * Based on hdaps.c driver:
+ * Copyright (C) 2005 Robert Love <rml at novell.com>
+ * Copyright (C) 2005 Jesper Juhl <jesper.juhl at gmail.com>
+ *
+ * Fan control based on smcFanControl:
+ * Copyright (C) 2006 Hendrik Holtmann <holtmann at 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.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/dmi.h>
+#include <asm/io.h>
+#include <linux/leds.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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" //r-o length 6
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" //r-o length 6
+#define BACKLIGHT_KEY 		"LKSB" //w-o
+
+#define CLAMSHELL_KEY 		"MSLD" //r-o length 1 (unused)
+
+#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
+#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
+#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
+#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
+
+#define FANS_COUNT		"FNum" //r-o length 1
+#define FANS_MANUAL		"FS! " //r-w length 2
+#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
+#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
+#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
+#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
+#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", NULL }
+};
+
+#define INIT_TIMEOUT_MSECS	5000	/* wait up to 5s for device init ... */
+#define INIT_WAIT_MSECS		50	/* ... in 50ms increments */
+
+#define APPLESMC_POLL_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_dev *applesmc_idev;
+
+/* Indicates whether this computer has an accelerometer. */
+static unsigned int applesmc_accelerometer = 0;
+
+/* Indicates whether this computer has light sensors and keyboard backlight. */
+static unsigned int applesmc_light = 0;
+
+/* Indicates which temperature sensors set to use. */
+static unsigned int applesmc_temperature_set = 0;
+
+static DECLARE_MUTEX(applesmc_sem);
+
+/*
+ * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * (masked with 0x0f), returning zero if the value is obtained.  Callers must
+ * hold applesmc_sem.
+ */
+static int __wait_status(u8 val)
+{
+	unsigned int i;
+
+	val = val & APPLESMC_STATUS_MASK;
+
+	for (i = 0; i < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) = 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_sem.
+ */
+static int applesmc_read_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug) printk(KERN_DEBUG "\n");
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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_sem.
+ */
+static int applesmc_write_key(const char* key, u8* buffer, u8 len)
+{
+	int ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * applesmc_read_motion_sensor - Read motion sensor (X, Y or Z). Callers must
+ * hold applesmc_sem.
+ */
+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;
+
+	down(&applesmc_sem);
+
+	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:
+	up(&applesmc_sem);
+	return ret;
+}
+
+/*
+ * applesmc_get_fan_count - get the number of fans. Callers must NOT hold
+ * applesmc_sem.
+ */
+static int applesmc_get_fan_count(void)
+{
+	int ret;
+	u8 buffer[1];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_COUNT, buffer, 1);
+
+	up(&applesmc_sem);
+	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_sem.
+ */
+static void applesmc_calibrate(void)
+{
+	applesmc_read_motion_sensor(SENSOR_X, &rest_x);
+	applesmc_read_motion_sensor(SENSOR_Y, &rest_y);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (down_trylock(&applesmc_sem)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	up(&applesmc_sem);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+}
+
+/* Displays degree Celsius * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev, char *sysfsbuf,
+								const char *key)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	up(&applesmc_sem);
+
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev, char *sysfsbuf,
+						const char* key, int offset)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev,
+					const char *sysfsbuf, size_t count,
+					const char* key, int offset)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+
+	speed = simple_strtoul(sysfsbuf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = key[0];
+	newkey[1] = '0' + offset;
+	newkey[2] = key[2];
+	newkey[3] = key[3];
+	newkey[4] = 0;
+
+	down(&applesmc_sem);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev, char *sysfsbuf,
+								int offset)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> offset) & 0x01;
+
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev,
+				const char *sysfsbuf, size_t count, int offset)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+
+	input = simple_strtoul(sysfsbuf, NULL, 10);
+
+	down(&applesmc_sem);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << offset);
+	else 
+		val = val & ~(0x01 << offset);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	up(&applesmc_sem);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	down(&applesmc_sem);
+	applesmc_calibrate();
+	up(&applesmc_sem);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	down(&applesmc_sem);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	up(&applesmc_sem);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
+					show_fan_actual_speed_##offset, NULL); \
+\
+static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
+} \
+static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
+\
+static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
+				show_fan_maximum_speed_##offset, NULL); \
+\
+static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
+					show_fan_safe_speed_##offset, NULL); \
+\
+static ssize_t show_fan_target_speed_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
+} \
+static ssize_t store_fan_target_speed_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
+static ssize_t show_fan_manual_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_fan_manual(dev, buf, offset); \
+} \
+static ssize_t store_fan_manual_##offset (struct device *dev, \
+		struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+	return applesmc_store_fan_manual(dev, buf, count, offset); \
+} \
+static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+		   show_fan_manual_##offset, store_fan_manual_##offset);
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+/* Macro creating the sysfs entries for a fan */
+#define device_create_file_fan(ret, client, offset) \
+do { \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
+if (ret) break; \
+ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
+} while (0)
+
+/*
+ * Macro defining the helper function and DEVICE_ATTR for a temperature sensor
+ * sysfs entry.
+ */
+#define sysfs_temperature_offset(offset) \
+static ssize_t show_temperature_##offset (struct device *dev, \
+				struct device_attribute *attr, char *buf) \
+{ \
+	return applesmc_show_temperature(dev, buf, \
+		temperature_sensors_sets[applesmc_temperature_set][offset]);  \
+} \
+static DEVICE_ATTR(temperature_##offset, S_IRUGO, \
+					show_temperature_##offset, NULL);
+
+/*
+ * Create the needed functions for each temperature sensors using the macro
+ * defined above (7 temperature sensors are supported)
+ */
+sysfs_temperature_offset(0);
+sysfs_temperature_offset(1);
+sysfs_temperature_offset(2);
+sysfs_temperature_offset(3);
+sysfs_temperature_offset(4);
+sysfs_temperature_offset(5);
+sysfs_temperature_offset(6);
+
+/* Macro creating the sysfs entry for a temperature sensor */
+#define device_create_files_temperature(ret, client, offset) \
+{ \
+	ret = sysfs_create_file(client, &dev_attr_temperature_##offset.attr); \
+} while (0)
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data +					(struct dmi_match_data*)id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void) {
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void) {
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+
+	struct dmi_match_data applesmc_dmi_data[] = {
+	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
+	/* MacBook: accelerometer and temperature set 0 */
+	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
+	/* MacBook: temperature set 1 */
+	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
+	};
+
+	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+	struct dmi_system_id applesmc_whitelist[] = {
+		{ applesmc_dmi_match, "Apple MacBook Pro", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+			(void*)&applesmc_dmi_data[0]},
+		{ applesmc_dmi_match, "Apple MacBook", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+			(void*)&applesmc_dmi_data[1]},
+		{ applesmc_dmi_match, "Apple Macmini", {
+		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+			(void*)&applesmc_dmi_data[2]},
+		{ .ident = NULL }
+	};
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	}
+	else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			device_create_file_fan(ret, &pdev->dev.kobj, 1);
+			if (ret)
+				goto out_device;
+		case 1:
+			device_create_file_fan(ret, &pdev->dev.kobj, 0);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	count = 0;
+	while (temperature_sensors_sets[applesmc_temperature_set][count]
+									!= NULL)
+		count++;
+
+	switch (count) {
+	default:
+	case 7:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 6);
+		if (ret)
+			goto out_device;
+	case 6:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 5);
+		if (ret)
+			goto out_device;
+	case 5:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 4);
+		if (ret)
+			goto out_device;
+	case 4:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 3);
+		if (ret)
+			goto out_device;
+	case 3:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 2);
+		if (ret)
+			goto out_device;
+	case 2:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 1);
+		if (ret)
+			goto out_device;
+	case 1:
+		device_create_files_temperature(ret, &pdev->dev.kobj, 0);
+		if (ret)
+			goto out_device;
+	case 0:
+		;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");




^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19  5:19   ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-03-19  6:54     ` Andrew Morton
  -1 siblings, 0 replies; 47+ messages in thread
From: Andrew Morton @ 2007-03-19  6:54 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas@boichat.ch> wrote:

> 
> This driver provides support for the Apple System Management Controller, which
> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
> temperature sensors, keyboard backlight control and fan control. Only
> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
> 

It's trivia time:

> +#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
> +#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
> +#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
> +#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
> +
> +#define FANS_COUNT		"FNum" //r-o length 1
> +#define FANS_MANUAL		"FS! " //r-w length 2
> +#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
> +#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
> +#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
> +#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
> +#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2

Please avoid C++-style comments.

> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
> +static const char* temperature_sensors_sets[][8] = {
> +	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
> +	{ "TC0D", "TC0P", NULL }
> +};

The NULLs here are harmless, but unneeded.

> +/* 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 int debug = 0;
> +static struct platform_device *pdev;
> +static s16 rest_x;
> +static s16 rest_y;
> +static struct timer_list applesmc_timer;
> +static struct input_dev *applesmc_idev;
> +
> +/* Indicates whether this computer has an accelerometer. */
> +static unsigned int applesmc_accelerometer = 0;
> +
> +/* Indicates whether this computer has light sensors and keyboard backlight. */
> +static unsigned int applesmc_light = 0;
> +
> +/* Indicates which temperature sensors set to use. */
> +static unsigned int applesmc_temperature_set = 0;

All the "= 0"s above are unneeded and will increase the module or vmlinux
size - they should be removed.

> +static DECLARE_MUTEX(applesmc_sem);

Semaphores should be used only when their counting feature is required.  I
think thsi can be switched to `struct mutex'.

> +/*
> + * 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_sem.
> + */
> +static int applesmc_read_key(const char* key, u8* buffer, u8 len)
> +{
> +	int ret = -EIO;
> +	int i;
> +
> +	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
> +	if (__wait_status(0x0c))
> +		goto out;
> +	
> +	for (i = 0; i < 4; i++) {
> +		outb(key[i], APPLESMC_DATA_PORT);
> +		if (__wait_status(0x04))
> +			goto out;
> +	}
> +	if (debug) printk(KERN_DEBUG "<%s", key);
> +
> +	outb(len, APPLESMC_DATA_PORT);
> +	if (debug) printk(KERN_DEBUG ">%x", len);

Please convert to standard kernel style:

	if (debug)
		printk(KERN_DEBUG ">%x", len);

There are many instances of this.

> +
> +	for (i = 0; i < len; i++) {
> +		if (__wait_status(0x05))
> +			goto out;
> +		buffer[i] = inb(APPLESMC_DATA_PORT);
> +		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
> +	}
> +	if (debug) printk(KERN_DEBUG "\n");
> +	ret = 0;
> +
> +out:
> +	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;
> +
> +	down(&applesmc_sem);
> +
> +	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 {

Please use

			} else {

here and wherever else the above appears.

> +				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:
> +	up(&applesmc_sem);
> +	return ret;
> +}
> +
>
> ...
>
> +/*
> + * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
> + *  - show actual speed
> + *  - show/store minimum speed
> + *  - show maximum speed
> + *  - show safe speed
> + *  - show/store target speed
> + *  - show/store manual mode
> + */
> +#define sysfs_fan_speeds_offset(offset) \
> +static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
> +					show_fan_actual_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
> +} \
> +static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
> +	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
> +\
> +static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
> +				show_fan_maximum_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
> +					show_fan_safe_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_target_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
> +} \
> +static ssize_t store_fan_target_speed_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
> +	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
> +static ssize_t show_fan_manual_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_manual(dev, buf, offset); \
> +} \
> +static ssize_t store_fan_manual_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_manual(dev, buf, count, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
> +		   show_fan_manual_##offset, store_fan_manual_##offset);

erk.  Can we use attribute groups here?

> +/*
> + * Create the needed functions for each fan using the macro defined above 
> + * (2 fans are supported)
> + */
> +sysfs_fan_speeds_offset(0);
> +sysfs_fan_speeds_offset(1);
> +
> +/* Macro creating the sysfs entries for a fan */
> +#define device_create_file_fan(ret, client, offset) \
> +do { \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
> +} while (0)

And here?

>
> ...
>
> +/* 
> + * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
> + */
> +static int applesmc_dmi_match(struct dmi_system_id *id)
> +{
> +	int i = 0;
> +	struct dmi_match_data* dmi_data =
> +					(struct dmi_match_data*)id->driver_data;

Unneeded and undesirable cast of void*.

> +	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
> +	applesmc_accelerometer = dmi_data->accelerometer;
> +	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
> +				applesmc_accelerometer ? "with" : "without");
> +	applesmc_light = dmi_data->light;
> +	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
> +					applesmc_light ? "with" : "without");
> +
> +	applesmc_temperature_set =  dmi_data->temperature_set;
> +	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
> +		i++;
> +	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
> +	return 1;
> +}
> +
> +/* Create accelerometer ressources */
> +static int applesmc_create_accelerometer(void) {

Opening brace goes in column 1

> +	int ret;
> +
> +	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
> +	if (ret)
> +		goto out;
> +
> +	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
> +	if (ret)
> +		goto out;
> +
> +	applesmc_idev = input_allocate_device();
> +	if (!applesmc_idev) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	/* initial calibrate for the input device */
> +	applesmc_calibrate();
> +
> +	/* initialize the input class */
> +	applesmc_idev->name = "applesmc";
> +	applesmc_idev->cdev.dev = &pdev->dev;
> +	applesmc_idev->evbit[0] = BIT(EV_ABS);
> +	input_set_abs_params(applesmc_idev, ABS_X,
> +			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +	input_set_abs_params(applesmc_idev, ABS_Y,
> +			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +
> +	input_register_device(applesmc_idev);
> +
> +	/* start up our timer for the input device */
> +	init_timer(&applesmc_timer);
> +	applesmc_timer.function = applesmc_mousedev_poll;
> +	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
> +	add_timer(&applesmc_timer);
> +
> +	return 0;
> +
> +out:
> +	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
> +	return ret;
> +}
> +
> +/* Release all ressources used by the accelerometer */
> +static void applesmc_release_accelerometer(void) {
> +	del_timer_sync(&applesmc_timer);
> +	input_unregister_device(applesmc_idev);
> +}

Opening brace in column 1

> +static int __init applesmc_init(void)
> +{
> +	int ret;
> +	int count;
> +
> +	struct dmi_match_data applesmc_dmi_data[] = {
> +	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
> +	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
> +	/* MacBook: accelerometer and temperature set 0 */
> +	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
> +	/* MacBook: temperature set 1 */
> +	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
> +	};
> +
> +	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
> +	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
> +	struct dmi_system_id applesmc_whitelist[] = {
> +		{ applesmc_dmi_match, "Apple MacBook Pro", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
> +			(void*)&applesmc_dmi_data[0]},
> +		{ applesmc_dmi_match, "Apple MacBook", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
> +			(void*)&applesmc_dmi_data[1]},
> +		{ applesmc_dmi_match, "Apple Macmini", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
> +			(void*)&applesmc_dmi_data[2]},
> +		{ .ident = NULL }
> +	};

The compiler will need to build the above arrays on the stack at runtime. 
Is it possible to make these static so they are build at compile-time?  And
to then make them __initdata so they get discarded?

> +	if (!dmi_check_system(applesmc_whitelist)) {
> +		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
> +	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
> +								"applesmc")) {
> +		ret = -ENXIO;
> +		goto out;
> +	}
> +
> +	ret = platform_driver_register(&applesmc_driver);
> +	if (ret)
> +		goto out_region;
> +
> +	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
> +	if (IS_ERR(pdev)) {
> +		ret = PTR_ERR(pdev);
> +		goto out_driver;
> +	}
> +


^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-19  6:54     ` Andrew Morton
  0 siblings, 0 replies; 47+ messages in thread
From: Andrew Morton @ 2007-03-19  6:54 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas at boichat.ch> wrote:

> 
> This driver provides support for the Apple System Management Controller, which
> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
> temperature sensors, keyboard backlight control and fan control. Only
> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
> 

It's trivia time:

> +#define MOTION_SENSOR_X_KEY	"MO_X" //r-o length 2
> +#define MOTION_SENSOR_Y_KEY	"MO_Y" //r-o length 2
> +#define MOTION_SENSOR_Z_KEY	"MO_Z" //r-o length 2
> +#define MOTION_SENSOR_KEY	"MOCN" //r/w length 2
> +
> +#define FANS_COUNT		"FNum" //r-o length 1
> +#define FANS_MANUAL		"FS! " //r-w length 2
> +#define FAN_ACTUAL_SPEED	"F0Ac" //r-o length 2
> +#define FAN_MIN_SPEED		"F0Mn" //r-o length 2
> +#define FAN_MAX_SPEED		"F0Mx" //r-o length 2
> +#define FAN_SAFE_SPEED		"F0Sf" //r-o length 2
> +#define FAN_TARGET_SPEED	"F0Tg" //r-w length 2

Please avoid C++-style comments.

> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
> +static const char* temperature_sensors_sets[][8] = {
> +	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
> +	{ "TC0D", "TC0P", NULL }
> +};

The NULLs here are harmless, but unneeded.

> +/* 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 int debug = 0;
> +static struct platform_device *pdev;
> +static s16 rest_x;
> +static s16 rest_y;
> +static struct timer_list applesmc_timer;
> +static struct input_dev *applesmc_idev;
> +
> +/* Indicates whether this computer has an accelerometer. */
> +static unsigned int applesmc_accelerometer = 0;
> +
> +/* Indicates whether this computer has light sensors and keyboard backlight. */
> +static unsigned int applesmc_light = 0;
> +
> +/* Indicates which temperature sensors set to use. */
> +static unsigned int applesmc_temperature_set = 0;

All the "= 0"s above are unneeded and will increase the module or vmlinux
size - they should be removed.

> +static DECLARE_MUTEX(applesmc_sem);

Semaphores should be used only when their counting feature is required.  I
think thsi can be switched to `struct mutex'.

> +/*
> + * 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_sem.
> + */
> +static int applesmc_read_key(const char* key, u8* buffer, u8 len)
> +{
> +	int ret = -EIO;
> +	int i;
> +
> +	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
> +	if (__wait_status(0x0c))
> +		goto out;
> +	
> +	for (i = 0; i < 4; i++) {
> +		outb(key[i], APPLESMC_DATA_PORT);
> +		if (__wait_status(0x04))
> +			goto out;
> +	}
> +	if (debug) printk(KERN_DEBUG "<%s", key);
> +
> +	outb(len, APPLESMC_DATA_PORT);
> +	if (debug) printk(KERN_DEBUG ">%x", len);

Please convert to standard kernel style:

	if (debug)
		printk(KERN_DEBUG ">%x", len);

There are many instances of this.

> +
> +	for (i = 0; i < len; i++) {
> +		if (__wait_status(0x05))
> +			goto out;
> +		buffer[i] = inb(APPLESMC_DATA_PORT);
> +		if (debug) printk(KERN_DEBUG "<%x", buffer[i]);
> +	}
> +	if (debug) printk(KERN_DEBUG "\n");
> +	ret = 0;
> +
> +out:
> +	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;
> +
> +	down(&applesmc_sem);
> +
> +	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 {

Please use

			} else {

here and wherever else the above appears.

> +				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:
> +	up(&applesmc_sem);
> +	return ret;
> +}
> +
>
> ...
>
> +/*
> + * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
> + *  - show actual speed
> + *  - show/store minimum speed
> + *  - show maximum speed
> + *  - show safe speed
> + *  - show/store target speed
> + *  - show/store manual mode
> + */
> +#define sysfs_fan_speeds_offset(offset) \
> +static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
> +					show_fan_actual_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
> +} \
> +static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
> +	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
> +\
> +static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
> +				show_fan_maximum_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
> +					show_fan_safe_speed_##offset, NULL); \
> +\
> +static ssize_t show_fan_target_speed_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
> +} \
> +static ssize_t store_fan_target_speed_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
> +	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
> +static ssize_t show_fan_manual_##offset (struct device *dev, \
> +				struct device_attribute *attr, char *buf) \
> +{ \
> +	return applesmc_show_fan_manual(dev, buf, offset); \
> +} \
> +static ssize_t store_fan_manual_##offset (struct device *dev, \
> +		struct device_attribute *attr, const char *buf, size_t count) \
> +{ \
> +	return applesmc_store_fan_manual(dev, buf, count, offset); \
> +} \
> +static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
> +		   show_fan_manual_##offset, store_fan_manual_##offset);

erk.  Can we use attribute groups here?

> +/*
> + * Create the needed functions for each fan using the macro defined above 
> + * (2 fans are supported)
> + */
> +sysfs_fan_speeds_offset(0);
> +sysfs_fan_speeds_offset(1);
> +
> +/* Macro creating the sysfs entries for a fan */
> +#define device_create_file_fan(ret, client, offset) \
> +do { \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
> +if (ret) break; \
> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
> +} while (0)

And here?

>
> ...
>
> +/* 
> + * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
> + */
> +static int applesmc_dmi_match(struct dmi_system_id *id)
> +{
> +	int i = 0;
> +	struct dmi_match_data* dmi_data > +					(struct dmi_match_data*)id->driver_data;

Unneeded and undesirable cast of void*.

> +	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
> +	applesmc_accelerometer = dmi_data->accelerometer;
> +	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
> +				applesmc_accelerometer ? "with" : "without");
> +	applesmc_light = dmi_data->light;
> +	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
> +					applesmc_light ? "with" : "without");
> +
> +	applesmc_temperature_set =  dmi_data->temperature_set;
> +	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
> +		i++;
> +	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
> +	return 1;
> +}
> +
> +/* Create accelerometer ressources */
> +static int applesmc_create_accelerometer(void) {

Opening brace goes in column 1

> +	int ret;
> +
> +	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
> +	if (ret)
> +		goto out;
> +
> +	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
> +	if (ret)
> +		goto out;
> +
> +	applesmc_idev = input_allocate_device();
> +	if (!applesmc_idev) {
> +		ret = -ENOMEM;
> +		goto out;
> +	}
> +
> +	/* initial calibrate for the input device */
> +	applesmc_calibrate();
> +
> +	/* initialize the input class */
> +	applesmc_idev->name = "applesmc";
> +	applesmc_idev->cdev.dev = &pdev->dev;
> +	applesmc_idev->evbit[0] = BIT(EV_ABS);
> +	input_set_abs_params(applesmc_idev, ABS_X,
> +			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +	input_set_abs_params(applesmc_idev, ABS_Y,
> +			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +
> +	input_register_device(applesmc_idev);
> +
> +	/* start up our timer for the input device */
> +	init_timer(&applesmc_timer);
> +	applesmc_timer.function = applesmc_mousedev_poll;
> +	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
> +	add_timer(&applesmc_timer);
> +
> +	return 0;
> +
> +out:
> +	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
> +	return ret;
> +}
> +
> +/* Release all ressources used by the accelerometer */
> +static void applesmc_release_accelerometer(void) {
> +	del_timer_sync(&applesmc_timer);
> +	input_unregister_device(applesmc_idev);
> +}

Opening brace in column 1

> +static int __init applesmc_init(void)
> +{
> +	int ret;
> +	int count;
> +
> +	struct dmi_match_data applesmc_dmi_data[] = {
> +	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
> +	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
> +	/* MacBook: accelerometer and temperature set 0 */
> +	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
> +	/* MacBook: temperature set 1 */
> +	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
> +	};
> +
> +	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
> +	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
> +	struct dmi_system_id applesmc_whitelist[] = {
> +		{ applesmc_dmi_match, "Apple MacBook Pro", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
> +			(void*)&applesmc_dmi_data[0]},
> +		{ applesmc_dmi_match, "Apple MacBook", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
> +			(void*)&applesmc_dmi_data[1]},
> +		{ applesmc_dmi_match, "Apple Macmini", {
> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
> +		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
> +			(void*)&applesmc_dmi_data[2]},
> +		{ .ident = NULL }
> +	};

The compiler will need to build the above arrays on the stack at runtime. 
Is it possible to make these static so they are build at compile-time?  And
to then make them __initdata so they get discarded?

> +	if (!dmi_check_system(applesmc_whitelist)) {
> +		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
> +	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
> +								"applesmc")) {
> +		ret = -ENXIO;
> +		goto out;
> +	}
> +
> +	ret = platform_driver_register(&applesmc_driver);
> +	if (ret)
> +		goto out_region;
> +
> +	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
> +	if (IS_ERR(pdev)) {
> +		ret = PTR_ERR(pdev);
> +		goto out_driver;
> +	}
> +



^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19  6:54     ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Andrew Morton
@ 2007-03-19  7:35       ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-19  7:35 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Andrew Morton wrote:
> On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas@boichat.ch> wrote:
> 
>> This driver provides support for the Apple System Management Controller, which
>> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
>> temperature sensors, keyboard backlight control and fan control. Only
>> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
>>
> 
> It's trivia time:
> [snip]

Thanks for your comments, I'll send a new patch ASAP.

Best regards,

Nicolas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-19  7:35       ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-19  7:35 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Andrew Morton wrote:
> On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas at boichat.ch> wrote:
> 
>> This driver provides support for the Apple System Management Controller, which
>> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
>> temperature sensors, keyboard backlight control and fan control. Only
>> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
>>
> 
> It's trivia time:
> [snip]

Thanks for your comments, I'll send a new patch ASAP.

Best regards,

Nicolas


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-03-19 21:43   ` Bob Copeland
  -1 siblings, 0 replies; 47+ messages in thread
From: Bob Copeland @ 2007-03-19 21:43 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

On 3/14/07, Nicolas Boichat <nicolas@boichat.ch> wrote:
> Hello,
>
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).

Hi Nicolas,

I tried out an earlier version of this patch several months ago just to play
around with the joystick part of the accelerometer driver on my MacBook, and
found that it was backwards in the y-direction compared to what Neverball
seemed to want (of course, NB has no way to invert the joystick).  I think
I just did something like this in my own copy:

+       y = -y;
        input_report_abs(applesmc_idev, ABS_X, x - rest_x);
        input_report_abs(applesmc_idev, ABS_Y, y - rest_y);

I don't claim you necessarily want to change it, but thought I'd pass it
along.

Bob

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-03-19 21:43   ` Bob Copeland
  0 siblings, 0 replies; 47+ messages in thread
From: Bob Copeland @ 2007-03-19 21:43 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

On 3/14/07, Nicolas Boichat <nicolas at boichat.ch> wrote:
> Hello,
>
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).

Hi Nicolas,

I tried out an earlier version of this patch several months ago just to play
around with the joystick part of the accelerometer driver on my MacBook, and
found that it was backwards in the y-direction compared to what Neverball
seemed to want (of course, NB has no way to invert the joystick).  I think
I just did something like this in my own copy:

+       y = -y;
        input_report_abs(applesmc_idev, ABS_X, x - rest_x);
        input_report_abs(applesmc_idev, ABS_Y, y - rest_y);

I don't claim you necessarily want to change it, but thought I'd pass it
along.

Bob


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19 21:43   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Bob Copeland
  (?)
@ 2007-03-20  7:02   ` Nicolas Boichat
  2007-03-20 15:14     ` Bob Copeland
                       ` (2 more replies)
  -1 siblings, 3 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-20  7:02 UTC (permalink / raw)
  To: Bob Copeland; +Cc: linux-kernel, mactel-linux-devel

Hello,

Bob Copeland wrote:
> On 3/14/07, Nicolas Boichat <nicolas@boichat.ch> wrote:
>> Hello,
>>
>> I developed, a while ago, a driver the Apple System Management
>> Controller, which provides an accelerometer (Apple Sudden Motion
>> Sensor), light sensors, temperature sensors, keyboard backlight control
>> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
>> MacMini).
> 
> Hi Nicolas,
> 
> I tried out an earlier version of this patch several months ago just to
> play
> around with the joystick part of the accelerometer driver on my MacBook,
> and
> found that it was backwards in the y-direction compared to what Neverball
> seemed to want (of course, NB has no way to invert the joystick).  I think
> I just did something like this in my own copy:
> 
> +       y = -y;
>        input_report_abs(applesmc_idev, ABS_X, x - rest_x);
>        input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
> 
> I don't claim you necessarily want to change it, but thought I'd pass it
> along.

I tried neverball on my Macbook Pro 1st generation (Core Duo, not Core 2
Duo), and the x axis in inverted, not the y axis.

Could you confirm which axis is inverted on your Macbook?

Also, have you tried the modified hdaps-gl, available here:
http://mactel-linux.svn.sourceforge.net/viewvc/mactel-linux/trunk/tools/hdaps-gl/
? Is it working correctly?

Thanks,

Best regards,

Nicolas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19  6:54     ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Andrew Morton
@ 2007-03-20  7:12       ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-20  7:12 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Hello,

Andrew Morton wrote:
> On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas@boichat.ch> wrote:
>
>   
>> This driver provides support for the Apple System Management Controller, which
>> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
>> temperature sensors, keyboard backlight control and fan control. Only
>> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
>>
>>     
>
> It's trivia time:
>   
> [snip, syntax fixed (C++-style comments replaced)]
>> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
>> +static const char* temperature_sensors_sets[][8] = {
>> +	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
>> +	{ "TC0D", "TC0P", NULL }
>> +};
>>     
>
> The NULLs here are harmless, but unneeded.
>   
Actually, I think it's safer to keep them. I use these NULL values as an
end-of-list marker in applesmc_init:
    for (i = 0;
         temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
         i++) { ...

If you remove these NULLs, and, later, add a temperature sensor to the
first set without thinking about incrementing the array size, you won't
get any warnings, and the code will not work, while if you keep them,
you will get a warning (drivers/hwmon/applesmc.c:73: warning: excess
elements in array initializer).
>   
> [snip, removed unneeded "= 0" in global variables]
>
>   
>> +static DECLARE_MUTEX(applesmc_sem);
>>     
>
> Semaphores should be used only when their counting feature is required.  I
> think thsi can be switched to `struct mutex'.
>   
Fixed. Note: this code comes from hdaps, which was, and is still, using
semaphores, it should probably be fixed too.
> [snip, "if" and "else" syntax fixed]
>>
>> +/*
>> + * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
>> + *  - show actual speed
>> + *  - show/store minimum speed
>> + *  - show maximum speed
>> + *  - show safe speed
>> + *  - show/store target speed
>> + *  - show/store manual mode
>> + */
>> +#define sysfs_fan_speeds_offset(offset) \
>> +static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
>> +					show_fan_actual_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
>> +} \
>> +static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
>> +	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
>> +\
>> +static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
>> +				show_fan_maximum_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
>> +					show_fan_safe_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_target_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
>> +} \
>> +static ssize_t store_fan_target_speed_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
>> +	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
>> +static ssize_t show_fan_manual_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_manual(dev, buf, offset); \
>> +} \
>> +static ssize_t store_fan_manual_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_manual(dev, buf, count, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
>> +		   show_fan_manual_##offset, store_fan_manual_##offset);
>>     
>
> erk.  Can we use attribute groups here?
>
>   
>> +/*
>> + * Create the needed functions for each fan using the macro defined above 
>> + * (2 fans are supported)
>> + */
>> +sysfs_fan_speeds_offset(0);
>> +sysfs_fan_speeds_offset(1);
>> +
>> +/* Macro creating the sysfs entries for a fan */
>> +#define device_create_file_fan(ret, client, offset) \
>> +do { \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
>> +} while (0)
>>     
>
> And here?
>   

I looked at the code of other drivers, and I found a much better way to
create all these sysfs files, using SENSOR_DEVICE_ATTR_2 and
SENSOR_DEVICE_ATTR instead of DEVICE_ATTR. This also allow using loops
to create the sysfs files as needed, instead of big switch statements.

I used attribute groups for the fans.
> [snip, fixed cast of void*]
>
>   
> [snip, syntax fixed (function opening braces at column 1)]
>
>   
>> +static int __init applesmc_init(void)
>> +{
>> +	int ret;
>> +	int count;
>> +
>> +	struct dmi_match_data applesmc_dmi_data[] = {
>> +	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
>> +	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
>> +	/* MacBook: accelerometer and temperature set 0 */
>> +	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
>> +	/* MacBook: temperature set 1 */
>> +	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
>> +	};
>> +
>> +	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
>> +	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
>> +	struct dmi_system_id applesmc_whitelist[] = {
>> +		{ applesmc_dmi_match, "Apple MacBook Pro", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
>> +			(void*)&applesmc_dmi_data[0]},
>> +		{ applesmc_dmi_match, "Apple MacBook", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
>> +			(void*)&applesmc_dmi_data[1]},
>> +		{ applesmc_dmi_match, "Apple Macmini", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
>> +			(void*)&applesmc_dmi_data[2]},
>> +		{ .ident = NULL }
>> +	};
>>     
>
> The compiler will need to build the above arrays on the stack at runtime. 
> Is it possible to make these static so they are build at compile-time?  And
> to then make them __initdata so they get discarded?
>   
Fixed.

Updated patch below.

Thanks,

Best regards,

Nicolas


This driver provides support for the Apple System Management Controller, which
provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
temperature sensors, keyboard backlight control and fan control. Only
Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>

---

 MAINTAINERS              |    6 
 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  917 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 948 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 81bcc22..cbfdc3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -367,6 +367,12 @@ L:	linux-laptop@vger.kernel.org
 W:	http://www.canb.auug.org.au/~sfr/
 S:	Supported
 
+APPLE SMC DRIVER
+P:	Nicolas Boichat
+M:	nicolas@boichat.ch
+L:	mactel-linux-devel@lists.sourceforge.net
+S:	Maintained
+
 APPLETALK NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
 M:	acme@conectiva.com.br
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6d105a1..25b72a4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -594,6 +594,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..4060667
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,917 @@
+/*
+ * 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.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>
+
+/* 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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" /* r-o length 6 */
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" /* r-o length 6 */
+#define BACKLIGHT_KEY 		"LKSB" /* w-o */
+
+#define CLAMSHELL_KEY 		"MSLD" /* r-o length 1 (unused) */
+
+#define MOTION_SENSOR_X_KEY	"MO_X" /* r-o length 2 */
+#define MOTION_SENSOR_Y_KEY	"MO_Y" /* r-o length 2 */
+#define MOTION_SENSOR_Z_KEY	"MO_Z" /* r-o length 2 */
+#define MOTION_SENSOR_KEY	"MOCN" /* r/w length 2 */
+
+#define FANS_COUNT		"FNum" /* r-o length 1 */
+#define FANS_MANUAL		"FS! " /* r-w length 2 */
+#define FAN_ACTUAL_SPEED	"F0Ac" /* r-o length 2 */
+#define FAN_MIN_SPEED		"F0Mn" /* r-o length 2 */
+#define FAN_MAX_SPEED		"F0Mx" /* r-o length 2 */
+#define FAN_SAFE_SPEED		"F0Sf" /* r-o length 2 */
+#define FAN_TARGET_SPEED	"F0Tg" /* r-w length 2 */
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", 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_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_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 struct mutex applesmc_lock;
+
+/*
+ * __wait_status - Wait up to 100ms 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 < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == 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 ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug)
+			printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug)
+		printk(KERN_DEBUG "\n");
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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 ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * 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);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (!mutex_trylock(&applesmc_lock)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	mutex_unlock(&applesmc_lock);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+}
+
+/* Displays degree Celsius * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev,
+			struct device_attribute *devattr, char *sysfsbuf)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	const char* key =
+		temperature_sensors_sets[applesmc_temperature_set][attr->index];
+	
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	mutex_unlock(&applesmc_lock);
+
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+	struct sensor_device_attribute_2 *sensor_attr =
+						to_sensor_dev_attr_2(attr);
+
+	newkey[0] = fan_speed_keys[sensor_attr->nr][0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = fan_speed_keys[sensor_attr->nr][2];
+	newkey[3] = fan_speed_keys[sensor_attr->nr][3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev,
+					struct device_attribute *attr,
+					const char *sysfsbuf, size_t count)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+	struct sensor_device_attribute_2 *sensor_attr =
+						to_sensor_dev_attr_2(attr);
+
+	speed = simple_strtoul(sysfsbuf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = fan_speed_keys[sensor_attr->nr][0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = fan_speed_keys[sensor_attr->nr][2];
+	newkey[3] = fan_speed_keys[sensor_attr->nr][3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev,
+			struct device_attribute *devattr, char *sysfsbuf)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev,
+					 struct device_attribute *devattr, 
+					 const char *sysfsbuf, size_t count)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	input = simple_strtoul(sysfsbuf, NULL, 10);
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << attr->index);
+	else 
+		val = val & ~(0x01 << attr->index);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	mutex_lock(&applesmc_lock);
+	applesmc_calibrate();
+	mutex_unlock(&applesmc_lock);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	mutex_lock(&applesmc_lock);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	mutex_unlock(&applesmc_lock);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_actual_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 0, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_maximum_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 2, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_safe_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 3, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset); \
+\
+static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_manual, applesmc_store_fan_manual, offset); \
+\
+static struct attribute *fan##offset##_attributes[] = { \
+	&sensor_dev_attr_fan##offset##_actual_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_minimum_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_maximum_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_safe_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_target_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
+	NULL \
+};
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+static const struct attribute_group fan_attribute_groups[] = {
+	{ .attrs = fan0_attributes },
+	{ .attrs = fan1_attributes }
+};
+
+/*
+ * Temperature sensors sysfs entries.
+ */
+static SENSOR_DEVICE_ATTR(temperature_0, S_IRUGO,
+					applesmc_show_temperature, NULL, 0);
+static SENSOR_DEVICE_ATTR(temperature_1, S_IRUGO,
+					applesmc_show_temperature, NULL, 1);
+static SENSOR_DEVICE_ATTR(temperature_2, S_IRUGO,
+					applesmc_show_temperature, NULL, 2);
+static SENSOR_DEVICE_ATTR(temperature_3, S_IRUGO,
+					applesmc_show_temperature, NULL, 3);
+static SENSOR_DEVICE_ATTR(temperature_4, S_IRUGO,
+					applesmc_show_temperature, NULL, 4);
+static SENSOR_DEVICE_ATTR(temperature_5, S_IRUGO,
+					applesmc_show_temperature, NULL, 5);
+static SENSOR_DEVICE_ATTR(temperature_6, S_IRUGO,
+					applesmc_show_temperature, NULL, 6);
+
+static struct attribute *temperature_attributes[] = {
+	&sensor_dev_attr_temperature_0.dev_attr.attr,
+	&sensor_dev_attr_temperature_1.dev_attr.attr,
+	&sensor_dev_attr_temperature_2.dev_attr.attr,
+	&sensor_dev_attr_temperature_3.dev_attr.attr,
+	&sensor_dev_attr_temperature_4.dev_attr.attr,
+	&sensor_dev_attr_temperature_5.dev_attr.attr,
+	&sensor_dev_attr_temperature_6.dev_attr.attr,
+};
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data = id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void)
+{
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void)
+{
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static __initdata struct dmi_match_data applesmc_dmi_data[] = {
+/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	{ .accelerometer = 1, .light = 1, .temperature_set = 0 },
+/* MacBook: accelerometer and temperature set 0 */
+	{ .accelerometer = 1, .light = 0, .temperature_set = 0 },
+/* MacBook: temperature set 1 */
+	{ .accelerometer = 0, .light = 0, .temperature_set = 1 }
+};
+
+/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+ * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+static __initdata struct dmi_system_id applesmc_whitelist[] = {
+	{ applesmc_dmi_match, "Apple MacBook Pro", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+		(void*)&applesmc_dmi_data[0]},
+	{ applesmc_dmi_match, "Apple MacBook", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+		(void*)&applesmc_dmi_data[1]},
+	{ applesmc_dmi_match, "Apple Macmini", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+		(void*)&applesmc_dmi_data[2]},
+	{ .ident = NULL }
+};
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+	int i;
+
+	mutex_init(&applesmc_lock);
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	} else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			ret = sysfs_create_group(&pdev->dev.kobj,
+						 &fan_attribute_groups[1]);
+			if (ret)
+				goto out_device;
+		case 1:
+			ret = sysfs_create_group(&pdev->dev.kobj,
+						 &fan_attribute_groups[0]);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	for (i = 0;
+	     temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
+	     i++) {
+		ret = sysfs_create_file(&pdev->dev.kobj,
+						temperature_attributes[i]);
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-20  7:12       ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-20  7:12 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

Hello,

Andrew Morton wrote:
> On Mon, 19 Mar 2007 13:19:00 +0800 Nicolas Boichat <nicolas at boichat.ch> wrote:
>
>   
>> This driver provides support for the Apple System Management Controller, which
>> provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
>> temperature sensors, keyboard backlight control and fan control. Only
>> Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).
>>
>>     
>
> It's trivia time:
>   
> [snip, syntax fixed (C++-style comments replaced)]
>> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
>> +static const char* temperature_sensors_sets[][8] = {
>> +	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
>> +	{ "TC0D", "TC0P", NULL }
>> +};
>>     
>
> The NULLs here are harmless, but unneeded.
>   
Actually, I think it's safer to keep them. I use these NULL values as an
end-of-list marker in applesmc_init:
    for (i = 0;
         temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
         i++) { ...

If you remove these NULLs, and, later, add a temperature sensor to the
first set without thinking about incrementing the array size, you won't
get any warnings, and the code will not work, while if you keep them,
you will get a warning (drivers/hwmon/applesmc.c:73: warning: excess
elements in array initializer).
>   
> [snip, removed unneeded "= 0" in global variables]
>
>   
>> +static DECLARE_MUTEX(applesmc_sem);
>>     
>
> Semaphores should be used only when their counting feature is required.  I
> think thsi can be switched to `struct mutex'.
>   
Fixed. Note: this code comes from hdaps, which was, and is still, using
semaphores, it should probably be fixed too.
> [snip, "if" and "else" syntax fixed]
>>
>> +/*
>> + * Macro defining helper functions and DEVICE_ATTR for a fan sysfs entries.
>> + *  - show actual speed
>> + *  - show/store minimum speed
>> + *  - show maximum speed
>> + *  - show safe speed
>> + *  - show/store target speed
>> + *  - show/store manual mode
>> + */
>> +#define sysfs_fan_speeds_offset(offset) \
>> +static ssize_t show_fan_actual_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_ACTUAL_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_actual_speed, S_IRUGO, \
>> +					show_fan_actual_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_minimum_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_MIN_SPEED, offset); \
>> +} \
>> +static ssize_t store_fan_minimum_speed_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_speed(dev, buf, count, FAN_MIN_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
>> +	show_fan_minimum_speed_##offset, store_fan_minimum_speed_##offset); \
>> +\
>> +static ssize_t show_fan_maximum_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_MAX_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_maximum_speed, S_IRUGO, \
>> +				show_fan_maximum_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_safe_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_SAFE_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_safe_speed, S_IRUGO, \
>> +					show_fan_safe_speed_##offset, NULL); \
>> +\
>> +static ssize_t show_fan_target_speed_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_speed(dev, buf, FAN_TARGET_SPEED, offset); \
>> +} \
>> +static ssize_t store_fan_target_speed_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_speed(dev, buf, count, FAN_TARGET_SPEED, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
>> +	show_fan_target_speed_##offset, store_fan_target_speed_##offset); \
>> +static ssize_t show_fan_manual_##offset (struct device *dev, \
>> +				struct device_attribute *attr, char *buf) \
>> +{ \
>> +	return applesmc_show_fan_manual(dev, buf, offset); \
>> +} \
>> +static ssize_t store_fan_manual_##offset (struct device *dev, \
>> +		struct device_attribute *attr, const char *buf, size_t count) \
>> +{ \
>> +	return applesmc_store_fan_manual(dev, buf, count, offset); \
>> +} \
>> +static DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
>> +		   show_fan_manual_##offset, store_fan_manual_##offset);
>>     
>
> erk.  Can we use attribute groups here?
>
>   
>> +/*
>> + * Create the needed functions for each fan using the macro defined above 
>> + * (2 fans are supported)
>> + */
>> +sysfs_fan_speeds_offset(0);
>> +sysfs_fan_speeds_offset(1);
>> +
>> +/* Macro creating the sysfs entries for a fan */
>> +#define device_create_file_fan(ret, client, offset) \
>> +do { \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_actual_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_minimum_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_maximum_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_safe_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_target_speed.attr); \
>> +if (ret) break; \
>> +ret = sysfs_create_file(client, &dev_attr_fan##offset##_manual.attr); \
>> +} while (0)
>>     
>
> And here?
>   

I looked at the code of other drivers, and I found a much better way to
create all these sysfs files, using SENSOR_DEVICE_ATTR_2 and
SENSOR_DEVICE_ATTR instead of DEVICE_ATTR. This also allow using loops
to create the sysfs files as needed, instead of big switch statements.

I used attribute groups for the fans.
> [snip, fixed cast of void*]
>
>   
> [snip, syntax fixed (function opening braces at column 1)]
>
>   
>> +static int __init applesmc_init(void)
>> +{
>> +	int ret;
>> +	int count;
>> +
>> +	struct dmi_match_data applesmc_dmi_data[] = {
>> +	/* MacBook Pro: accelerometer, backlight and temperature set 0 */
>> +	  { .accelerometer = 1, .light = 1, .temperature_set = 0 },
>> +	/* MacBook: accelerometer and temperature set 0 */
>> +	  { .accelerometer = 1, .light = 0, .temperature_set = 0 },
>> +	/* MacBook: temperature set 1 */
>> +	  { .accelerometer = 0, .light = 0, .temperature_set = 1 }
>> +	};
>> +
>> +	/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
>> +	 * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
>> +	struct dmi_system_id applesmc_whitelist[] = {
>> +		{ applesmc_dmi_match, "Apple MacBook Pro", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
>> +			(void*)&applesmc_dmi_data[0]},
>> +		{ applesmc_dmi_match, "Apple MacBook", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
>> +			(void*)&applesmc_dmi_data[1]},
>> +		{ applesmc_dmi_match, "Apple Macmini", {
>> +		  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
>> +		  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
>> +			(void*)&applesmc_dmi_data[2]},
>> +		{ .ident = NULL }
>> +	};
>>     
>
> The compiler will need to build the above arrays on the stack at runtime. 
> Is it possible to make these static so they are build at compile-time?  And
> to then make them __initdata so they get discarded?
>   
Fixed.

Updated patch below.

Thanks,

Best regards,

Nicolas


This driver provides support for the Apple System Management Controller, which
provides an accelerometer (Apple Sudden Motion Sensor), light sensors,
temperature sensors, keyboard backlight control and fan control. Only
Intel-based Apple's computers are supported (MacBook Pro, MacBook, MacMini).

Signed-off-by: Nicolas Boichat <nicolas at boichat.ch>

---

 MAINTAINERS              |    6 
 drivers/hwmon/Kconfig    |   24 +
 drivers/hwmon/Makefile   |    1 
 drivers/hwmon/applesmc.c |  917 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 948 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 81bcc22..cbfdc3e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -367,6 +367,12 @@ L:	linux-laptop at vger.kernel.org
 W:	http://www.canb.auug.org.au/~sfr/
 S:	Supported
 
+APPLE SMC DRIVER
+P:	Nicolas Boichat
+M:	nicolas at boichat.ch
+L:	mactel-linux-devel at lists.sourceforge.net
+S:	Maintained
+
 APPLETALK NETWORK LAYER
 P:	Arnaldo Carvalho de Melo
 M:	acme at conectiva.com.br
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6d105a1..25b72a4 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -594,6 +594,30 @@ config SENSORS_HDAPS
 	  Say Y here if you have an applicable laptop and want to experience
 	  the awesome power of hdaps.
 
+config SENSORS_APPLESMC
+	tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
+	depends on HWMON && INPUT && X86
+	select NEW_LEDS
+	select LEDS_CLASS
+	default n
+	help
+	  This driver provides support for the Apple System Management
+	  Controller, which provides an accelerometer (Apple Sudden Motion
+	  Sensor), light sensors, temperature sensors, keyboard backlight
+	  control and fan control.
+
+	  Only Intel-based Apple's computers are supported (MacBook Pro,
+	  MacBook, MacMini).
+
+	  Data from the different sensors, keyboard backlight control and fan
+	  control are accessible via sysfs.
+
+	  This driver also provides an absolute input class device, allowing
+	  the laptop to act as a pinball machine-esque joystick.
+
+	  Say Y here if you have an applicable laptop and want to experience
+	  the awesome power of applesmc.
+
 config HWMON_DEBUG_CHIP
 	bool "Hardware Monitoring Chip debugging messages"
 	depends on HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 4165c27..544f8d8 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_ADM1026)	+= adm1026.o
 obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
+obj-$(CONFIG_SENSORS_APPLESMC)	+= applesmc.o
 obj-$(CONFIG_SENSORS_AMS)	+= ams/
 obj-$(CONFIG_SENSORS_ATXP1)	+= atxp1.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
new file mode 100644
index 0000000..4060667
--- /dev/null
+++ b/drivers/hwmon/applesmc.c
@@ -0,0 +1,917 @@
+/*
+ * 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 at boichat.ch>
+ *
+ * Based on hdaps.c driver:
+ * Copyright (C) 2005 Robert Love <rml at novell.com>
+ * Copyright (C) 2005 Jesper Juhl <jesper.juhl at gmail.com>
+ *
+ * Fan control based on smcFanControl:
+ * Copyright (C) 2006 Hendrik Holtmann <holtmann at 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.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>
+
+/* 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	5 /* 0x300-0x304 */
+
+#define APPLESMC_STATUS_MASK	0x0f
+#define APPLESMC_READ_CMD	0x10
+#define APPLESMC_WRITE_CMD	0x11
+
+#define LIGHT_SENSOR_LEFT_KEY	"ALV0" /* r-o length 6 */
+#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" /* r-o length 6 */
+#define BACKLIGHT_KEY 		"LKSB" /* w-o */
+
+#define CLAMSHELL_KEY 		"MSLD" /* r-o length 1 (unused) */
+
+#define MOTION_SENSOR_X_KEY	"MO_X" /* r-o length 2 */
+#define MOTION_SENSOR_Y_KEY	"MO_Y" /* r-o length 2 */
+#define MOTION_SENSOR_Z_KEY	"MO_Z" /* r-o length 2 */
+#define MOTION_SENSOR_KEY	"MOCN" /* r/w length 2 */
+
+#define FANS_COUNT		"FNum" /* r-o length 1 */
+#define FANS_MANUAL		"FS! " /* r-w length 2 */
+#define FAN_ACTUAL_SPEED	"F0Ac" /* r-o length 2 */
+#define FAN_MIN_SPEED		"F0Mn" /* r-o length 2 */
+#define FAN_MAX_SPEED		"F0Mx" /* r-o length 2 */
+#define FAN_SAFE_SPEED		"F0Sf" /* r-o length 2 */
+#define FAN_TARGET_SPEED	"F0Tg" /* r-w length 2 */
+
+/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
+static const char* temperature_sensors_sets[][8] = {
+	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+	{ "TC0D", "TC0P", 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_PERIOD	(HZ/20)	/* poll for input every 1/20s */
+#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 int debug = 0;
+static struct platform_device *pdev;
+static s16 rest_x;
+static s16 rest_y;
+static struct timer_list applesmc_timer;
+static struct input_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 struct mutex applesmc_lock;
+
+/*
+ * __wait_status - Wait up to 100ms 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 < 10000; i++) {
+		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) = 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 ret = -EIO;
+	int i;
+
+	outb(APPLESMC_READ_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+	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))
+			goto out;
+		buffer[i] = inb(APPLESMC_DATA_PORT);
+		if (debug)
+			printk(KERN_DEBUG "<%x", buffer[i]);
+	}
+	if (debug)
+		printk(KERN_DEBUG "\n");
+
+	ret = 0;
+
+out:
+	return ret;
+}
+
+/*
+ * 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 ret = -EIO;
+	int i;
+
+	outb(APPLESMC_WRITE_CMD, APPLESMC_CMD_PORT);
+	if (__wait_status(0x0c))
+		goto out;
+	
+	for (i = 0; i < 4; i++) {
+		outb(key[i], APPLESMC_DATA_PORT);
+		if (__wait_status(0x04))
+			goto out;
+	}
+
+	outb(len, APPLESMC_DATA_PORT);
+
+	for (i = 0; i < len; i++) {
+		if (__wait_status(0x04))
+			goto out;
+		outb(buffer[i], APPLESMC_DATA_PORT);
+	}
+
+	ret = 0;
+out:
+	return ret;
+}
+
+/*
+ * 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);
+}
+
+static void applesmc_mousedev_poll(unsigned long unused)
+{
+	s16 x, y;
+
+	/* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
+	if (!mutex_trylock(&applesmc_lock)) {
+		mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+		return;
+	}
+
+	if (applesmc_read_motion_sensor(SENSOR_X, &x))
+		goto out;
+	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
+		goto out;
+
+	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
+	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
+	input_sync(applesmc_idev);
+
+out:
+	mod_timer(&applesmc_timer, jiffies + APPLESMC_POLL_PERIOD);
+
+	mutex_unlock(&applesmc_lock);
+}
+
+/* Sysfs Files */
+
+static ssize_t applesmc_position_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	int ret;
+	s16 x, y, z;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_motion_sensor(SENSOR_X, &x);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Y, &y);
+	if (ret)
+		goto out;
+	ret = applesmc_read_motion_sensor(SENSOR_Z, &z);
+	if (ret)
+		goto out;
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+}
+
+static ssize_t applesmc_light_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 left = 0, right = 0;
+	u8 buffer[6];
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, 6);
+	left = buffer[2];
+	if (ret)
+		goto out;
+	ret = applesmc_read_key(LIGHT_SENSOR_RIGHT_KEY, buffer, 6);
+	right = buffer[2];
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+}
+
+/* Displays degree Celsius * 100 */
+static ssize_t applesmc_show_temperature(struct device *dev,
+			struct device_attribute *devattr, char *sysfsbuf)
+{
+	int ret;
+	u8 buffer[2];
+	unsigned int temp;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	const char* key +		temperature_sensors_sets[applesmc_temperature_set][attr->index];
+	
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(key, buffer, 2);
+	temp = buffer[0]*100;
+	temp += (buffer[1] >> 6) * 25;
+
+	mutex_unlock(&applesmc_lock);
+
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", temp);
+}
+
+static ssize_t applesmc_show_fan_speed(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	unsigned int speed = 0;
+	char newkey[5];
+	u8 buffer[2];
+	struct sensor_device_attribute_2 *sensor_attr +						to_sensor_dev_attr_2(attr);
+
+	newkey[0] = fan_speed_keys[sensor_attr->nr][0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = fan_speed_keys[sensor_attr->nr][2];
+	newkey[3] = fan_speed_keys[sensor_attr->nr][3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(newkey, buffer, 2);
+	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%u\n", speed);
+}
+
+static ssize_t applesmc_store_fan_speed(struct device *dev,
+					struct device_attribute *attr,
+					const char *sysfsbuf, size_t count)
+{
+	int ret;
+	u32 speed;
+	char newkey[5];
+	u8 buffer[2];
+	struct sensor_device_attribute_2 *sensor_attr +						to_sensor_dev_attr_2(attr);
+
+	speed = simple_strtoul(sysfsbuf, NULL, 10);
+
+	if (speed > 0x4000) /* Bigger than a 14-bit value */
+		return -EINVAL;
+
+	newkey[0] = fan_speed_keys[sensor_attr->nr][0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = fan_speed_keys[sensor_attr->nr][2];
+	newkey[3] = fan_speed_keys[sensor_attr->nr][3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	buffer[0] = (speed >> 6) & 0xff;
+	buffer[1] = (speed << 2) & 0xff;
+	ret = applesmc_write_key(newkey, buffer, 2);
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_show_fan_manual(struct device *dev,
+			struct device_attribute *devattr, char *sysfsbuf)
+{
+	int ret;
+	u16 manual = 0;
+	u8 buffer[2];
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	manual = ((buffer[0] << 8 | buffer[1]) >> attr->index) & 0x01;
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return sprintf(sysfsbuf, "%d\n", manual);
+}
+
+static ssize_t applesmc_store_fan_manual(struct device *dev,
+					 struct device_attribute *devattr, 
+					 const char *sysfsbuf, size_t count)
+{
+	int ret;
+	u8 buffer[2];
+	u32 input;
+	u16 val;
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+	input = simple_strtoul(sysfsbuf, NULL, 10);
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+	val = (buffer[0] << 8 | buffer[1]);
+	if (ret)
+		goto out;
+
+	if (input)
+		val = val | (0x01 << attr->index);
+	else 
+		val = val & ~(0x01 << attr->index);
+
+	buffer[0] = (val >> 8) & 0xFF;
+	buffer[1] = val & 0xFF;
+
+	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+
+out:
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static ssize_t applesmc_calibrate_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+}
+
+static ssize_t applesmc_calibrate_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	mutex_lock(&applesmc_lock);
+	applesmc_calibrate();
+	mutex_unlock(&applesmc_lock);
+
+	return count;
+}
+
+static void applesmc_backlight_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	u8 buffer[2];
+	
+	mutex_lock(&applesmc_lock);
+	buffer[0] = value;
+	buffer[1] = 0x00;
+	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
+	mutex_unlock(&applesmc_lock);
+}
+
+static struct led_classdev applesmc_backlight = {
+	.name			= "smc:kbd_backlight",
+	.default_trigger	= "nand-disk",
+	.brightness_set		= applesmc_backlight_set,
+};
+
+static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
+static DEVICE_ATTR(calibrate, 0644,
+			applesmc_calibrate_show, applesmc_calibrate_store);
+
+static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
+
+/*
+ * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
+ *  - show actual speed
+ *  - show/store minimum speed
+ *  - show maximum speed
+ *  - show safe speed
+ *  - show/store target speed
+ *  - show/store manual mode
+ */
+#define sysfs_fan_speeds_offset(offset) \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_actual_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 0, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_maximum_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 2, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_safe_speed, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 3, offset); \
+\
+static SENSOR_DEVICE_ATTR_2(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset); \
+\
+static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_manual, applesmc_store_fan_manual, offset); \
+\
+static struct attribute *fan##offset##_attributes[] = { \
+	&sensor_dev_attr_fan##offset##_actual_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_minimum_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_maximum_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_safe_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_target_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
+	NULL \
+};
+
+/*
+ * Create the needed functions for each fan using the macro defined above 
+ * (2 fans are supported)
+ */
+sysfs_fan_speeds_offset(0);
+sysfs_fan_speeds_offset(1);
+
+static const struct attribute_group fan_attribute_groups[] = {
+	{ .attrs = fan0_attributes },
+	{ .attrs = fan1_attributes }
+};
+
+/*
+ * Temperature sensors sysfs entries.
+ */
+static SENSOR_DEVICE_ATTR(temperature_0, S_IRUGO,
+					applesmc_show_temperature, NULL, 0);
+static SENSOR_DEVICE_ATTR(temperature_1, S_IRUGO,
+					applesmc_show_temperature, NULL, 1);
+static SENSOR_DEVICE_ATTR(temperature_2, S_IRUGO,
+					applesmc_show_temperature, NULL, 2);
+static SENSOR_DEVICE_ATTR(temperature_3, S_IRUGO,
+					applesmc_show_temperature, NULL, 3);
+static SENSOR_DEVICE_ATTR(temperature_4, S_IRUGO,
+					applesmc_show_temperature, NULL, 4);
+static SENSOR_DEVICE_ATTR(temperature_5, S_IRUGO,
+					applesmc_show_temperature, NULL, 5);
+static SENSOR_DEVICE_ATTR(temperature_6, S_IRUGO,
+					applesmc_show_temperature, NULL, 6);
+
+static struct attribute *temperature_attributes[] = {
+	&sensor_dev_attr_temperature_0.dev_attr.attr,
+	&sensor_dev_attr_temperature_1.dev_attr.attr,
+	&sensor_dev_attr_temperature_2.dev_attr.attr,
+	&sensor_dev_attr_temperature_3.dev_attr.attr,
+	&sensor_dev_attr_temperature_4.dev_attr.attr,
+	&sensor_dev_attr_temperature_5.dev_attr.attr,
+	&sensor_dev_attr_temperature_6.dev_attr.attr,
+};
+
+/* Module stuff */
+
+/* 
+ * applesmc_dmi_match - found a match.  return one, short-circuiting the hunt.
+ */
+static int applesmc_dmi_match(struct dmi_system_id *id)
+{
+	int i = 0;
+	struct dmi_match_data* dmi_data = id->driver_data;
+	printk(KERN_INFO "applesmc: %s detected:\n", id->ident);
+	applesmc_accelerometer = dmi_data->accelerometer;
+	printk(KERN_INFO "applesmc:  - Model %s accelerometer\n",
+				applesmc_accelerometer ? "with" : "without");
+	applesmc_light = dmi_data->light;
+	printk(KERN_INFO "applesmc:  - Model %s light sensors and backlight\n",
+					applesmc_light ? "with" : "without");
+
+	applesmc_temperature_set =  dmi_data->temperature_set;
+	while (temperature_sensors_sets[applesmc_temperature_set][i] != NULL)
+		i++;
+	printk(KERN_INFO "applesmc:  - Model with %d temperature sensors\n", i);
+	return 1;
+}
+
+/* Create accelerometer ressources */
+static int applesmc_create_accelerometer(void)
+{
+	int ret;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
+	if (ret)
+		goto out;
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	if (ret)
+		goto out;
+
+	applesmc_idev = input_allocate_device();
+	if (!applesmc_idev) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initial calibrate for the input device */
+	applesmc_calibrate();
+
+	/* initialize the input class */
+	applesmc_idev->name = "applesmc";
+	applesmc_idev->cdev.dev = &pdev->dev;
+	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	input_set_abs_params(applesmc_idev, ABS_X,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+	input_set_abs_params(applesmc_idev, ABS_Y,
+			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
+
+	input_register_device(applesmc_idev);
+
+	/* start up our timer for the input device */
+	init_timer(&applesmc_timer);
+	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
+	add_timer(&applesmc_timer);
+
+	return 0;
+
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+/* Release all ressources used by the accelerometer */
+static void applesmc_release_accelerometer(void)
+{
+	del_timer_sync(&applesmc_timer);
+	input_unregister_device(applesmc_idev);
+}
+
+static __initdata struct dmi_match_data applesmc_dmi_data[] = {
+/* MacBook Pro: accelerometer, backlight and temperature set 0 */
+	{ .accelerometer = 1, .light = 1, .temperature_set = 0 },
+/* MacBook: accelerometer and temperature set 0 */
+	{ .accelerometer = 1, .light = 0, .temperature_set = 0 },
+/* MacBook: temperature set 1 */
+	{ .accelerometer = 0, .light = 0, .temperature_set = 1 }
+};
+
+/* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1".
+ * So we need to put "Apple MacBook Pro" before "Apple MacBook". */
+static __initdata struct dmi_system_id applesmc_whitelist[] = {
+	{ applesmc_dmi_match, "Apple MacBook Pro", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBookPro") },
+		(void*)&applesmc_dmi_data[0]},
+	{ applesmc_dmi_match, "Apple MacBook", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"MacBook") },
+		(void*)&applesmc_dmi_data[1]},
+	{ applesmc_dmi_match, "Apple Macmini", {
+	  DMI_MATCH(DMI_BOARD_VENDOR,"Apple"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"Macmini") },
+		(void*)&applesmc_dmi_data[2]},
+	{ .ident = NULL }
+};
+
+static int __init applesmc_init(void)
+{
+	int ret;
+	int count;
+	int i;
+
+	mutex_init(&applesmc_lock);
+
+	if (!dmi_check_system(applesmc_whitelist)) {
+		printk(KERN_WARNING "applesmc: supported laptop not found!\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS,
+								"applesmc")) {
+		ret = -ENXIO;
+		goto out;
+	}
+
+	ret = platform_driver_register(&applesmc_driver);
+	if (ret)
+		goto out_region;
+
+	pdev = platform_device_register_simple("applesmc", -1, NULL, 0);
+	if (IS_ERR(pdev)) {
+		ret = PTR_ERR(pdev);
+		goto out_driver;
+	}
+
+	/* create fan files */
+	count = applesmc_get_fan_count();
+	if (count < 0) {
+		printk(KERN_ERR "applesmc: Cannot get the number of fans.\n");
+	} else {
+		printk(KERN_INFO "applesmc: %d fans found.\n", count);
+
+		switch (count) {
+		default:
+			printk(KERN_WARNING "applesmc: More than 2 fans found,"
+					" but at most 2 fans are supported"
+						" by the driver.\n");
+		case 2:
+			ret = sysfs_create_group(&pdev->dev.kobj,
+						 &fan_attribute_groups[1]);
+			if (ret)
+				goto out_device;
+		case 1:
+			ret = sysfs_create_group(&pdev->dev.kobj,
+						 &fan_attribute_groups[0]);
+			if (ret)
+				goto out_device;
+		case 0:
+			;
+		}
+	}
+
+	for (i = 0;
+	     temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
+	     i++) {
+		ret = sysfs_create_file(&pdev->dev.kobj,
+						temperature_attributes[i]);
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_accelerometer) {
+		ret = applesmc_create_accelerometer();
+		if (ret)
+			goto out_device;
+	}
+
+	if (applesmc_light) {
+		/* Add light sensor file */
+		ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_light.attr);
+		if (ret)
+			goto out_accelerometer;
+
+		/* register as a led device */
+		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
+		if (ret < 0)
+			goto out_accelerometer;
+	}
+
+	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+	return 0;
+
+out_accelerometer:
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+out_device:
+	platform_device_unregister(pdev);
+out_driver:
+	platform_driver_unregister(&applesmc_driver);
+out_region:
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+out:
+	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
+	return ret;
+}
+
+static void __exit applesmc_exit(void)
+{
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+	if (applesmc_accelerometer)
+		applesmc_release_accelerometer();
+	platform_device_unregister(pdev);
+	platform_driver_unregister(&applesmc_driver);
+	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);
+
+	printk(KERN_INFO "applesmc: driver unloaded.\n");
+}
+
+module_init(applesmc_init);
+module_exit(applesmc_exit);
+
+MODULE_AUTHOR("Nicolas Boichat");
+MODULE_DESCRIPTION("Apple SMC");
+MODULE_LICENSE("GPL v2");




^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19 21:43   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Bob Copeland
@ 2007-03-20 10:08     ` Jean Delvare
  -1 siblings, 0 replies; 47+ messages in thread
From: Jean Delvare @ 2007-03-20 10:08 UTC (permalink / raw)
  To: Bob Copeland
  Cc: Nicolas Boichat, linux-kernel, rlove, linux-kernel, lm-sensors

On Mon, 19 Mar 2007 17:43:38 -0400, Bob Copeland wrote:
> I tried out an earlier version of this patch several months ago just to play
> around with the joystick part of the accelerometer driver on my MacBook, and
> found that it was backwards in the y-direction compared to what Neverball
> seemed to want (of course, NB has no way to invert the joystick).  I think
> I just did something like this in my own copy:
> 
> +       y = -y;
>         input_report_abs(applesmc_idev, ABS_X, x - rest_x);
>         input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
> 
> I don't claim you necessarily want to change it, but thought I'd pass it
> along.

This appears to be a common problem with these devices, the hdaps driver
(IBM) needs to invert the axis on some models too, and I seem to
remember something similar for the (not yet merged) HP laptops
accelerometer driver.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-03-20 10:08     ` Jean Delvare
  0 siblings, 0 replies; 47+ messages in thread
From: Jean Delvare @ 2007-03-20 10:08 UTC (permalink / raw)
  To: Bob Copeland
  Cc: Nicolas Boichat, linux-kernel, rlove, linux-kernel, lm-sensors

On Mon, 19 Mar 2007 17:43:38 -0400, Bob Copeland wrote:
> I tried out an earlier version of this patch several months ago just to play
> around with the joystick part of the accelerometer driver on my MacBook, and
> found that it was backwards in the y-direction compared to what Neverball
> seemed to want (of course, NB has no way to invert the joystick).  I think
> I just did something like this in my own copy:
> 
> +       y = -y;
>         input_report_abs(applesmc_idev, ABS_X, x - rest_x);
>         input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
> 
> I don't claim you necessarily want to change it, but thought I'd pass it
> along.

This appears to be a common problem with these devices, the hdaps driver
(IBM) needs to invert the axis on some models too, and I seem to
remember something similar for the (not yet merged) HP laptops
accelerometer driver.

-- 
Jean Delvare


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-20  7:02   ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
@ 2007-03-20 15:14     ` Bob Copeland
  2007-03-21  4:03     ` Bob Copeland
       [not found]     ` <eb4a44160703200016i74786682n41f87f3d88f90409@mail.gmail.com>
  2 siblings, 0 replies; 47+ messages in thread
From: Bob Copeland @ 2007-03-20 15:14 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, mactel-linux-devel

> I tried neverball on my Macbook Pro 1st generation (Core Duo, not Core 2
> Duo), and the x axis in inverted, not the y axis.
>
> Could you confirm which axis is inverted on your Macbook?

Indeed, my memory is hazy and it may well have been the x-axis.  I can't find
my modified copy.  I'll check it out again tonight along with your hdaps-gl and
let you know how it goes...

-Bob

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-03-20 16:12   ` Gerb Stralko
  -1 siblings, 0 replies; 47+ messages in thread
From: Gerb Stralko @ 2007-03-20 16:12 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

> +/* 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      5 /* 0x300-0x304 */
> +
> +#define APPLESMC_STATUS_MASK   0x0f
> +#define APPLESMC_READ_CMD      0x10
> +#define APPLESMC_WRITE_CMD     0x11
> +
> +#define LIGHT_SENSOR_LEFT_KEY  "ALV0" //r-o length 6
> +#define LIGHT_SENSOR_RIGHT_KEY "ALV1" //r-o length 6
> +#define BACKLIGHT_KEY          "LKSB" //w-o
> +
> +#define CLAMSHELL_KEY          "MSLD" //r-o length 1 (unused)
> +
> +#define MOTION_SENSOR_X_KEY    "MO_X" //r-o length 2
> +#define MOTION_SENSOR_Y_KEY    "MO_Y" //r-o length 2
> +#define MOTION_SENSOR_Z_KEY    "MO_Z" //r-o length 2
> +#define MOTION_SENSOR_KEY      "MOCN" //r/w length 2
> +
> +#define FANS_COUNT             "FNum" //r-o length 1
> +#define FANS_MANUAL            "FS! " //r-w length 2
> +#define FAN_ACTUAL_SPEED       "F0Ac" //r-o length 2
> +#define FAN_MIN_SPEED          "F0Mn" //r-o length 2
> +#define FAN_MAX_SPEED          "F0Mx" //r-o length 2
> +#define FAN_SAFE_SPEED         "F0Sf" //r-o length 2
> +#define FAN_TARGET_SPEED       "F0Tg" //r-w length 2
> +
> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
> +static const char* temperature_sensors_sets[][8] = {
> +       { "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
> +       { "TC0D", "TC0P", NULL }
> +};
> +
> +#define INIT_TIMEOUT_MSECS     5000    /* wait up to 5s for device init ... */
> +#define INIT_WAIT_MSECS                50      /* ... in 50ms increments */
> +
> +#define APPLESMC_POLL_PERIOD   (HZ/20) /* poll for input every 1/20s */
> +#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 int debug = 0;
> +static struct platform_device *pdev;
> +static s16 rest_x;
> +static s16 rest_y;
> +static struct timer_list applesmc_timer;
> +static struct input_dev *applesmc_idev;
> +
> +/* Indicates whether this computer has an accelerometer. */
> +static unsigned int applesmc_accelerometer = 0;
> +
> +/* Indicates whether this computer has light sensors and keyboard backlight. */
> +static unsigned int applesmc_light = 0;
> +
> +/* Indicates which temperature sensors set to use. */
> +static unsigned int applesmc_temperature_set = 0;
> +

Is it possible to put some of this in a header file?  I think it may
make the driver look alittle nicer and IMO easier to read.  If this is
a problem or not just plain _stupid_ then just ignore me.  I'm not
trying to be nit picky, i just think this is a *great* driver and I'm
excited to use it, but the cleaner it is the more manageable it will
be in the future.

-Jerry

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-03-20 16:12   ` Gerb Stralko
  0 siblings, 0 replies; 47+ messages in thread
From: Gerb Stralko @ 2007-03-20 16:12 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, lm-sensors, rlove, linux-kernel

> +/* 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      5 /* 0x300-0x304 */
> +
> +#define APPLESMC_STATUS_MASK   0x0f
> +#define APPLESMC_READ_CMD      0x10
> +#define APPLESMC_WRITE_CMD     0x11
> +
> +#define LIGHT_SENSOR_LEFT_KEY  "ALV0" //r-o length 6
> +#define LIGHT_SENSOR_RIGHT_KEY "ALV1" //r-o length 6
> +#define BACKLIGHT_KEY          "LKSB" //w-o
> +
> +#define CLAMSHELL_KEY          "MSLD" //r-o length 1 (unused)
> +
> +#define MOTION_SENSOR_X_KEY    "MO_X" //r-o length 2
> +#define MOTION_SENSOR_Y_KEY    "MO_Y" //r-o length 2
> +#define MOTION_SENSOR_Z_KEY    "MO_Z" //r-o length 2
> +#define MOTION_SENSOR_KEY      "MOCN" //r/w length 2
> +
> +#define FANS_COUNT             "FNum" //r-o length 1
> +#define FANS_MANUAL            "FS! " //r-w length 2
> +#define FAN_ACTUAL_SPEED       "F0Ac" //r-o length 2
> +#define FAN_MIN_SPEED          "F0Mn" //r-o length 2
> +#define FAN_MAX_SPEED          "F0Mx" //r-o length 2
> +#define FAN_SAFE_SPEED         "F0Sf" //r-o length 2
> +#define FAN_TARGET_SPEED       "F0Tg" //r-w length 2
> +
> +/* Temperature sensors keys. First set for Macbook(Pro), second for Macmini */
> +static const char* temperature_sensors_sets[][8] = {
> +       { "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
> +       { "TC0D", "TC0P", NULL }
> +};
> +
> +#define INIT_TIMEOUT_MSECS     5000    /* wait up to 5s for device init ... */
> +#define INIT_WAIT_MSECS                50      /* ... in 50ms increments */
> +
> +#define APPLESMC_POLL_PERIOD   (HZ/20) /* poll for input every 1/20s */
> +#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 int debug = 0;
> +static struct platform_device *pdev;
> +static s16 rest_x;
> +static s16 rest_y;
> +static struct timer_list applesmc_timer;
> +static struct input_dev *applesmc_idev;
> +
> +/* Indicates whether this computer has an accelerometer. */
> +static unsigned int applesmc_accelerometer = 0;
> +
> +/* Indicates whether this computer has light sensors and keyboard backlight. */
> +static unsigned int applesmc_light = 0;
> +
> +/* Indicates which temperature sensors set to use. */
> +static unsigned int applesmc_temperature_set = 0;
> +

Is it possible to put some of this in a header file?  I think it may
make the driver look alittle nicer and IMO easier to read.  If this is
a problem or not just plain _stupid_ then just ignore me.  I'm not
trying to be nit picky, i just think this is a *great* driver and I'm
excited to use it, but the cleaner it is the more manageable it will
be in the future.

-Jerry


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-20  7:02   ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
  2007-03-20 15:14     ` Bob Copeland
@ 2007-03-21  4:03     ` Bob Copeland
       [not found]     ` <eb4a44160703200016i74786682n41f87f3d88f90409@mail.gmail.com>
  2 siblings, 0 replies; 47+ messages in thread
From: Bob Copeland @ 2007-03-21  4:03 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, mactel-linux-devel

On Tue, Mar 20, 2007 at 03:02:14PM +0800, Nicolas Boichat wrote:
> I tried neverball on my Macbook Pro 1st generation (Core Duo, not Core 2
> Duo), and the x axis in inverted, not the y axis.
> 
> Could you confirm which axis is inverted on your Macbook?
> 
> Also, have you tried the modified hdaps-gl, available here:
> http://mactel-linux.svn.sourceforge.net/viewvc/mactel-linux/trunk/tools/hdaps-gl/
> ? Is it working correctly?

Ok I tried it out again and it is in fact the x-axis that is inverted
within neverball.  The hdaps-gl works fine (Macbook Core Duo here).  

Thanks for the driver!

-- 
Bob Copeland %% www.bobcopeland.com 


^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-20 10:08     ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Jean Delvare
@ 2007-03-22 10:36       ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-22 10:36 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Bob Copeland, linux-kernel, rlove, linux-kernel, lm-sensors

Jean Delvare wrote:
> On Mon, 19 Mar 2007 17:43:38 -0400, Bob Copeland wrote:
>   
>> I tried out an earlier version of this patch several months ago just to play
>> around with the joystick part of the accelerometer driver on my MacBook, and
>> found that it was backwards in the y-direction compared to what Neverball
>> seemed to want (of course, NB has no way to invert the joystick).  I think
>> I just did something like this in my own copy:
>>
>> +       y = -y;
>>         input_report_abs(applesmc_idev, ABS_X, x - rest_x);
>>         input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
>>
>> I don't claim you necessarily want to change it, but thought I'd pass it
>> along.
>>     
>
> This appears to be a common problem with these devices, the hdaps driver
> (IBM) needs to invert the axis on some models too, and I seem to
> remember something similar for the (not yet merged) HP laptops
> accelerometer driver.
>   
Ok, so let's invert the axis on the input device.

I think the raw x value in sysfs behaves the same way as hdaps (because
tools like hdaps-gl don't need any inversion), so I don't invert it.

Anyone with an IBM laptop could confirm this please?
 - x value gets more positive when you lift the right side on the laptop
(= tilted to the left)
 - y value gets more negative when you tilt the laptop backwards
(simply cat /sys/devices/platform/hdaps/position)

Thanks,

Best regards,

Nicolas

Invert x axis on applesmc input device to make it usable as a joystick.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>


---

 drivers/hwmon/applesmc.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4060667..581ed3e 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -349,6 +349,7 @@ 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_mousedev_poll(unsigned long unused)
@@ -366,6 +367,7 @@ static void applesmc_mousedev_poll(unsigned long unused)
 	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
 		goto out;
 
+	x = -x;
 	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
 	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
 	input_sync(applesmc_idev);



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-03-22 10:36       ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-03-22 10:36 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Bob Copeland, linux-kernel, rlove, linux-kernel, lm-sensors

Jean Delvare wrote:
> On Mon, 19 Mar 2007 17:43:38 -0400, Bob Copeland wrote:
>   
>> I tried out an earlier version of this patch several months ago just to play
>> around with the joystick part of the accelerometer driver on my MacBook, and
>> found that it was backwards in the y-direction compared to what Neverball
>> seemed to want (of course, NB has no way to invert the joystick).  I think
>> I just did something like this in my own copy:
>>
>> +       y = -y;
>>         input_report_abs(applesmc_idev, ABS_X, x - rest_x);
>>         input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
>>
>> I don't claim you necessarily want to change it, but thought I'd pass it
>> along.
>>     
>
> This appears to be a common problem with these devices, the hdaps driver
> (IBM) needs to invert the axis on some models too, and I seem to
> remember something similar for the (not yet merged) HP laptops
> accelerometer driver.
>   
Ok, so let's invert the axis on the input device.

I think the raw x value in sysfs behaves the same way as hdaps (because
tools like hdaps-gl don't need any inversion), so I don't invert it.

Anyone with an IBM laptop could confirm this please?
 - x value gets more positive when you lift the right side on the laptop
(= tilted to the left)
 - y value gets more negative when you tilt the laptop backwards
(simply cat /sys/devices/platform/hdaps/position)

Thanks,

Best regards,

Nicolas

Invert x axis on applesmc input device to make it usable as a joystick.

Signed-off-by: Nicolas Boichat <nicolas at boichat.ch>


---

 drivers/hwmon/applesmc.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4060667..581ed3e 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -349,6 +349,7 @@ 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_mousedev_poll(unsigned long unused)
@@ -366,6 +367,7 @@ static void applesmc_mousedev_poll(unsigned long unused)
 	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
 		goto out;
 
+	x = -x;
 	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
 	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
 	input_sync(applesmc_idev);




^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-19  5:19   ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-03-22 15:37     ` Dmitry Torokhov
  -1 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-03-22 15:37 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Nicolas,

On Monday 19 March 2007 01:19, Nicolas Boichat wrote:
> +       /* initialize the input class */
> +       applesmc_idev->name = "applesmc";

You may want to set applesmc_idev->id.bus = BUS_HOST;

> +       applesmc_idev->cdev.dev = &pdev->dev;
> +       applesmc_idev->evbit[0] = BIT(EV_ABS);
> +       input_set_abs_params(applesmc_idev, ABS_X,
> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +       input_set_abs_params(applesmc_idev, ABS_Y,
> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +
> +       input_register_device(applesmc_idev);

Please add error hanling here.

> +
> +       /* start up our timer for the input device */
> +       init_timer(&applesmc_timer);
> +       applesmc_timer.function = applesmc_mousedev_poll;
> +       applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
> +       add_timer(&applesmc_timer);
> 

Please consider implemention open and close methods for the input
device and start/stop timer from there - there is no point of checking
hardware state if noone is listening to events. 

-- 
Dmitry

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and
@ 2007-03-22 15:37     ` Dmitry Torokhov
  0 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-03-22 15:37 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Nicolas,

On Monday 19 March 2007 01:19, Nicolas Boichat wrote:
> +???????/* initialize the input class */
> +???????applesmc_idev->name = "applesmc";

You may want to set applesmc_idev->id.bus = BUS_HOST;

> +???????applesmc_idev->cdev.dev = &pdev->dev;
> +???????applesmc_idev->evbit[0] = BIT(EV_ABS);
> +???????input_set_abs_params(applesmc_idev, ABS_X,
> +???????????????????????-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +???????input_set_abs_params(applesmc_idev, ABS_Y,
> +???????????????????????-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
> +
> +???????input_register_device(applesmc_idev);

Please add error hanling here.

> +
> +???????/* start up our timer for the input device */
> +???????init_timer(&applesmc_timer);
> +???????applesmc_timer.function = applesmc_mousedev_poll;
> +???????applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
> +???????add_timer(&applesmc_timer);
> 

Please consider implemention open and close methods for the input
device and start/stop timer from there - there is no point of checking
hardware state if noone is listening to events. 

-- 
Dmitry


^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH] Apple SMC driver - fix input device
  2007-03-22 15:37     ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Dmitry Torokhov
@ 2007-04-09 13:53       ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-09 13:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Dmitry,

Sorry I did not receive your message originally, someone else pointed it
to me recently, and I recovered it from LKML archives.

Dmitry Torokhov wrote:
> Hi Nicolas,
>
> On Monday 19 March 2007 01:19, Nicolas Boichat wrote:
>   
>> +       /* initialize the input class */
>> +       applesmc_idev->name = "applesmc";
>>     
>
> You may want to set applesmc_idev->id.bus = BUS_HOST;
>
>   
>> +       applesmc_idev->cdev.dev = &pdev->dev;
>> +       applesmc_idev->evbit[0] = BIT(EV_ABS);
>> +       input_set_abs_params(applesmc_idev, ABS_X,
>> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
>> +       input_set_abs_params(applesmc_idev, ABS_Y,
>> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
>> +
>> +       input_register_device(applesmc_idev);
>>     
>
> Please add error hanling here.
>
>   
>> +
>> +       /* start up our timer for the input device */
>> +       init_timer(&applesmc_timer);
>> +       applesmc_timer.function = applesmc_mousedev_poll;
>> +       applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
>> +       add_timer(&applesmc_timer);
>>
>>     
>
> Please consider implemention open and close methods for the input
> device and start/stop timer from there - there is no point of checking
> hardware state if noone is listening to events. 
>   

Fixed all these. Thanks.

Andrew, could you please push this patch in mm too?

Best regards,

Nicolas

- Invert y axis on input device.
- Only activate the input device polling timer when the device is open.
- Others minor fixes asked by Dmitry Torokhov.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>


---

 drivers/hwmon/applesmc.c |   29 +++++++++++++++++++++++++----
 1 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4060667..f7b59fc 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -349,9 +349,22 @@ 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_mousedev_poll(unsigned long unused)
+static int applesmc_idev_open(struct input_dev *dev)
+{
+	add_timer(&applesmc_timer);
+
+	return 0;
+}
+
+static void applesmc_idev_close(struct input_dev *dev)
+{
+	del_timer_sync(&applesmc_timer);
+}
+
+static void applesmc_idev_poll(unsigned long unused)
 {
 	s16 x, y;
 
@@ -366,6 +379,7 @@ static void applesmc_mousedev_poll(unsigned long unused)
 	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
 		goto out;
 
+	x = -x;
 	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
 	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
 	input_sync(applesmc_idev);
@@ -739,23 +753,30 @@ static int applesmc_create_accelerometer(void)
 
 	/* initialize the input class */
 	applesmc_idev->name = "applesmc";
+	applesmc_idev->id.bustype = BUS_HOST;
 	applesmc_idev->cdev.dev = &pdev->dev;
 	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	applesmc_idev->open = applesmc_idev_open;
+	applesmc_idev->close = applesmc_idev_close;
 	input_set_abs_params(applesmc_idev, ABS_X,
 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
 	input_set_abs_params(applesmc_idev, ABS_Y,
 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
 
-	input_register_device(applesmc_idev);
+	ret = input_register_device(applesmc_idev);
+	if (ret)
+		goto out_idev;
 
 	/* start up our timer for the input device */
 	init_timer(&applesmc_timer);
-	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.function = applesmc_idev_poll;
 	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
-	add_timer(&applesmc_timer);
 
 	return 0;
 
+out_idev:
+	input_free_device(applesmc_idev);
+
 out:
 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
 	return ret;



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH] Apple SMC driver - fix input device
@ 2007-04-09 13:53       ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-09 13:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Dmitry,

Sorry I did not receive your message originally, someone else pointed it
to me recently, and I recovered it from LKML archives.

Dmitry Torokhov wrote:
> Hi Nicolas,
>
> On Monday 19 March 2007 01:19, Nicolas Boichat wrote:
>   
>> +       /* initialize the input class */
>> +       applesmc_idev->name = "applesmc";
>>     
>
> You may want to set applesmc_idev->id.bus = BUS_HOST;
>
>   
>> +       applesmc_idev->cdev.dev = &pdev->dev;
>> +       applesmc_idev->evbit[0] = BIT(EV_ABS);
>> +       input_set_abs_params(applesmc_idev, ABS_X,
>> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
>> +       input_set_abs_params(applesmc_idev, ABS_Y,
>> +                       -256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
>> +
>> +       input_register_device(applesmc_idev);
>>     
>
> Please add error hanling here.
>
>   
>> +
>> +       /* start up our timer for the input device */
>> +       init_timer(&applesmc_timer);
>> +       applesmc_timer.function = applesmc_mousedev_poll;
>> +       applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
>> +       add_timer(&applesmc_timer);
>>
>>     
>
> Please consider implemention open and close methods for the input
> device and start/stop timer from there - there is no point of checking
> hardware state if noone is listening to events. 
>   

Fixed all these. Thanks.

Andrew, could you please push this patch in mm too?

Best regards,

Nicolas

- Invert y axis on input device.
- Only activate the input device polling timer when the device is open.
- Others minor fixes asked by Dmitry Torokhov.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>


---

 drivers/hwmon/applesmc.c |   29 +++++++++++++++++++++++++----
 1 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4060667..f7b59fc 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -349,9 +349,22 @@ 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_mousedev_poll(unsigned long unused)
+static int applesmc_idev_open(struct input_dev *dev)
+{
+	add_timer(&applesmc_timer);
+
+	return 0;
+}
+
+static void applesmc_idev_close(struct input_dev *dev)
+{
+	del_timer_sync(&applesmc_timer);
+}
+
+static void applesmc_idev_poll(unsigned long unused)
 {
 	s16 x, y;
 
@@ -366,6 +379,7 @@ static void applesmc_mousedev_poll(unsigned long unused)
 	if (applesmc_read_motion_sensor(SENSOR_Y, &y))
 		goto out;
 
+	x = -x;
 	input_report_abs(applesmc_idev, ABS_X, x - rest_x);
 	input_report_abs(applesmc_idev, ABS_Y, y - rest_y);
 	input_sync(applesmc_idev);
@@ -739,23 +753,30 @@ static int applesmc_create_accelerometer(void)
 
 	/* initialize the input class */
 	applesmc_idev->name = "applesmc";
+	applesmc_idev->id.bustype = BUS_HOST;
 	applesmc_idev->cdev.dev = &pdev->dev;
 	applesmc_idev->evbit[0] = BIT(EV_ABS);
+	applesmc_idev->open = applesmc_idev_open;
+	applesmc_idev->close = applesmc_idev_close;
 	input_set_abs_params(applesmc_idev, ABS_X,
 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
 	input_set_abs_params(applesmc_idev, ABS_Y,
 			-256, 256, APPLESMC_INPUT_FUZZ, APPLESMC_INPUT_FLAT);
 
-	input_register_device(applesmc_idev);
+	ret = input_register_device(applesmc_idev);
+	if (ret)
+		goto out_idev;
 
 	/* start up our timer for the input device */
 	init_timer(&applesmc_timer);
-	applesmc_timer.function = applesmc_mousedev_poll;
+	applesmc_timer.function = applesmc_idev_poll;
 	applesmc_timer.expires = jiffies + APPLESMC_POLL_PERIOD;
-	add_timer(&applesmc_timer);
 
 	return 0;
 
+out_idev:
+	input_free_device(applesmc_idev);
+
 out:
 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
 	return ret;



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver - fix input device
  2007-04-09 13:53       ` [lm-sensors] " Nicolas Boichat
@ 2007-04-09 15:17         ` Dmitry Torokhov
  -1 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-04-09 15:17 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Nicolas,

On 4/9/07, Nicolas Boichat <nicolas@boichat.ch> wrote:
> Hi Dmitry,
>
> Sorry I did not receive your message originally, someone else pointed it
> to me recently, and I recovered it from LKML archives.
>

For some reason your server does not like @insightbb.com addresses.

>
> - Invert y axis on input device.
> - Only activate the input device polling timer when the device is open.
> - Others minor fixes asked by Dmitry Torokhov.
>
> Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
>

You may add:

 Acked-by: Dmitry Torokhov <dtor@mail.ru>

-- 
Dmitry

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [PATCH] Apple SMC driver - fix input device
@ 2007-04-09 15:17         ` Dmitry Torokhov
  0 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-04-09 15:17 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Andrew Morton, linux-kernel, lm-sensors, rlove, linux-kernel

Hi Nicolas,

On 4/9/07, Nicolas Boichat <nicolas@boichat.ch> wrote:
> Hi Dmitry,
>
> Sorry I did not receive your message originally, someone else pointed it
> to me recently, and I recovered it from LKML archives.
>

For some reason your server does not like @insightbb.com addresses.

>
> - Invert y axis on input device.
> - Only activate the input device polling timer when the device is open.
> - Others minor fixes asked by Dmitry Torokhov.
>
> Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
>

You may add:

 Acked-by: Dmitry Torokhov <dtor@mail.ru>

-- 
Dmitry

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver - fix input device
  2007-04-09 13:53       ` [lm-sensors] " Nicolas Boichat
@ 2007-04-09 20:04         ` Andrew Morton
  -1 siblings, 0 replies; 47+ messages in thread
From: Andrew Morton @ 2007-04-09 20:04 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Dmitry Torokhov, linux-kernel, lm-sensors, rlove, linux-kernel,
	Jean Delvare, Paul Mackerras, Benjamin Herrenschmidt

On Mon, 09 Apr 2007 21:53:32 +0800
Nicolas Boichat <nicolas@boichat.ch> wrote:

> Fixed all these. Thanks.
> 
> Andrew, could you please push this patch in mm too?
> 
> Best regards,
> 
> Nicolas
> 
> - Invert y axis on input device.
> - Only activate the input device polling timer when the device is open.
> - Others minor fixes asked by Dmitry Torokhov.
> 
> Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>

Thanks.

I've been assuming that this patch will go via Jean, since it is in
drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
more a Paul or Dmitry thing.  Or a me thing.

Who wants to take it??

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [PATCH] Apple SMC driver - fix input device
@ 2007-04-09 20:04         ` Andrew Morton
  0 siblings, 0 replies; 47+ messages in thread
From: Andrew Morton @ 2007-04-09 20:04 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Dmitry Torokhov, linux-kernel, lm-sensors, rlove, linux-kernel,
	Jean Delvare, Paul Mackerras, Benjamin Herrenschmidt

On Mon, 09 Apr 2007 21:53:32 +0800
Nicolas Boichat <nicolas@boichat.ch> wrote:

> Fixed all these. Thanks.
> 
> Andrew, could you please push this patch in mm too?
> 
> Best regards,
> 
> Nicolas
> 
> - Invert y axis on input device.
> - Only activate the input device polling timer when the device is open.
> - Others minor fixes asked by Dmitry Torokhov.
> 
> Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>

Thanks.

I've been assuming that this patch will go via Jean, since it is in
drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
more a Paul or Dmitry thing.  Or a me thing.

Who wants to take it??

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver - fix input device
  2007-04-09 20:04         ` [lm-sensors] " Andrew Morton
@ 2007-04-09 20:11           ` Dmitry Torokhov
  -1 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-04-09 20:11 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Nicolas Boichat, linux-kernel, lm-sensors, rlove, linux-kernel,
	Jean Delvare, Paul Mackerras, Benjamin Herrenschmidt

On 4/9/07, Andrew Morton <akpm@linux-foundation.org> wrote:
> On Mon, 09 Apr 2007 21:53:32 +0800
> Nicolas Boichat <nicolas@boichat.ch> wrote:
>
> > Fixed all these. Thanks.
> >
> > Andrew, could you please push this patch in mm too?
> >
> > Best regards,
> >
> > Nicolas
> >
> > - Invert y axis on input device.
> > - Only activate the input device polling timer when the device is open.
> > - Others minor fixes asked by Dmitry Torokhov.
> >
> > Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
>
> Thanks.
>
> I've been assuming that this patch will go via Jean, since it is in
> drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
> more a Paul or Dmitry thing.  Or a me thing.
>
> Who wants to take it??

I'd say Jean - he's maintaining drivers/hwmon, isn't he?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [PATCH] Apple SMC driver - fix input device
@ 2007-04-09 20:11           ` Dmitry Torokhov
  0 siblings, 0 replies; 47+ messages in thread
From: Dmitry Torokhov @ 2007-04-09 20:11 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Nicolas Boichat, linux-kernel, lm-sensors, rlove, linux-kernel,
	Jean Delvare, Paul Mackerras, Benjamin Herrenschmidt

On 4/9/07, Andrew Morton <akpm@linux-foundation.org> wrote:
> On Mon, 09 Apr 2007 21:53:32 +0800
> Nicolas Boichat <nicolas@boichat.ch> wrote:
>
> > Fixed all these. Thanks.
> >
> > Andrew, could you please push this patch in mm too?
> >
> > Best regards,
> >
> > Nicolas
> >
> > - Invert y axis on input device.
> > - Only activate the input device polling timer when the device is open.
> > - Others minor fixes asked by Dmitry Torokhov.
> >
> > Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
>
> Thanks.
>
> I've been assuming that this patch will go via Jean, since it is in
> drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
> more a Paul or Dmitry thing.  Or a me thing.
>
> Who wants to take it??

I'd say Jean - he's maintaining drivers/hwmon, isn't he?

-- 
Dmitry

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [PATCH] Apple SMC driver - fix input device
  2007-04-09 20:04         ` [lm-sensors] " Andrew Morton
@ 2007-04-09 21:51           ` Paul Mackerras
  -1 siblings, 0 replies; 47+ messages in thread
From: Paul Mackerras @ 2007-04-09 21:51 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Nicolas Boichat, Dmitry Torokhov, linux-kernel, lm-sensors,
	rlove, linux-kernel, Jean Delvare, Benjamin Herrenschmidt

Andrew Morton writes:

> I've been assuming that this patch will go via Jean, since it is in
> drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
> more a Paul or Dmitry thing.  Or a me thing.
> 
> Who wants to take it??

Not me, and I expect not Ben, since it's an intel-only mac thingy...

Paul.

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [PATCH] Apple SMC driver - fix input device
@ 2007-04-09 21:51           ` Paul Mackerras
  0 siblings, 0 replies; 47+ messages in thread
From: Paul Mackerras @ 2007-04-09 21:51 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Nicolas Boichat, Dmitry Torokhov, linux-kernel, lm-sensors,
	rlove, linux-kernel, Jean Delvare, Benjamin Herrenschmidt

Andrew Morton writes:

> I've been assuming that this patch will go via Jean, since it is in
> drivers/hwmon/applesmc.c.  But perhaps that is inappropriate, and it is
> more a Paul or Dmitry thing.  Or a me thing.
> 
> Who wants to take it??

Not me, and I expect not Ben, since it's an intel-only mac thingy...

Paul.

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
@ 2007-04-11 12:25   ` Jean Delvare
  -1 siblings, 0 replies; 47+ messages in thread
From: Jean Delvare @ 2007-04-11 12:25 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, linux-kernel, rlove, lm-sensors

Hi Nicolas,

Sorry for the delay.

On Wed, 14 Mar 2007 17:29:39 +0800, Nicolas Boichat wrote:
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).
> 
> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
> months ago) by various users on different systems on the mactel-linux lists.
> 
> However, I'm not really satisfied with the way sysfs files are created:
> I use a lot of preprocessor macros to avoid repetition of code.
> The files created with these macros in /sys/devices/platform/applesmc are
> the following (on a Macbook Pro):
> fan0_actual_speed
> fan0_manual
> fan0_maximum_speed
> fan0_minimum_speed
> fan0_safe_speed
> fan0_target_speed
> fan1_actual_speed
> fan1_manual
> fan1_maximum_speed
> fan1_minimum_speed
> fan1_safe_speed
> fan1_target_speed
> temperature_0
> temperature_1
> temperature_2
> temperature_3
> temperature_4
> temperature_5
> temperature_6

First of all, please read Documentation/hwmon/sysfs-documentation, and
rename the entries to match the standard names whenever possible. Also
make sure that you use the standard units. If you use the standard
names and units and if you register your device with the hwmon class,
standard monitoring application will be able to support your driver.

> (i.e. temperature_* is created by one macro, fan*_actual_speed by
> another, ...)
> Is it acceptable programming practice? Is there a way to create these
> files in a more elegant manner?

Some old hardware monitoring drivers are still doing this, but this is
strongly discouraged for new drivers. It is possible (and easy) to
avoid using such macros, by sharing callback functions between various
sysfs files.

This is done by using SENSOR_DEVICE_ATTR instead of DEVICE_ATTR to
declare the sysfs attributes. It takes an extra parameter, which is the
entry number/index. You retrieve that index in the callback function:

static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
			 char *buf)
{
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
	int nr = attr->index;
	(...)
}

Take a look at the hwmon/f71805f.c driver for examples.

> Also, I never call any sysfs_remove_* function, as the files are
> deleted when the module is unloaded. Is it safe to do so? Doesn't it
> cause any memory leak?

This is considered a bad practice, as in theory you driver shouldn't
create the device by itself, and the files are associated to the device,
not the driver. All hardware monitoring drivers have been fixed now, so
please add the file removal calls in your driver too. You might find it
easier to use file groups rather than individual files. Again, see for
example the f71805f driver, and in particular the f71805f_attributes
array and f71805f_group structure, and the sysfs_create_group() and
sysfs_remove_group() calls.

I'm sorry but I really don't have the time for a complete review of
your driver.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-04-11 12:25   ` Jean Delvare
  0 siblings, 0 replies; 47+ messages in thread
From: Jean Delvare @ 2007-04-11 12:25 UTC (permalink / raw)
  To: Nicolas Boichat; +Cc: linux-kernel, linux-kernel, rlove, lm-sensors

Hi Nicolas,

Sorry for the delay.

On Wed, 14 Mar 2007 17:29:39 +0800, Nicolas Boichat wrote:
> I developed, a while ago, a driver the Apple System Management
> Controller, which provides an accelerometer (Apple Sudden Motion
> Sensor), light sensors, temperature sensors, keyboard backlight control
> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
> MacMini).
> 
> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
> months ago) by various users on different systems on the mactel-linux lists.
> 
> However, I'm not really satisfied with the way sysfs files are created:
> I use a lot of preprocessor macros to avoid repetition of code.
> The files created with these macros in /sys/devices/platform/applesmc are
> the following (on a Macbook Pro):
> fan0_actual_speed
> fan0_manual
> fan0_maximum_speed
> fan0_minimum_speed
> fan0_safe_speed
> fan0_target_speed
> fan1_actual_speed
> fan1_manual
> fan1_maximum_speed
> fan1_minimum_speed
> fan1_safe_speed
> fan1_target_speed
> temperature_0
> temperature_1
> temperature_2
> temperature_3
> temperature_4
> temperature_5
> temperature_6

First of all, please read Documentation/hwmon/sysfs-documentation, and
rename the entries to match the standard names whenever possible. Also
make sure that you use the standard units. If you use the standard
names and units and if you register your device with the hwmon class,
standard monitoring application will be able to support your driver.

> (i.e. temperature_* is created by one macro, fan*_actual_speed by
> another, ...)
> Is it acceptable programming practice? Is there a way to create these
> files in a more elegant manner?

Some old hardware monitoring drivers are still doing this, but this is
strongly discouraged for new drivers. It is possible (and easy) to
avoid using such macros, by sharing callback functions between various
sysfs files.

This is done by using SENSOR_DEVICE_ATTR instead of DEVICE_ATTR to
declare the sysfs attributes. It takes an extra parameter, which is the
entry number/index. You retrieve that index in the callback function:

static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
			 char *buf)
{
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
	int nr = attr->index;
	(...)
}

Take a look at the hwmon/f71805f.c driver for examples.

> Also, I never call any sysfs_remove_* function, as the files are
> deleted when the module is unloaded. Is it safe to do so? Doesn't it
> cause any memory leak?

This is considered a bad practice, as in theory you driver shouldn't
create the device by itself, and the files are associated to the device,
not the driver. All hardware monitoring drivers have been fixed now, so
please add the file removal calls in your driver too. You might find it
easier to use file groups rather than individual files. Again, see for
example the f71805f driver, and in particular the f71805f_attributes
array and f71805f_group structure, and the sysfs_create_group() and
sysfs_remove_group() calls.

I'm sorry but I really don't have the time for a complete review of
your driver.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control)
  2007-04-11 12:25   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Jean Delvare
@ 2007-04-11 12:47     ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-11 12:47 UTC (permalink / raw)
  To: Jean Delvare; +Cc: linux-kernel, linux-kernel, rlove, lm-sensors

Hi,

Thanks for your comments.

Jean Delvare wrote:
> Hi Nicolas,
> 
> Sorry for the delay.
> 
> On Wed, 14 Mar 2007 17:29:39 +0800, Nicolas Boichat wrote:
>> I developed, a while ago, a driver the Apple System Management
>> Controller, which provides an accelerometer (Apple Sudden Motion
>> Sensor), light sensors, temperature sensors, keyboard backlight control
>> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
>> MacMini).
>>
>> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
>> months ago) by various users on different systems on the mactel-linux lists.
>>
>> However, I'm not really satisfied with the way sysfs files are created:
>> I use a lot of preprocessor macros to avoid repetition of code.
>> The files created with these macros in /sys/devices/platform/applesmc are
>> the following (on a Macbook Pro):
>> fan0_actual_speed
>> fan0_manual
>> fan0_maximum_speed
>> fan0_minimum_speed
>> fan0_safe_speed
>> fan0_target_speed
>> fan1_actual_speed
>> fan1_manual
>> fan1_maximum_speed
>> fan1_minimum_speed
>> fan1_safe_speed
>> fan1_target_speed
>> temperature_0
>> temperature_1
>> temperature_2
>> temperature_3
>> temperature_4
>> temperature_5
>> temperature_6
> 
> First of all, please read Documentation/hwmon/sysfs-documentation, and
> rename the entries to match the standard names whenever possible. Also
> make sure that you use the standard units. If you use the standard
> names and units and if you register your device with the hwmon class,
> standard monitoring application will be able to support your driver.

Ok I'll have a look at this.

>> (i.e. temperature_* is created by one macro, fan*_actual_speed by
>> another, ...)
>> Is it acceptable programming practice? Is there a way to create these
>> files in a more elegant manner?
> 
> Some old hardware monitoring drivers are still doing this, but this is
> strongly discouraged for new drivers. It is possible (and easy) to
> avoid using such macros, by sharing callback functions between various
> sysfs files.
> 
> This is done by using SENSOR_DEVICE_ATTR instead of DEVICE_ATTR to
> declare the sysfs attributes. It takes an extra parameter, which is the
> entry number/index. You retrieve that index in the callback function:
> 
> static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
> 			 char *buf)
> {
> 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> 	int nr = attr->index;
> 	(...)
> }
> 
> Take a look at the hwmon/f71805f.c driver for examples.

Yes I'm using something like this in the version that is in the -mm tree
now.

>> Also, I never call any sysfs_remove_* function, as the files are
>> deleted when the module is unloaded. Is it safe to do so? Doesn't it
>> cause any memory leak?
> 
> This is considered a bad practice, as in theory you driver shouldn't
> create the device by itself, and the files are associated to the device,
> not the driver. All hardware monitoring drivers have been fixed now, so
> please add the file removal calls in your driver too. You might find it
> easier to use file groups rather than individual files. Again, see for
> example the f71805f driver, and in particular the f71805f_attributes
> array and f71805f_group structure, and the sysfs_create_group() and
> sysfs_remove_group() calls.

Ok I'll fix this.

> I'm sorry but I really don't have the time for a complete review of
> your driver.

Your comments are already very valuable, thanks.

Best regards,

Nicolas

^ permalink raw reply	[flat|nested] 47+ messages in thread

* Re: [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring
@ 2007-04-11 12:47     ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-11 12:47 UTC (permalink / raw)
  To: Jean Delvare; +Cc: linux-kernel, linux-kernel, rlove, lm-sensors

Hi,

Thanks for your comments.

Jean Delvare wrote:
> Hi Nicolas,
> 
> Sorry for the delay.
> 
> On Wed, 14 Mar 2007 17:29:39 +0800, Nicolas Boichat wrote:
>> I developed, a while ago, a driver the Apple System Management
>> Controller, which provides an accelerometer (Apple Sudden Motion
>> Sensor), light sensors, temperature sensors, keyboard backlight control
>> and fan control on Intel-based Apple's computers (MacBook Pro, MacBook,
>> MacMini).
>>
>> This patch has been tested successfully since kernel 2.6.18 (i.e. 3-4
>> months ago) by various users on different systems on the mactel-linux lists.
>>
>> However, I'm not really satisfied with the way sysfs files are created:
>> I use a lot of preprocessor macros to avoid repetition of code.
>> The files created with these macros in /sys/devices/platform/applesmc are
>> the following (on a Macbook Pro):
>> fan0_actual_speed
>> fan0_manual
>> fan0_maximum_speed
>> fan0_minimum_speed
>> fan0_safe_speed
>> fan0_target_speed
>> fan1_actual_speed
>> fan1_manual
>> fan1_maximum_speed
>> fan1_minimum_speed
>> fan1_safe_speed
>> fan1_target_speed
>> temperature_0
>> temperature_1
>> temperature_2
>> temperature_3
>> temperature_4
>> temperature_5
>> temperature_6
> 
> First of all, please read Documentation/hwmon/sysfs-documentation, and
> rename the entries to match the standard names whenever possible. Also
> make sure that you use the standard units. If you use the standard
> names and units and if you register your device with the hwmon class,
> standard monitoring application will be able to support your driver.

Ok I'll have a look at this.

>> (i.e. temperature_* is created by one macro, fan*_actual_speed by
>> another, ...)
>> Is it acceptable programming practice? Is there a way to create these
>> files in a more elegant manner?
> 
> Some old hardware monitoring drivers are still doing this, but this is
> strongly discouraged for new drivers. It is possible (and easy) to
> avoid using such macros, by sharing callback functions between various
> sysfs files.
> 
> This is done by using SENSOR_DEVICE_ATTR instead of DEVICE_ATTR to
> declare the sysfs attributes. It takes an extra parameter, which is the
> entry number/index. You retrieve that index in the callback function:
> 
> static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
> 			 char *buf)
> {
> 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
> 	int nr = attr->index;
> 	(...)
> }
> 
> Take a look at the hwmon/f71805f.c driver for examples.

Yes I'm using something like this in the version that is in the -mm tree
now.

>> Also, I never call any sysfs_remove_* function, as the files are
>> deleted when the module is unloaded. Is it safe to do so? Doesn't it
>> cause any memory leak?
> 
> This is considered a bad practice, as in theory you driver shouldn't
> create the device by itself, and the files are associated to the device,
> not the driver. All hardware monitoring drivers have been fixed now, so
> please add the file removal calls in your driver too. You might find it
> easier to use file groups rather than individual files. Again, see for
> example the f71805f driver, and in particular the f71805f_attributes
> array and f71805f_group structure, and the sysfs_create_group() and
> sysfs_remove_group() calls.

Ok I'll fix this.

> I'm sorry but I really don't have the time for a complete review of
> your driver.

Your comments are already very valuable, thanks.

Best regards,

Nicolas

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH 1/2] Apple SMC driver - standardize and sanitize sysfs tree + minor features addition
  2007-04-11 12:25   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Jean Delvare
@ 2007-04-13  5:33     ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-13  5:33 UTC (permalink / raw)
  To: Jean Delvare, Andrew Morton
  Cc: linux-kernel, linux-kernel, rlove, lm-sensors, benh, paulus, dtor

Hi again,

Jean Delvare wrote:
>>
>> However, I'm not really satisfied with the way sysfs files are created:
>> I use a lot of preprocessor macros to avoid repetition of code.
>> The files created with these macros in /sys/devices/platform/applesmc are
>> the following (on a Macbook Pro):
>> fan0_actual_speed
>> fan0_manual
>> fan0_maximum_speed
>> fan0_minimum_speed
>> fan0_safe_speed
>> fan0_target_speed
>> fan1_actual_speed
>> fan1_manual
>> fan1_maximum_speed
>> fan1_minimum_speed
>> fan1_safe_speed
>> fan1_target_speed
>> temperature_0
>> temperature_1
>> temperature_2
>> temperature_3
>> temperature_4
>> temperature_5
>> temperature_6
>>     
>
> First of all, please read Documentation/hwmon/sysfs-documentation, and
> rename the entries to match the standard names whenever possible. Also
> make sure that you use the standard units. If you use the standard
> names and units and if you register your device with the hwmon class,
> standard monitoring application will be able to support your driver.
>   

Fixed.

[snip]

>> Also, I never call any sysfs_remove_* function, as the files are
>> deleted when the module is unloaded. Is it safe to do so? Doesn't it
>> cause any memory leak?
>>     
>
> This is considered a bad practice, as in theory you driver shouldn't
> create the device by itself, and the files are associated to the device,
> not the driver. All hardware monitoring drivers have been fixed now, so
> please add the file removal calls in your driver too. You might find it
> easier to use file groups rather than individual files. Again, see for
> example the f71805f driver, and in particular the f71805f_attributes
> array and f71805f_group structure, and the sysfs_create_group() and
> sysfs_remove_group() calls.
>   

Fixed too.

I also added some sanity checks, and some minor features I discovered
using key enumeration (see next patch).

Best regards,

Nicolas

- Standardize applesmc to use sysfs filenames recommended by
  Documentation/hwmon/sysfs-interface, and register the device with the hwmon
  class.
- Use snprintf instead of sprintf in sysfs show handlers.
- Remove the sysfs files properly in case of initialisation problem, and when
  the driver is unloaded.
- Add data buffer length sanity checks.
- Improvements of SMC keys' comments (add data type reported by the device).
- Add temperature sensors to Macbook Pro.
- Add support for reading fan physical position (e.g. "Left Side")

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |  280 ++++++++++++++++++++++++++++++++--------------
 1 files changed, 192 insertions(+), 88 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index f7b59fc..531bc9a 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -37,40 +37,48 @@
 #include <linux/hwmon-sysfs.h>
 #include <asm/io.h>
 #include <linux/leds.h>
+#include <linux/hwmon.h>
 
-/* data port used by apple SMC */
+/* data port used by Apple SMC */
 #define APPLESMC_DATA_PORT	0x300
-/* command/status port used by apple SMC */
+/* command/status port used by Apple SMC */
 #define APPLESMC_CMD_PORT	0x304
 
-#define APPLESMC_NR_PORTS	5 /* 0x300-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 LIGHT_SENSOR_LEFT_KEY	"ALV0" /* r-o length 6 */
-#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" /* r-o length 6 */
-#define BACKLIGHT_KEY 		"LKSB" /* w-o */
+#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 length 1 (unused) */
+#define CLAMSHELL_KEY 		"MSLD" /* r-o ui8 (unused) */
 
-#define MOTION_SENSOR_X_KEY	"MO_X" /* r-o length 2 */
-#define MOTION_SENSOR_Y_KEY	"MO_Y" /* r-o length 2 */
-#define MOTION_SENSOR_Z_KEY	"MO_Z" /* r-o length 2 */
-#define MOTION_SENSOR_KEY	"MOCN" /* r/w length 2 */
+#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 length 1 */
-#define FANS_MANUAL		"FS! " /* r-w length 2 */
-#define FAN_ACTUAL_SPEED	"F0Ac" /* r-o length 2 */
-#define FAN_MIN_SPEED		"F0Mn" /* r-o length 2 */
-#define FAN_MAX_SPEED		"F0Mx" /* r-o length 2 */
-#define FAN_SAFE_SPEED		"F0Sf" /* r-o length 2 */
-#define FAN_TARGET_SPEED	"F0Tg" /* r-w length 2 */
+#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. First set for Macbook(Pro), second for Macmini */
-static const char* temperature_sensors_sets[][8] = {
-	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+/*
+ * Temperature sensors keys (sp78 - 2 bytes).
+ * First set for Macbook(Pro), second for Macmini.
+ */
+static const char* temperature_sensors_sets[][13] = {
+	{ "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H",
+	  "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL },
 	{ "TC0D", "TC0P", NULL }
 };
 
@@ -110,6 +118,7 @@ static s16 rest_x;
 static s16 rest_y;
 static struct timer_list applesmc_timer;
 static struct input_dev *applesmc_idev;
+static struct class_device *hwmon_class_dev;
 
 /* Indicates whether this computer has an accelerometer. */
 static unsigned int applesmc_accelerometer;
@@ -152,17 +161,22 @@ static int __wait_status(u8 val)
  */
 static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 {
-	int ret = -EIO;
 	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))
-		goto out;
+		return -EIO;
 	
 	for (i = 0; i < 4; i++) {
 		outb(key[i], APPLESMC_DATA_PORT);
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 	}
 	if (debug)
 		printk(KERN_DEBUG "<%s", key);
@@ -173,7 +187,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 
 	for (i = 0; i < len; i++) {
 		if (__wait_status(0x05))
-			goto out;
+			return -EIO;
 		buffer[i] = inb(APPLESMC_DATA_PORT);
 		if (debug)
 			printk(KERN_DEBUG "<%x", buffer[i]);
@@ -181,10 +195,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 	if (debug)
 		printk(KERN_DEBUG "\n");
 
-	ret = 0;
-
-out:
-	return ret;
+	return 0;
 }
 
 /*
@@ -194,30 +205,33 @@ out:
  */
 static int applesmc_write_key(const char* key, u8* buffer, u8 len)
 {
-	int ret = -EIO;
 	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))
-		goto out;
+		return -EIO;
 	
 	for (i = 0; i < 4; i++) {
 		outb(key[i], APPLESMC_DATA_PORT);
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 	}
 
 	outb(len, APPLESMC_DATA_PORT);
 
 	for (i = 0; i < len; i++) {
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 		outb(buffer[i], APPLESMC_DATA_PORT);
 	}
 
-	ret = 0;
-out:
-	return ret;
+	return 0;
 }
 
 /*
@@ -415,7 +429,7 @@ out:
 	if (ret)
 		return ret;
 	else
-		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+		return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
 }
 
 static ssize_t applesmc_light_show(struct device *dev,
@@ -439,10 +453,10 @@ out:
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+		return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
 }
 
-/* Displays degree Celsius * 100 */
+/* Displays degree Celsius * 1000 */
 static ssize_t applesmc_show_temperature(struct device *dev,
 			struct device_attribute *devattr, char *sysfsbuf)
 {
@@ -456,15 +470,15 @@ static ssize_t applesmc_show_temperature(struct device *dev,
 	mutex_lock(&applesmc_lock);
 
 	ret = applesmc_read_key(key, buffer, 2);
-	temp = buffer[0]*100;
-	temp += (buffer[1] >> 6) * 25;
+	temp = buffer[0]*1000;
+	temp += (buffer[1] >> 6) * 250;
 
 	mutex_unlock(&applesmc_lock);
 
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%u\n", temp);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
 }
 
 static ssize_t applesmc_show_fan_speed(struct device *dev,
@@ -492,7 +506,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%u\n", speed);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
 }
 
 static ssize_t applesmc_store_fan_speed(struct device *dev,
@@ -547,7 +561,7 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%d\n", manual);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
 }
 
 static ssize_t applesmc_store_fan_manual(struct device *dev,
@@ -587,10 +601,37 @@ out:
 		return count;
 }
 
+static ssize_t applesmc_show_fan_position(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	char newkey[5];
+	u8 buffer[17];
+	struct sensor_device_attribute_2 *sensor_attr =
+						to_sensor_dev_attr_2(attr);
+
+	newkey[0] = FAN_POSITION[0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = FAN_POSITION[2];
+	newkey[3] = FAN_POSITION[3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(newkey, buffer, 16);
+	buffer[16] = 0;
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
+}
+
 static ssize_t applesmc_calibrate_show(struct device *dev,
 				struct device_attribute *attr, char *sysfsbuf)
 {
-	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+	return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
 }
 
 static ssize_t applesmc_calibrate_store(struct device *dev,
@@ -625,6 +666,15 @@ static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
 static DEVICE_ATTR(calibrate, 0644,
 			applesmc_calibrate_show, applesmc_calibrate_store);
 
+static struct attribute *accelerometer_attributes[] = {
+	&dev_attr_position.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static const struct attribute_group accelerometer_attributes_group =
+	{ .attrs = accelerometer_attributes };
+
 static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
 
 /*
@@ -637,31 +687,35 @@ static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
  *  - show/store manual mode
  */
 #define sysfs_fan_speeds_offset(offset) \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_actual_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 0, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 0, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_maximum_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 2, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 2, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_safe_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 3, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 3, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
 \
 static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_manual, applesmc_store_fan_manual, offset); \
+	applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
+\
+static SENSOR_DEVICE_ATTR(fan##offset##_position, S_IRUGO, \
+	applesmc_show_fan_position, NULL, offset-1); \
 \
 static struct attribute *fan##offset##_attributes[] = { \
-	&sensor_dev_attr_fan##offset##_actual_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_minimum_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_maximum_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_safe_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_target_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
 	&sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_position.dev_attr.attr, \
 	NULL \
 };
 
@@ -669,42 +723,61 @@ static struct attribute *fan##offset##_attributes[] = { \
  * Create the needed functions for each fan using the macro defined above 
  * (2 fans are supported)
  */
-sysfs_fan_speeds_offset(0);
 sysfs_fan_speeds_offset(1);
+sysfs_fan_speeds_offset(2);
 
 static const struct attribute_group fan_attribute_groups[] = {
-	{ .attrs = fan0_attributes },
-	{ .attrs = fan1_attributes }
+	{ .attrs = fan1_attributes },
+	{ .attrs = fan2_attributes }
 };
 
 /*
  * Temperature sensors sysfs entries.
  */
-static SENSOR_DEVICE_ATTR(temperature_0, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_1_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 0);
-static SENSOR_DEVICE_ATTR(temperature_1, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_2_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 1);
-static SENSOR_DEVICE_ATTR(temperature_2, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_3_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 2);
-static SENSOR_DEVICE_ATTR(temperature_3, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_4_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 3);
-static SENSOR_DEVICE_ATTR(temperature_4, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_5_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 4);
-static SENSOR_DEVICE_ATTR(temperature_5, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_6_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 5);
-static SENSOR_DEVICE_ATTR(temperature_6, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_7_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp_8_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp_9_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp_10_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp_11_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp_12_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 11);
 
 static struct attribute *temperature_attributes[] = {
-	&sensor_dev_attr_temperature_0.dev_attr.attr,
-	&sensor_dev_attr_temperature_1.dev_attr.attr,
-	&sensor_dev_attr_temperature_2.dev_attr.attr,
-	&sensor_dev_attr_temperature_3.dev_attr.attr,
-	&sensor_dev_attr_temperature_4.dev_attr.attr,
-	&sensor_dev_attr_temperature_5.dev_attr.attr,
-	&sensor_dev_attr_temperature_6.dev_attr.attr,
+	&sensor_dev_attr_temp_1_input.dev_attr.attr,
+	&sensor_dev_attr_temp_2_input.dev_attr.attr,
+	&sensor_dev_attr_temp_3_input.dev_attr.attr,
+	&sensor_dev_attr_temp_4_input.dev_attr.attr,
+	&sensor_dev_attr_temp_5_input.dev_attr.attr,
+	&sensor_dev_attr_temp_6_input.dev_attr.attr,
+	&sensor_dev_attr_temp_7_input.dev_attr.attr,
+	&sensor_dev_attr_temp_8_input.dev_attr.attr,
+	&sensor_dev_attr_temp_9_input.dev_attr.attr,
+	&sensor_dev_attr_temp_10_input.dev_attr.attr,
+	&sensor_dev_attr_temp_11_input.dev_attr.attr,
+	&sensor_dev_attr_temp_12_input.dev_attr.attr,
+	NULL
 };
 
+static const struct attribute_group temperature_attributes_group =
+	{ .attrs = temperature_attributes };
+
 /* Module stuff */
 
 /* 
@@ -734,18 +807,15 @@ static int applesmc_create_accelerometer(void)
 {
 	int ret;
 
-	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
-	if (ret)
-		goto out;
-
-	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	ret = sysfs_create_group(&pdev->dev.kobj,
+					&accelerometer_attributes_group);
 	if (ret)
 		goto out;
 
 	applesmc_idev = input_allocate_device();
 	if (!applesmc_idev) {
 		ret = -ENOMEM;
-		goto out;
+		goto out_sysfs;
 	}
 
 	/* initial calibrate for the input device */
@@ -777,6 +847,9 @@ static int applesmc_create_accelerometer(void)
 out_idev:
 	input_free_device(applesmc_idev);
 
+out_sysfs:
+	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);	
+
 out:
 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
 	return ret;
@@ -787,6 +860,7 @@ static void applesmc_release_accelerometer(void)
 {
 	del_timer_sync(&applesmc_timer);
 	input_unregister_device(applesmc_idev);
+	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
 }
 
 static __initdata struct dmi_match_data applesmc_dmi_data[] = {
@@ -867,7 +941,7 @@ static int __init applesmc_init(void)
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[0]);
 			if (ret)
-				goto out_device;
+				goto out_fan_1;
 		case 0:
 			;
 		}
@@ -876,16 +950,24 @@ static int __init applesmc_init(void)
 	for (i = 0;
 	     temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
 	     i++) {
+		if (temperature_attributes[i] == NULL) {
+			printk(KERN_ERR "applesmc: More temperature sensors "
+				"in temperature_sensors_sets (at least %i)"
+				"than available sysfs files in "
+				"temperature_attributes (%i), please report "
+				"this bug.\n", i, i-1);
+			goto out_temperature;
+		}
 		ret = sysfs_create_file(&pdev->dev.kobj,
 						temperature_attributes[i]);
 		if (ret)
-			goto out_device;
+			goto out_temperature;
 	}
 
 	if (applesmc_accelerometer) {
 		ret = applesmc_create_accelerometer();
 		if (ret)
-			goto out_device;
+			goto out_temperature;
 	}
 
 	if (applesmc_light) {
@@ -897,15 +979,33 @@ static int __init applesmc_init(void)
 		/* register as a led device */
 		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
 		if (ret < 0)
-			goto out_accelerometer;
+			goto out_light_sysfs;
+	}
+
+	hwmon_class_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_class_dev)) {
+		ret = PTR_ERR(hwmon_class_dev);
+		goto out_light;
 	}
 
 	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+
 	return 0;
 
+out_light:
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+out_light_sysfs:
+	if (applesmc_light)
+		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
 out_accelerometer:
 	if (applesmc_accelerometer)
 		applesmc_release_accelerometer();
+out_temperature:
+	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
+out_fan_1:
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
 out_device:
 	platform_device_unregister(pdev);
 out_driver:
@@ -919,10 +1019,14 @@ out:
 
 static void __exit applesmc_exit(void)
 {
+	hwmon_device_unregister(hwmon_class_dev);	
 	if (applesmc_light)
 		led_classdev_unregister(&applesmc_backlight);
 	if (applesmc_accelerometer)
 		applesmc_release_accelerometer();
+	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&applesmc_driver);
 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);


^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH 1/2] Apple SMC driver - standardize and
@ 2007-04-13  5:33     ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-13  5:33 UTC (permalink / raw)
  To: Jean Delvare, Andrew Morton
  Cc: linux-kernel, linux-kernel, rlove, lm-sensors, benh, paulus, dtor

Hi again,

Jean Delvare wrote:
>>
>> However, I'm not really satisfied with the way sysfs files are created:
>> I use a lot of preprocessor macros to avoid repetition of code.
>> The files created with these macros in /sys/devices/platform/applesmc are
>> the following (on a Macbook Pro):
>> fan0_actual_speed
>> fan0_manual
>> fan0_maximum_speed
>> fan0_minimum_speed
>> fan0_safe_speed
>> fan0_target_speed
>> fan1_actual_speed
>> fan1_manual
>> fan1_maximum_speed
>> fan1_minimum_speed
>> fan1_safe_speed
>> fan1_target_speed
>> temperature_0
>> temperature_1
>> temperature_2
>> temperature_3
>> temperature_4
>> temperature_5
>> temperature_6
>>     
>
> First of all, please read Documentation/hwmon/sysfs-documentation, and
> rename the entries to match the standard names whenever possible. Also
> make sure that you use the standard units. If you use the standard
> names and units and if you register your device with the hwmon class,
> standard monitoring application will be able to support your driver.
>   

Fixed.

[snip]

>> Also, I never call any sysfs_remove_* function, as the files are
>> deleted when the module is unloaded. Is it safe to do so? Doesn't it
>> cause any memory leak?
>>     
>
> This is considered a bad practice, as in theory you driver shouldn't
> create the device by itself, and the files are associated to the device,
> not the driver. All hardware monitoring drivers have been fixed now, so
> please add the file removal calls in your driver too. You might find it
> easier to use file groups rather than individual files. Again, see for
> example the f71805f driver, and in particular the f71805f_attributes
> array and f71805f_group structure, and the sysfs_create_group() and
> sysfs_remove_group() calls.
>   

Fixed too.

I also added some sanity checks, and some minor features I discovered
using key enumeration (see next patch).

Best regards,

Nicolas

- Standardize applesmc to use sysfs filenames recommended by
  Documentation/hwmon/sysfs-interface, and register the device with the hwmon
  class.
- Use snprintf instead of sprintf in sysfs show handlers.
- Remove the sysfs files properly in case of initialisation problem, and when
  the driver is unloaded.
- Add data buffer length sanity checks.
- Improvements of SMC keys' comments (add data type reported by the device).
- Add temperature sensors to Macbook Pro.
- Add support for reading fan physical position (e.g. "Left Side")

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |  280 ++++++++++++++++++++++++++++++++--------------
 1 files changed, 192 insertions(+), 88 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index f7b59fc..531bc9a 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -37,40 +37,48 @@
 #include <linux/hwmon-sysfs.h>
 #include <asm/io.h>
 #include <linux/leds.h>
+#include <linux/hwmon.h>
 
-/* data port used by apple SMC */
+/* data port used by Apple SMC */
 #define APPLESMC_DATA_PORT	0x300
-/* command/status port used by apple SMC */
+/* command/status port used by Apple SMC */
 #define APPLESMC_CMD_PORT	0x304
 
-#define APPLESMC_NR_PORTS	5 /* 0x300-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 LIGHT_SENSOR_LEFT_KEY	"ALV0" /* r-o length 6 */
-#define LIGHT_SENSOR_RIGHT_KEY	"ALV1" /* r-o length 6 */
-#define BACKLIGHT_KEY 		"LKSB" /* w-o */
+#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 length 1 (unused) */
+#define CLAMSHELL_KEY 		"MSLD" /* r-o ui8 (unused) */
 
-#define MOTION_SENSOR_X_KEY	"MO_X" /* r-o length 2 */
-#define MOTION_SENSOR_Y_KEY	"MO_Y" /* r-o length 2 */
-#define MOTION_SENSOR_Z_KEY	"MO_Z" /* r-o length 2 */
-#define MOTION_SENSOR_KEY	"MOCN" /* r/w length 2 */
+#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 length 1 */
-#define FANS_MANUAL		"FS! " /* r-w length 2 */
-#define FAN_ACTUAL_SPEED	"F0Ac" /* r-o length 2 */
-#define FAN_MIN_SPEED		"F0Mn" /* r-o length 2 */
-#define FAN_MAX_SPEED		"F0Mx" /* r-o length 2 */
-#define FAN_SAFE_SPEED		"F0Sf" /* r-o length 2 */
-#define FAN_TARGET_SPEED	"F0Tg" /* r-w length 2 */
+#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. First set for Macbook(Pro), second for Macmini */
-static const char* temperature_sensors_sets[][8] = {
-	{ "TB0T", "TC0D", "TC0P", "Th0H", "Ts0P", "Th1H", "Ts1P", NULL },
+/*
+ * Temperature sensors keys (sp78 - 2 bytes).
+ * First set for Macbook(Pro), second for Macmini.
+ */
+static const char* temperature_sensors_sets[][13] = {
+	{ "TA0P", "TB0T", "TC0D", "TC0P", "TG0H", "TG0P", "TG0T", "Th0H",
+	  "Th1H", "Tm0P", "Ts0P", "Ts1P", NULL },
 	{ "TC0D", "TC0P", NULL }
 };
 
@@ -110,6 +118,7 @@ static s16 rest_x;
 static s16 rest_y;
 static struct timer_list applesmc_timer;
 static struct input_dev *applesmc_idev;
+static struct class_device *hwmon_class_dev;
 
 /* Indicates whether this computer has an accelerometer. */
 static unsigned int applesmc_accelerometer;
@@ -152,17 +161,22 @@ static int __wait_status(u8 val)
  */
 static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 {
-	int ret = -EIO;
 	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))
-		goto out;
+		return -EIO;
 	
 	for (i = 0; i < 4; i++) {
 		outb(key[i], APPLESMC_DATA_PORT);
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 	}
 	if (debug)
 		printk(KERN_DEBUG "<%s", key);
@@ -173,7 +187,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 
 	for (i = 0; i < len; i++) {
 		if (__wait_status(0x05))
-			goto out;
+			return -EIO;
 		buffer[i] = inb(APPLESMC_DATA_PORT);
 		if (debug)
 			printk(KERN_DEBUG "<%x", buffer[i]);
@@ -181,10 +195,7 @@ static int applesmc_read_key(const char* key, u8* buffer, u8 len)
 	if (debug)
 		printk(KERN_DEBUG "\n");
 
-	ret = 0;
-
-out:
-	return ret;
+	return 0;
 }
 
 /*
@@ -194,30 +205,33 @@ out:
  */
 static int applesmc_write_key(const char* key, u8* buffer, u8 len)
 {
-	int ret = -EIO;
 	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))
-		goto out;
+		return -EIO;
 	
 	for (i = 0; i < 4; i++) {
 		outb(key[i], APPLESMC_DATA_PORT);
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 	}
 
 	outb(len, APPLESMC_DATA_PORT);
 
 	for (i = 0; i < len; i++) {
 		if (__wait_status(0x04))
-			goto out;
+			return -EIO;
 		outb(buffer[i], APPLESMC_DATA_PORT);
 	}
 
-	ret = 0;
-out:
-	return ret;
+	return 0;
 }
 
 /*
@@ -415,7 +429,7 @@ out:
 	if (ret)
 		return ret;
 	else
-		return sprintf(buf, "(%d,%d,%d)\n", x, y, z);
+		return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
 }
 
 static ssize_t applesmc_light_show(struct device *dev,
@@ -439,10 +453,10 @@ out:
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "(%d,%d)\n", left, right);
+		return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
 }
 
-/* Displays degree Celsius * 100 */
+/* Displays degree Celsius * 1000 */
 static ssize_t applesmc_show_temperature(struct device *dev,
 			struct device_attribute *devattr, char *sysfsbuf)
 {
@@ -456,15 +470,15 @@ static ssize_t applesmc_show_temperature(struct device *dev,
 	mutex_lock(&applesmc_lock);
 
 	ret = applesmc_read_key(key, buffer, 2);
-	temp = buffer[0]*100;
-	temp += (buffer[1] >> 6) * 25;
+	temp = buffer[0]*1000;
+	temp += (buffer[1] >> 6) * 250;
 
 	mutex_unlock(&applesmc_lock);
 
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%u\n", temp);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", temp);
 }
 
 static ssize_t applesmc_show_fan_speed(struct device *dev,
@@ -492,7 +506,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%u\n", speed);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
 }
 
 static ssize_t applesmc_store_fan_speed(struct device *dev,
@@ -547,7 +561,7 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
 	if (ret)
 		return ret;
 	else
-		return sprintf(sysfsbuf, "%d\n", manual);
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
 }
 
 static ssize_t applesmc_store_fan_manual(struct device *dev,
@@ -587,10 +601,37 @@ out:
 		return count;
 }
 
+static ssize_t applesmc_show_fan_position(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	char newkey[5];
+	u8 buffer[17];
+	struct sensor_device_attribute_2 *sensor_attr +						to_sensor_dev_attr_2(attr);
+
+	newkey[0] = FAN_POSITION[0];
+	newkey[1] = '0' + sensor_attr->index;
+	newkey[2] = FAN_POSITION[2];
+	newkey[3] = FAN_POSITION[3];
+	newkey[4] = 0;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(newkey, buffer, 16);
+	buffer[16] = 0;
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
+}
+
 static ssize_t applesmc_calibrate_show(struct device *dev,
 				struct device_attribute *attr, char *sysfsbuf)
 {
-	return sprintf(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
+	return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
 }
 
 static ssize_t applesmc_calibrate_store(struct device *dev,
@@ -625,6 +666,15 @@ static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
 static DEVICE_ATTR(calibrate, 0644,
 			applesmc_calibrate_show, applesmc_calibrate_store);
 
+static struct attribute *accelerometer_attributes[] = {
+	&dev_attr_position.attr,
+	&dev_attr_calibrate.attr,
+	NULL
+};
+
+static const struct attribute_group accelerometer_attributes_group +	{ .attrs = accelerometer_attributes };
+
 static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
 
 /*
@@ -637,31 +687,35 @@ static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
  *  - show/store manual mode
  */
 #define sysfs_fan_speeds_offset(offset) \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_actual_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 0, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_input, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 0, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_minimum_speed, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_min, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 1, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_maximum_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 2, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_max, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 2, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_safe_speed, S_IRUGO, \
-			applesmc_show_fan_speed, NULL, 3, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_safe, S_IRUGO, \
+			applesmc_show_fan_speed, NULL, 3, offset-1); \
 \
-static SENSOR_DEVICE_ATTR_2(fan##offset##_target_speed, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset); \
+static SENSOR_DEVICE_ATTR_2(fan##offset##_output, S_IRUGO | S_IWUSR, \
+	applesmc_show_fan_speed, applesmc_store_fan_speed, 4, offset-1); \
 \
 static SENSOR_DEVICE_ATTR(fan##offset##_manual, S_IRUGO | S_IWUSR, \
-	applesmc_show_fan_manual, applesmc_store_fan_manual, offset); \
+	applesmc_show_fan_manual, applesmc_store_fan_manual, offset-1); \
+\
+static SENSOR_DEVICE_ATTR(fan##offset##_position, S_IRUGO, \
+	applesmc_show_fan_position, NULL, offset-1); \
 \
 static struct attribute *fan##offset##_attributes[] = { \
-	&sensor_dev_attr_fan##offset##_actual_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_minimum_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_maximum_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_safe_speed.dev_attr.attr, \
-	&sensor_dev_attr_fan##offset##_target_speed.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_input.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_min.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_max.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_safe.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_output.dev_attr.attr, \
 	&sensor_dev_attr_fan##offset##_manual.dev_attr.attr, \
+	&sensor_dev_attr_fan##offset##_position.dev_attr.attr, \
 	NULL \
 };
 
@@ -669,42 +723,61 @@ static struct attribute *fan##offset##_attributes[] = { \
  * Create the needed functions for each fan using the macro defined above 
  * (2 fans are supported)
  */
-sysfs_fan_speeds_offset(0);
 sysfs_fan_speeds_offset(1);
+sysfs_fan_speeds_offset(2);
 
 static const struct attribute_group fan_attribute_groups[] = {
-	{ .attrs = fan0_attributes },
-	{ .attrs = fan1_attributes }
+	{ .attrs = fan1_attributes },
+	{ .attrs = fan2_attributes }
 };
 
 /*
  * Temperature sensors sysfs entries.
  */
-static SENSOR_DEVICE_ATTR(temperature_0, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_1_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 0);
-static SENSOR_DEVICE_ATTR(temperature_1, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_2_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 1);
-static SENSOR_DEVICE_ATTR(temperature_2, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_3_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 2);
-static SENSOR_DEVICE_ATTR(temperature_3, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_4_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 3);
-static SENSOR_DEVICE_ATTR(temperature_4, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_5_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 4);
-static SENSOR_DEVICE_ATTR(temperature_5, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_6_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 5);
-static SENSOR_DEVICE_ATTR(temperature_6, S_IRUGO,
+static SENSOR_DEVICE_ATTR(temp_7_input, S_IRUGO,
 					applesmc_show_temperature, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp_8_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 7);
+static SENSOR_DEVICE_ATTR(temp_9_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp_10_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp_11_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp_12_input, S_IRUGO,
+					applesmc_show_temperature, NULL, 11);
 
 static struct attribute *temperature_attributes[] = {
-	&sensor_dev_attr_temperature_0.dev_attr.attr,
-	&sensor_dev_attr_temperature_1.dev_attr.attr,
-	&sensor_dev_attr_temperature_2.dev_attr.attr,
-	&sensor_dev_attr_temperature_3.dev_attr.attr,
-	&sensor_dev_attr_temperature_4.dev_attr.attr,
-	&sensor_dev_attr_temperature_5.dev_attr.attr,
-	&sensor_dev_attr_temperature_6.dev_attr.attr,
+	&sensor_dev_attr_temp_1_input.dev_attr.attr,
+	&sensor_dev_attr_temp_2_input.dev_attr.attr,
+	&sensor_dev_attr_temp_3_input.dev_attr.attr,
+	&sensor_dev_attr_temp_4_input.dev_attr.attr,
+	&sensor_dev_attr_temp_5_input.dev_attr.attr,
+	&sensor_dev_attr_temp_6_input.dev_attr.attr,
+	&sensor_dev_attr_temp_7_input.dev_attr.attr,
+	&sensor_dev_attr_temp_8_input.dev_attr.attr,
+	&sensor_dev_attr_temp_9_input.dev_attr.attr,
+	&sensor_dev_attr_temp_10_input.dev_attr.attr,
+	&sensor_dev_attr_temp_11_input.dev_attr.attr,
+	&sensor_dev_attr_temp_12_input.dev_attr.attr,
+	NULL
 };
 
+static const struct attribute_group temperature_attributes_group +	{ .attrs = temperature_attributes };
+
 /* Module stuff */
 
 /* 
@@ -734,18 +807,15 @@ static int applesmc_create_accelerometer(void)
 {
 	int ret;
 
-	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_position.attr);
-	if (ret)
-		goto out;
-
-	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_calibrate.attr);
+	ret = sysfs_create_group(&pdev->dev.kobj,
+					&accelerometer_attributes_group);
 	if (ret)
 		goto out;
 
 	applesmc_idev = input_allocate_device();
 	if (!applesmc_idev) {
 		ret = -ENOMEM;
-		goto out;
+		goto out_sysfs;
 	}
 
 	/* initial calibrate for the input device */
@@ -777,6 +847,9 @@ static int applesmc_create_accelerometer(void)
 out_idev:
 	input_free_device(applesmc_idev);
 
+out_sysfs:
+	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);	
+
 out:
 	printk(KERN_WARNING "applesmc: driver init failed (ret=%d)!\n", ret);
 	return ret;
@@ -787,6 +860,7 @@ static void applesmc_release_accelerometer(void)
 {
 	del_timer_sync(&applesmc_timer);
 	input_unregister_device(applesmc_idev);
+	sysfs_remove_group(&pdev->dev.kobj, &accelerometer_attributes_group);
 }
 
 static __initdata struct dmi_match_data applesmc_dmi_data[] = {
@@ -867,7 +941,7 @@ static int __init applesmc_init(void)
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[0]);
 			if (ret)
-				goto out_device;
+				goto out_fan_1;
 		case 0:
 			;
 		}
@@ -876,16 +950,24 @@ static int __init applesmc_init(void)
 	for (i = 0;
 	     temperature_sensors_sets[applesmc_temperature_set][i] != NULL;
 	     i++) {
+		if (temperature_attributes[i] = NULL) {
+			printk(KERN_ERR "applesmc: More temperature sensors "
+				"in temperature_sensors_sets (at least %i)"
+				"than available sysfs files in "
+				"temperature_attributes (%i), please report "
+				"this bug.\n", i, i-1);
+			goto out_temperature;
+		}
 		ret = sysfs_create_file(&pdev->dev.kobj,
 						temperature_attributes[i]);
 		if (ret)
-			goto out_device;
+			goto out_temperature;
 	}
 
 	if (applesmc_accelerometer) {
 		ret = applesmc_create_accelerometer();
 		if (ret)
-			goto out_device;
+			goto out_temperature;
 	}
 
 	if (applesmc_light) {
@@ -897,15 +979,33 @@ static int __init applesmc_init(void)
 		/* register as a led device */
 		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
 		if (ret < 0)
-			goto out_accelerometer;
+			goto out_light_sysfs;
+	}
+
+	hwmon_class_dev = hwmon_device_register(&pdev->dev);
+	if (IS_ERR(hwmon_class_dev)) {
+		ret = PTR_ERR(hwmon_class_dev);
+		goto out_light;
 	}
 
 	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
+
 	return 0;
 
+out_light:
+	if (applesmc_light)
+		led_classdev_unregister(&applesmc_backlight);
+out_light_sysfs:
+	if (applesmc_light)
+		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
 out_accelerometer:
 	if (applesmc_accelerometer)
 		applesmc_release_accelerometer();
+out_temperature:
+	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
+out_fan_1:
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
 out_device:
 	platform_device_unregister(pdev);
 out_driver:
@@ -919,10 +1019,14 @@ out:
 
 static void __exit applesmc_exit(void)
 {
+	hwmon_device_unregister(hwmon_class_dev);	
 	if (applesmc_light)
 		led_classdev_unregister(&applesmc_backlight);
 	if (applesmc_accelerometer)
 		applesmc_release_accelerometer();
+	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
+	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&applesmc_driver);
 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH 2/2] Apple SMC driver - implement key enumeration
  2007-04-13  5:33     ` [lm-sensors] [PATCH 1/2] Apple SMC driver - standardize and Nicolas Boichat
@ 2007-04-13  6:38       ` Nicolas Boichat
  -1 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-13  6:38 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Jean Delvare, linux-kernel, linux-kernel, rlove, lm-sensors,
	benh, paulus, dtor

Hello,

Apple SMC allows to enumerate all keys available on the device. This is
useful to discover new sensors. With this feature, I'll be able to ask
people with different macintel for their key lists, and update the
driver accordingly. (note: I don't think it is reasonable to auto-detect
temperature sensors when the driver is loaded, as enumerating all the
keys takes a long time)

It works this way:
# cd /sys/devices/platform/applesmc
# echo 2 > key_at_index
(replace 2 with any number between 0 and the value you can read in
key_count)
# cat key_at_index_name
ACID
# cat key_at_index_type
ch8*
# cat key_at_index_data_length
8
# hexdump -C key_at_index_data
00000000  ba 31 96 0b 50 05 10 74 |.1..P..t|
(key_at_index_data output is binary, is it acceptable for a sysfs file?)

Please tell me if you think there is a better way to implement the sysfs
interface.

Best regards,

Nicolas

Implement key enumeration in applesmc.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |  263 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 262 insertions(+), 1 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 531bc9a..4ec38ef 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -51,6 +51,10 @@
 #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) */
@@ -131,6 +135,12 @@ static unsigned int applesmc_temperature_set;
 
 static struct 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;
+
 /*
  * __wait_status - Wait up to 100ms for the status port to get a certain value
  * (masked with 0x0f), returning zero if the value is obtained.  Callers must
@@ -235,6 +245,73 @@ static int applesmc_write_key(const char* key, u8* buffer, u8 len)
 }
 
 /*
+ * 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.
  */
@@ -656,6 +733,157 @@ static void applesmc_backlight_set(struct led_classdev *led_cdev,
 	mutex_unlock(&applesmc_lock);
 }
 
+static ssize_t applesmc_key_count_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 buffer[4];
+	u32 count;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
+	count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
+						((u32)buffer[2]<<8) + buffer[3];
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
+}
+
+static ssize_t applesmc_key_at_index_read_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	if (ret) {
+		mutex_unlock(&applesmc_lock);
+
+		return ret;	
+	}
+
+	/* 
+	 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
+	 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
+	 */
+	ret = applesmc_read_key(key, sysfsbuf, info[0]);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret) {
+		return info[0];
+	}
+	else {
+		return ret;
+	}
+}
+
+static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret)
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
+	else
+		return ret;
+}
+
+static ssize_t applesmc_key_at_index_type_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret)
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
+	else
+		return ret;
+}
+
+static ssize_t applesmc_key_at_index_name_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret && key[0])
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
+	else
+		return -EINVAL;
+}
+
+static ssize_t applesmc_key_at_index_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
+}
+
+static ssize_t applesmc_key_at_index_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	mutex_lock(&applesmc_lock);
+
+	key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
+
+	mutex_unlock(&applesmc_lock);
+
+	return count;
+}
+
 static struct led_classdev applesmc_backlight = {
 	.name			= "smc:kbd_backlight",
 	.default_trigger	= "nand-disk",
@@ -677,6 +905,31 @@ static const struct attribute_group accelerometer_attributes_group =
 
 static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
 
+static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
+static DEVICE_ATTR(key_at_index, 0644,
+		applesmc_key_at_index_show, applesmc_key_at_index_store);
+static DEVICE_ATTR(key_at_index_name, 0444,
+					applesmc_key_at_index_name_show, NULL);
+static DEVICE_ATTR(key_at_index_type, 0444,
+					applesmc_key_at_index_type_show, NULL);
+static DEVICE_ATTR(key_at_index_data_length, 0444,
+				applesmc_key_at_index_data_length_show, NULL);
+static DEVICE_ATTR(key_at_index_data, 0444,
+				applesmc_key_at_index_read_show, NULL);
+
+static struct attribute *key_enumeration_attributes[] = {
+	&dev_attr_key_count.attr,
+	&dev_attr_key_at_index.attr,
+	&dev_attr_key_at_index_name.attr,
+	&dev_attr_key_at_index_type.attr,
+	&dev_attr_key_at_index_data_length.attr,
+	&dev_attr_key_at_index_data.attr,
+	NULL
+};
+
+static const struct attribute_group key_enumeration_group =
+	{ .attrs = key_enumeration_attributes };
+
 /*
  * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
  *  - show actual speed
@@ -920,6 +1173,11 @@ static int __init applesmc_init(void)
 		goto out_driver;
 	}
 
+	/* Create key enumeration sysfs files */
+	ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
+	if (ret)
+		goto out_device;
+
 	/* create fan files */
 	count = applesmc_get_fan_count();
 	if (count < 0) {
@@ -936,7 +1194,7 @@ static int __init applesmc_init(void)
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[1]);
 			if (ret)
-				goto out_device;
+				goto out_key_enumeration;
 		case 1:
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[0]);
@@ -1006,6 +1264,8 @@ out_temperature:
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
 out_fan_1:
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
+out_key_enumeration:
+	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
 out_device:
 	platform_device_unregister(pdev);
 out_driver:
@@ -1027,6 +1287,7 @@ static void __exit applesmc_exit(void)
 	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
+	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&applesmc_driver);
 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [lm-sensors] [PATCH 2/2] Apple SMC driver - implement key
@ 2007-04-13  6:38       ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-13  6:38 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Jean Delvare, linux-kernel, linux-kernel, rlove, lm-sensors,
	benh, paulus, dtor

Hello,

Apple SMC allows to enumerate all keys available on the device. This is
useful to discover new sensors. With this feature, I'll be able to ask
people with different macintel for their key lists, and update the
driver accordingly. (note: I don't think it is reasonable to auto-detect
temperature sensors when the driver is loaded, as enumerating all the
keys takes a long time)

It works this way:
# cd /sys/devices/platform/applesmc
# echo 2 > key_at_index
(replace 2 with any number between 0 and the value you can read in
key_count)
# cat key_at_index_name
ACID
# cat key_at_index_type
ch8*
# cat key_at_index_data_length
8
# hexdump -C key_at_index_data
00000000  ba 31 96 0b 50 05 10 74 |.1..P..t|
(key_at_index_data output is binary, is it acceptable for a sysfs file?)

Please tell me if you think there is a better way to implement the sysfs
interface.

Best regards,

Nicolas

Implement key enumeration in applesmc.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |  263 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 262 insertions(+), 1 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 531bc9a..4ec38ef 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -51,6 +51,10 @@
 #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) */
@@ -131,6 +135,12 @@ static unsigned int applesmc_temperature_set;
 
 static struct 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;
+
 /*
  * __wait_status - Wait up to 100ms for the status port to get a certain value
  * (masked with 0x0f), returning zero if the value is obtained.  Callers must
@@ -235,6 +245,73 @@ static int applesmc_write_key(const char* key, u8* buffer, u8 len)
 }
 
 /*
+ * 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.
  */
@@ -656,6 +733,157 @@ static void applesmc_backlight_set(struct led_classdev *led_cdev,
 	mutex_unlock(&applesmc_lock);
 }
 
+static ssize_t applesmc_key_count_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	int ret;
+	u8 buffer[4];
+	u32 count;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_read_key(KEY_COUNT_KEY, buffer, 4);
+	count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
+						((u32)buffer[2]<<8) + buffer[3];
+
+	mutex_unlock(&applesmc_lock);
+	if (ret)
+		return ret;
+	else
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
+}
+
+static ssize_t applesmc_key_at_index_read_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	if (ret) {
+		mutex_unlock(&applesmc_lock);
+
+		return ret;	
+	}
+
+	/* 
+	 * info[0] maximum value (APPLESMC_MAX_DATA_LENGTH) is much lower than
+	 * PAGE_SIZE, so we don't need any checks before writing to sysfsbuf.
+	 */
+	ret = applesmc_read_key(key, sysfsbuf, info[0]);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret) {
+		return info[0];
+	}
+	else {
+		return ret;
+	}
+}
+
+static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret)
+		return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", info[0]);
+	else
+		return ret;
+}
+
+static ssize_t applesmc_key_at_index_type_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	char info[6];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	if (ret || !key[0]) {
+		mutex_unlock(&applesmc_lock);
+
+		return -EINVAL;
+	}
+
+	ret = applesmc_get_key_type(key, info);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret)
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", info+1);
+	else
+		return ret;
+}
+
+static ssize_t applesmc_key_at_index_name_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	char key[5];
+	int ret;
+
+	mutex_lock(&applesmc_lock);
+
+	ret = applesmc_get_key_at_index(key_at_index, key);
+
+	mutex_unlock(&applesmc_lock);
+
+	if (!ret && key[0])
+		return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
+	else
+		return -EINVAL;
+}
+
+static ssize_t applesmc_key_at_index_show(struct device *dev,
+				struct device_attribute *attr, char *sysfsbuf)
+{
+	return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
+}
+
+static ssize_t applesmc_key_at_index_store(struct device *dev,
+	struct device_attribute *attr, const char *sysfsbuf, size_t count)
+{
+	mutex_lock(&applesmc_lock);
+
+	key_at_index = simple_strtoul(sysfsbuf, NULL, 10);
+
+	mutex_unlock(&applesmc_lock);
+
+	return count;
+}
+
 static struct led_classdev applesmc_backlight = {
 	.name			= "smc:kbd_backlight",
 	.default_trigger	= "nand-disk",
@@ -677,6 +905,31 @@ static const struct attribute_group accelerometer_attributes_group  
 static DEVICE_ATTR(light, 0444, applesmc_light_show, NULL);
 
+static DEVICE_ATTR(key_count, 0444, applesmc_key_count_show, NULL);
+static DEVICE_ATTR(key_at_index, 0644,
+		applesmc_key_at_index_show, applesmc_key_at_index_store);
+static DEVICE_ATTR(key_at_index_name, 0444,
+					applesmc_key_at_index_name_show, NULL);
+static DEVICE_ATTR(key_at_index_type, 0444,
+					applesmc_key_at_index_type_show, NULL);
+static DEVICE_ATTR(key_at_index_data_length, 0444,
+				applesmc_key_at_index_data_length_show, NULL);
+static DEVICE_ATTR(key_at_index_data, 0444,
+				applesmc_key_at_index_read_show, NULL);
+
+static struct attribute *key_enumeration_attributes[] = {
+	&dev_attr_key_count.attr,
+	&dev_attr_key_at_index.attr,
+	&dev_attr_key_at_index_name.attr,
+	&dev_attr_key_at_index_type.attr,
+	&dev_attr_key_at_index_data_length.attr,
+	&dev_attr_key_at_index_data.attr,
+	NULL
+};
+
+static const struct attribute_group key_enumeration_group +	{ .attrs = key_enumeration_attributes };
+
 /*
  * Macro defining SENSOR_DEVICE_ATTR for a fan sysfs entries.
  *  - show actual speed
@@ -920,6 +1173,11 @@ static int __init applesmc_init(void)
 		goto out_driver;
 	}
 
+	/* Create key enumeration sysfs files */
+	ret = sysfs_create_group(&pdev->dev.kobj, &key_enumeration_group);
+	if (ret)
+		goto out_device;
+
 	/* create fan files */
 	count = applesmc_get_fan_count();
 	if (count < 0) {
@@ -936,7 +1194,7 @@ static int __init applesmc_init(void)
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[1]);
 			if (ret)
-				goto out_device;
+				goto out_key_enumeration;
 		case 1:
 			ret = sysfs_create_group(&pdev->dev.kobj,
 						 &fan_attribute_groups[0]);
@@ -1006,6 +1264,8 @@ out_temperature:
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
 out_fan_1:
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
+out_key_enumeration:
+	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
 out_device:
 	platform_device_unregister(pdev);
 out_driver:
@@ -1027,6 +1287,7 @@ static void __exit applesmc_exit(void)
 	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[0]);
 	sysfs_remove_group(&pdev->dev.kobj, &fan_attribute_groups[1]);
+	sysfs_remove_group(&pdev->dev.kobj, &key_enumeration_group);
 	platform_device_unregister(pdev);
 	platform_driver_unregister(&applesmc_driver);
 	release_region(APPLESMC_DATA_PORT, APPLESMC_NR_PORTS);



_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

^ permalink raw reply related	[flat|nested] 47+ messages in thread

* [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight
       [not found]     ` <eb4a44160703200016i74786682n41f87f3d88f90409@mail.gmail.com>
@ 2007-04-14  8:05       ` Nicolas Boichat
  2007-04-14  8:45         ` Richard Purdie
  0 siblings, 1 reply; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-14  8:05 UTC (permalink / raw)
  To: Andrew Morton, rpurdie; +Cc: Bradley Hook, mactel-linux-devel, linux-kernel

Hi,

I got this bug report a while ago:

Bradley Hook wrote:
> Slightly off-topic, but I've been experiencing a minor bug in the
> keyboard backlight feature.
>
> I say it is "minor" only because the feature serves no real functional
> purpose. You can activate a trigger called "heartbeat" that will cause
> the keyboard light to pulse at a speed based on the CPU usage. On my
> MBP17, after activating this trigger the machine will either lock-up
> or core dump within about a minute (timing is not consistent).
>   

This is caused by the fact applesmc_backlight_set locks a mutex (or more
precisely sleeps while trying to lock a mutex) while being in a softirq
context.

This might be obvious for others, but it was not for me, and there is
absolutely no mention in the documentation of the fact it is not always
safe to sleep in the brightness_set handler of a led_class device (it is
safe when it is called because someone wrote to the brightness sysfs file).

So, with the patch below, in case the mutex is locked for another
operation, the "brightness_set" called by the led trigger is simply
ignored. I don't think it is the behaviour we want, and I think it would
be a good idea to try again a little while afterwards. Richard, would
you like me to provide a patch for this? It would imply adding a
parameter to brightness_set indicating whether it's safe to sleep or
not, make it return an int, and modify the triggers code to retry if the
return value indicates an error.

Also, the led-trigger code seems buggy when it comes to locking. Setting
CONFIG_DEBUG_SPINLOCK_SLEEP causes a lot a warnings. The problem is that
the list of triggers is locked using a rw spinlock, but the rest of the
code seems to ignore that, and calls a lot of functions which can sleep
(kzalloc with GFP_KERNEL, sysfs_add_file, mutex_lock, etc...). I think
the list lock should be converted to a mutex (or maybe modified to use
RCU). I'm not very experienced in that domain, but if you want I can
provide a patch for this.

Best regards,

Nicolas

Cannot sleep in led->brightness_set handler if it is called from a softirq.
Reduce wait_status timetout from 100ms to 2ms, as wait_status either takes less
than 1.5 ms, or fails.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |   25 ++++++++++++++++++++-----
 1 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4ec38ef..c93c290 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -142,7 +142,7 @@ static struct mutex applesmc_lock;
 static unsigned int key_at_index;
 
 /*
- * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * __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.
  */
@@ -152,9 +152,14 @@ static int __wait_status(u8 val)
 
 	val = val & APPLESMC_STATUS_MASK;
 
-	for (i = 0; i < 10000; i++) {
-		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
+	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);
 	}
 
@@ -725,8 +730,18 @@ static void applesmc_backlight_set(struct led_classdev *led_cdev,
 						enum led_brightness value)
 {
 	u8 buffer[2];
-	
-	mutex_lock(&applesmc_lock);
+
+	if (in_interrupt()) {
+		/* Cannot sleep, as we are called from a timer. */
+		if (!mutex_trylock(&applesmc_lock)) {
+			printk(KERN_ERR "applesmc: Could not set the backlight,"
+							" mutex is locked.\n");
+			return;
+		}
+	} else {
+		mutex_lock(&applesmc_lock);
+	}
+
 	buffer[0] = value;
 	buffer[1] = 0x00;
 	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);



^ permalink raw reply related	[flat|nested] 47+ messages in thread

* Re: [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight
  2007-04-14  8:05       ` [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight Nicolas Boichat
@ 2007-04-14  8:45         ` Richard Purdie
  2007-04-14 13:31           ` [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight - use a workqueue Nicolas Boichat
  0 siblings, 1 reply; 47+ messages in thread
From: Richard Purdie @ 2007-04-14  8:45 UTC (permalink / raw)
  To: Nicolas Boichat
  Cc: Andrew Morton, Bradley Hook, mactel-linux-devel, linux-kernel

Hi,

On Sat, 2007-04-14 at 16:05 +0800, Nicolas Boichat wrote:
> Bradley Hook wrote:
> > Slightly off-topic, but I've been experiencing a minor bug in the
> > keyboard backlight feature.
> >
> > I say it is "minor" only because the feature serves no real functional
> > purpose. You can activate a trigger called "heartbeat" that will cause
> > the keyboard light to pulse at a speed based on the CPU usage. On my
> > MBP17, after activating this trigger the machine will either lock-up
> > or core dump within about a minute (timing is not consistent).
> >   
> 
> This is caused by the fact applesmc_backlight_set locks a mutex (or more
> precisely sleeps while trying to lock a mutex) while being in a softirq
> context.
> 
> This might be obvious for others, but it was not for me, and there is
> absolutely no mention in the documentation of the fact it is not always
> safe to sleep in the brightness_set handler of a led_class device (it is
> safe when it is called because someone wrote to the brightness sysfs file).

Its never safe for a brightness_set handler to sleep. They're designed
to be called from interrupt context and as you've noted, several
triggers do that.

The solution if you have locks like your case is to offload the work to
a workqueue, there is simply no other way to do it.

I'll have a look to see if I can improve the documentation (patches
welcome).

> Also, the led-trigger code seems buggy when it comes to locking. Setting
> CONFIG_DEBUG_SPINLOCK_SLEEP causes a lot a warnings. The problem is that
> the list of triggers is locked using a rw spinlock, but the rest of the
> code seems to ignore that, and calls a lot of functions which can sleep
> (kzalloc with GFP_KERNEL, sysfs_add_file, mutex_lock, etc...). I think
> the list lock should be converted to a mutex (or maybe modified to use
> RCU). I'm not very experienced in that domain, but if you want I can
> provide a patch for this.

Someone else has mentioned this within the past few days and there is a
problem with the trigger->activate and ->deactivate calls occurring
within a spinlock. Its not a simple problem to solve unfortunately and
you can't just convert to a mutex but I'm looking at it. Again, patches
welcome but its going to need careful thought.

Cheers,

Richard



^ permalink raw reply	[flat|nested] 47+ messages in thread

* [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight - use a workqueue
  2007-04-14  8:45         ` Richard Purdie
@ 2007-04-14 13:31           ` Nicolas Boichat
  0 siblings, 0 replies; 47+ messages in thread
From: Nicolas Boichat @ 2007-04-14 13:31 UTC (permalink / raw)
  To: Richard Purdie, Andrew Morton
  Cc: Bradley Hook, mactel-linux-devel, linux-kernel

Hi again,

Richard Purdie wrote:
> Hi,
>
> On Sat, 2007-04-14 at 16:05 +0800, Nicolas Boichat wrote:
>   
>> Bradley Hook wrote:
>>     
>>> Slightly off-topic, but I've been experiencing a minor bug in the
>>> keyboard backlight feature.
>>>
>>> I say it is "minor" only because the feature serves no real functional
>>> purpose. You can activate a trigger called "heartbeat" that will cause
>>> the keyboard light to pulse at a speed based on the CPU usage. On my
>>> MBP17, after activating this trigger the machine will either lock-up
>>> or core dump within about a minute (timing is not consistent).
>>>   
>>>       
>> This is caused by the fact applesmc_backlight_set locks a mutex (or more
>> precisely sleeps while trying to lock a mutex) while being in a softirq
>> context.
>>
>> This might be obvious for others, but it was not for me, and there is
>> absolutely no mention in the documentation of the fact it is not always
>> safe to sleep in the brightness_set handler of a led_class device (it is
>> safe when it is called because someone wrote to the brightness sysfs file).
>>     
>
> Its never safe for a brightness_set handler to sleep. They're designed
> to be called from interrupt context and as you've noted, several
> triggers do that.
>
> The solution if you have locks like your case is to offload the work to
> a workqueue, there is simply no other way to do it.
>   

Ok, I used a workqueue. Andrew, please ignore the previous patch
("[PATCH] applesmc - fix crash when activating a led trigger on the
keyboard backlight"), and use this one instead, thanks.


> I'll have a look to see if I can improve the documentation (patches
> welcome).
>
>   
>> Also, the led-trigger code seems buggy when it comes to locking. Setting
>> CONFIG_DEBUG_SPINLOCK_SLEEP causes a lot a warnings. The problem is that
>> the list of triggers is locked using a rw spinlock, but the rest of the
>> code seems to ignore that, and calls a lot of functions which can sleep
>> (kzalloc with GFP_KERNEL, sysfs_add_file, mutex_lock, etc...). I think
>> the list lock should be converted to a mutex (or maybe modified to use
>> RCU). I'm not very experienced in that domain, but if you want I can
>> provide a patch for this.
>>     
>
> Someone else has mentioned this within the past few days and there is a
> problem with the trigger->activate and ->deactivate calls occurring
> within a spinlock. Its not a simple problem to solve unfortunately and
> you can't just convert to a mutex but I'm looking at it. Again, patches
> welcome but its going to need careful thought.
>   

Ok you know your code better so maybe it's easier and better if you do
it. I'll tell you if I suddenly decide to work on that, so we don't
duplicate our work.

Best regards,

Nicolas

- Cannot sleep in led->brightness_set handler, as it might be called from a
softirq, so we use a workqueue to change the brightness (as recommended by
Richard Purdie)
- Reduce wait_status timetout from 100ms to 2ms, as wait_status either takes less
than 1.5 ms, or fails.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
---

 drivers/hwmon/applesmc.c |   61 +++++++++++++++++++++++++++++++++++++---------
 1 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 4ec38ef..ea0a004 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -38,6 +38,7 @@
 #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
@@ -116,7 +117,7 @@ struct dmi_match_data {
 	int temperature_set;
 };
 
-static int debug = 0;
+static const int debug = 0;
 static struct platform_device *pdev;
 static s16 rest_x;
 static s16 rest_y;
@@ -141,8 +142,10 @@ static struct mutex applesmc_lock;
  */
 static unsigned int key_at_index;
 
+static struct workqueue_struct *applesmc_led_wq;
+
 /*
- * __wait_status - Wait up to 100ms for the status port to get a certain value
+ * __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.
  */
@@ -152,9 +155,14 @@ static int __wait_status(u8 val)
 
 	val = val & APPLESMC_STATUS_MASK;
 
-	for (i = 0; i < 10000; i++) {
-		if ((inb(APPLESMC_CMD_PORT) & APPLESMC_STATUS_MASK) == val)
+	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);
 	}
 
@@ -721,17 +729,33 @@ static ssize_t applesmc_calibrate_store(struct device *dev,
 	return count;
 }
 
-static void applesmc_backlight_set(struct led_classdev *led_cdev,
-						enum led_brightness value)
+/* Store the next backlight value to be written by the work */
+static unsigned int backlight_value;
+
+static void applesmc_backlight_set(struct work_struct *work)
 {
 	u8 buffer[2];
 	
 	mutex_lock(&applesmc_lock);
-	buffer[0] = value;
+	buffer[0] = backlight_value;
 	buffer[1] = 0x00;
 	applesmc_write_key(BACKLIGHT_KEY, buffer, 2);
 	mutex_unlock(&applesmc_lock);
 }
+DECLARE_WORK(backlight_work, &applesmc_backlight_set);
+
+static void applesmc_brightness_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	int ret;
+
+	backlight_value = value;
+	ret = queue_work(applesmc_led_wq, &backlight_work);
+
+	if (debug && (!ret)) {
+		printk(KERN_DEBUG "applesmc: work was already on the queue.\n");
+	}
+}
 
 static ssize_t applesmc_key_count_show(struct device *dev,
 				struct device_attribute *attr, char *sysfsbuf)
@@ -887,7 +911,7 @@ static ssize_t applesmc_key_at_index_store(struct device *dev,
 static struct led_classdev applesmc_backlight = {
 	.name			= "smc:kbd_backlight",
 	.default_trigger	= "nand-disk",
-	.brightness_set		= applesmc_backlight_set,
+	.brightness_set		= applesmc_brightness_set,
 };
 
 static DEVICE_ATTR(position, 0444, applesmc_position_show, NULL);
@@ -1234,25 +1258,35 @@ static int __init applesmc_init(void)
 		if (ret)
 			goto out_accelerometer;
 
+		/* Create the workqueue */
+		applesmc_led_wq = create_singlethread_workqueue("applesmc-led");
+		if (!applesmc_led_wq) {
+			ret = -ENOMEM;
+			goto out_light_sysfs;
+		}
+
 		/* register as a led device */
 		ret = led_classdev_register(&pdev->dev, &applesmc_backlight);
 		if (ret < 0)
-			goto out_light_sysfs;
+			goto out_light_wq;
 	}
 
 	hwmon_class_dev = hwmon_device_register(&pdev->dev);
 	if (IS_ERR(hwmon_class_dev)) {
 		ret = PTR_ERR(hwmon_class_dev);
-		goto out_light;
+		goto out_light_ledclass;
 	}
 
 	printk(KERN_INFO "applesmc: driver successfully loaded.\n");
 
 	return 0;
 
-out_light:
+out_light_ledclass:
 	if (applesmc_light)
 		led_classdev_unregister(&applesmc_backlight);
+out_light_wq:
+	if (applesmc_light)
+		destroy_workqueue(applesmc_led_wq);
 out_light_sysfs:
 	if (applesmc_light)
 		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
@@ -1280,8 +1314,11 @@ out:
 static void __exit applesmc_exit(void)
 {
 	hwmon_device_unregister(hwmon_class_dev);	
-	if (applesmc_light)
+	if (applesmc_light) {
 		led_classdev_unregister(&applesmc_backlight);
+		destroy_workqueue(applesmc_led_wq);
+		sysfs_remove_file(&pdev->dev.kobj, &dev_attr_light.attr);
+	}
 	if (applesmc_accelerometer)
 		applesmc_release_accelerometer();
 	sysfs_remove_group(&pdev->dev.kobj, &temperature_attributes_group);



^ permalink raw reply related	[flat|nested] 47+ messages in thread

end of thread, other threads:[~2007-04-14 13:39 UTC | newest]

Thread overview: 47+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-14  9:29 [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-03-14  9:29 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
2007-03-14 11:11 ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Cong WANG
2007-03-14 14:00   ` Cong WANG
2007-03-15 11:31     ` Nicolas Boichat
2007-03-19  5:19 ` [PATCH] " Nicolas Boichat
2007-03-19  5:19   ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
2007-03-19  6:54   ` [PATCH] Apple SMC driver (hardware monitoring and control) Andrew Morton
2007-03-19  6:54     ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Andrew Morton
2007-03-19  7:35     ` [PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-03-19  7:35       ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
2007-03-20  7:12     ` [PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-03-20  7:12       ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Nicolas Boichat
2007-03-22 15:37   ` [PATCH] Apple SMC driver (hardware monitoring and control) Dmitry Torokhov
2007-03-22 15:37     ` [lm-sensors] [PATCH] Apple SMC driver (hardware monitoring and Dmitry Torokhov
2007-04-09 13:53     ` [PATCH] Apple SMC driver - fix input device Nicolas Boichat
2007-04-09 13:53       ` [lm-sensors] " Nicolas Boichat
2007-04-09 15:17       ` Dmitry Torokhov
2007-04-09 15:17         ` [lm-sensors] " Dmitry Torokhov
2007-04-09 20:04       ` Andrew Morton
2007-04-09 20:04         ` [lm-sensors] " Andrew Morton
2007-04-09 20:11         ` Dmitry Torokhov
2007-04-09 20:11           ` [lm-sensors] " Dmitry Torokhov
2007-04-09 21:51         ` Paul Mackerras
2007-04-09 21:51           ` [lm-sensors] " Paul Mackerras
2007-03-19 21:43 ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Bob Copeland
2007-03-19 21:43   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Bob Copeland
2007-03-20  7:02   ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-03-20 15:14     ` Bob Copeland
2007-03-21  4:03     ` Bob Copeland
     [not found]     ` <eb4a44160703200016i74786682n41f87f3d88f90409@mail.gmail.com>
2007-04-14  8:05       ` [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight Nicolas Boichat
2007-04-14  8:45         ` Richard Purdie
2007-04-14 13:31           ` [PATCH] applesmc - fix crash when activating a led trigger on the keyboard backlight - use a workqueue Nicolas Boichat
2007-03-20 10:08   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Jean Delvare
2007-03-20 10:08     ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Jean Delvare
2007-03-22 10:36     ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-03-22 10:36       ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Nicolas Boichat
2007-03-20 16:12 ` [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Gerb Stralko
2007-03-20 16:12   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Gerb Stralko
2007-04-11 12:25 ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Jean Delvare
2007-04-11 12:25   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Jean Delvare
2007-04-11 12:47   ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring and control) Nicolas Boichat
2007-04-11 12:47     ` [lm-sensors] [RFC][PATCH] Apple SMC driver (hardware monitoring Nicolas Boichat
2007-04-13  5:33   ` [PATCH 1/2] Apple SMC driver - standardize and sanitize sysfs tree + minor features addition Nicolas Boichat
2007-04-13  5:33     ` [lm-sensors] [PATCH 1/2] Apple SMC driver - standardize and Nicolas Boichat
2007-04-13  6:38     ` [PATCH 2/2] Apple SMC driver - implement key enumeration Nicolas Boichat
2007-04-13  6:38       ` [lm-sensors] [PATCH 2/2] Apple SMC driver - implement key Nicolas Boichat

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.