Apple Motion Sensor driver
RFC 1/1 Apple Motion Sensor driver
Hello
Below you find the latest revision of my AMS driver. AMS stands for Apple Motion Sensor and it's included in the 2005 revisions of Apple iBooks and PowerBooks. The driver implements the PMU and I2C variants. MacBooks have another variant, which is not handled by this driver, mainly because it's totally different and I don't have access to a MacBook.
Some of the code is based on the I2C ams driver from Stelian Pop, whom I'd like to thank for his work.
HD parking is marked BROKEN as of now, because it'll need changes to the block, IDE and SCSI layers. The infrastructure works, tough (tested with my own HD parking code). That's why I left it in there.
I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.
Thanks for your feedback,
Michael
---
drivers/hwmon/ams-core.c +264 -0
@@ -0,0 +1,264 @@ +/* +* Apple Motion Sensor driver +* +* Copyright (C) 2005 Stelian Pop (stelian at popies.net) +* Copyright (C) 2006 Michael Hanselmann (linux-kernel at hansmi.ch) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <asm/pmac_pfunc.h> + +#include "ams.h" + +/* There is only one motion sensor per machine */ +struct ams ams; + +static unsigned int verbose; +module_param (verbose, bool,0 ); +MODULE_PARM_DESC (verbose,"Show free falls and shocks in kernel output" ); + +/* Call with lock held! */ +void ams_sensors (s8 *x,s8 *y,s8 *z) +{ +s8 tmpx, tmpy, tmpz; +u32 orient; + + orient = ams.vflag? ams.orient1 : ams.orient2; + +if (orient &0x80 ) +/* X and Y swapped */ + ams.get_xyz (&tmpy, &tmpx, &tmpz); +else + ams.get_xyz (&tmpx, &tmpy, &tmpz); + +if (orient &0x04 ) + tmpz = ~tmpz; +if (orient &0x02 ) + tmpy = ~tmpy; +if (orient &0x01 ) + tmpx = ~tmpx; + +if (x) + *x = tmpx; +if (y) + *y = tmpy; +if (z) + *z = tmpz; +} + +static ssize_t ams_show_xyz (struct device *dev, +struct device_attribute *attr,char *buf) +{ +s8 x, y, z; + +mutex_lock (&ams.lock); +ams_sensors (&x, &y, &z); +mutex_unlock (&ams.lock); + +return snprintf (buf,PAGE_SIZE ,"%d %d %d\n" , x, y, z); +} + +static DEVICE_ATTR(xyz, S_IRUGO, ams_show_xyz, NULL); + +static void ams_handle_irq (void *data) +{ +enum ams_irq irq = *((enum ams_irq *)data); +unsigned long flags; + +spin_lock_irqsave (&ams.irq_lock, flags); + + ams.worker_irqs |= irq; +schedule_work (&ams.worker); + +spin_unlock_irqrestore (&ams.irq_lock, flags); +} + +static enum ams_irq ams_freefall_irq_data =AMS_IRQ_FREEFALL ; +static struct pmf_irq_client ams_freefall_client = { + .owner =THIS_MODULE , + .handler = ams_handle_irq, + .data = &ams_freefall_irq_data, +}; + +static enum ams_irq ams_shock_irq_data =AMS_IRQ_SHOCK ; +static struct pmf_irq_client ams_shock_client = { + .owner =THIS_MODULE , + .handler = ams_handle_irq, + .data = &ams_shock_irq_data, +}; + +static void ams_worker (void *data) +{ +mutex_lock (&ams.lock); + +if (ams.has_device) { +int do_park =0 ; + +if (ams.worker_irqs &AMS_IRQ_FREEFALL ) { +if (verbose) +printk (KERN_INFO "ams: freefall detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_FREEFALL ; + ams.clear_irq (AMS_IRQ_FREEFALL ); + + do_park =1 ; + } + +if (ams.worker_irqs &AMS_IRQ_SHOCK ) { +if (verbose) +printk (KERN_INFO "ams: shock detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_SHOCK ; + ams.clear_irq (AMS_IRQ_SHOCK ); + + do_park =1 ; + } + +if (do_park) +ams_hdpark (); + } + +mutex_unlock (&ams.lock); +} + +/* Call with lock held! */ +int ams_sensor_attach (void ) +{ +int result; +u32 *prop; + +/* Get orientation */ + prop = (u32 *)get_property (ams.of_node,"orientation" ,NULL ); +if (!prop) +return -ENODEV ; + ams.orient1 = *prop; + ams.orient2 = *(prop +1 ); + +/* Register freefall interrupt handler */ + result =pmf_register_irq_client (ams.of_node, +"accel-int-1" , + &ams_freefall_client); +if (result <0 ) +return -ENODEV ; + +/* Reset saved irqs */ + ams.worker_irqs =0 ; + +/* Register shock interrupt handler */ + result =pmf_register_irq_client (ams.of_node, +"accel-int-2" , + &ams_shock_client); +if (result <0 ) { +pmf_unregister_irq_client (&ams_freefall_client); +return -ENODEV ; + } + +/* Create device */ + ams.of_dev =of_platform_device_create (ams.of_node,"ams" ,NULL ); +if (!ams.of_dev) { +pmf_unregister_irq_client (&ams_shock_client); +pmf_unregister_irq_client (&ams_freefall_client); +return -ENODEV ; + } + +/* Create attributes */ +device_create_file (&ams.of_dev->dev, &dev_attr_xyz); + + ams.vflag = !!(ams.get_vendor () &0x10 ); + +/* Init hdpark stuff */ +ams_hdpark_init (); + +/* Init mouse device */ +ams_mouse_init (); + +return 0 ; +} + +int __init ams_init (void ) +{ +struct device_node *np; + +spin_lock_init (&ams.irq_lock); +mutex_init (&ams.lock); +INIT_WORK (&ams.worker, ams_worker,NULL ); + +#ifdef CONFIG_SENSORS_AMS_I2C + np =of_find_node_by_name (NULL ,"accelerometer" ); +if (np &&device_is_compatible (np,"AAPL,accelerometer_1" )) +/* Found I2C motion sensor */ +return ams_i2c_init (np); +#endif + +#ifdef CONFIG_SENSORS_AMS_PMU + np =of_find_node_by_name (NULL ,"sms" ); +if (np &&device_is_compatible (np,"sms" )) +/* Found PMU motion sensor */ +return ams_pmu_init (np); +#endif + +printk (KERN_ERR "ams: No motion sensor found.\n" ); + +return -ENODEV ; +} + +void ams_exit (void ) +{ +mutex_lock (&ams.lock); + +if (ams.has_device) { +/* Remove mouse device */ +ams_mouse_exit (); + +/* Shut down implementation */ + ams.exit (); + +/* Cancel interrupt worker +* +* We do this after ams.exit(), because an interrupt might +* have arrived before disabling them. +*/ +cancel_delayed_work (&ams.worker); +flush_scheduled_work (); + +/* Disable hard disk parking */ +ams_hdpark_exit (); + +/* Remove attributes */ +device_remove_file (&ams.of_dev->dev, &dev_attr_xyz); + +/* Remove device */ +of_device_unregister (ams.of_dev); + +/* Remove handler */ +pmf_unregister_irq_client (&ams_shock_client); +pmf_unregister_irq_client (&ams_freefall_client); + } + +mutex_unlock (&ams.lock); +} + +MODULE_AUTHOR ("Stelian Pop, Michael Hanselmann" ); +MODULE_DESCRIPTION ("Apple Motion Sensor driver" ); +MODULE_LICENSE ("GPL" ); + +module_init (ams_init); +module_exit (ams_exit);
drivers/hwmon/ams.h +91 -0
91 lines not on this page · raw message
drivers/hwmon/ams-hdpark.c +72 -0
72 lines not on this page · raw message
drivers/hwmon/ams-i2c.c +284 -0
284 lines not on this page · raw message
drivers/hwmon/ams-mouse.c +146 -0
146 lines not on this page · raw message
drivers/hwmon/ams-pmu.c +202 -0
202 lines not on this page · raw message
drivers/hwmon/Kconfig +36 -0
@@ -507,6 +507,42 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_AMS + tristate "Motion sensor driver" + default y + help + Support for the motion sensor included in PowerBooks. + +config SENSORS_AMS_PMU + bool "PMU variant" + depends on SENSORS_AMS && ADB_PMU + default y + help + PMU variant of motion sensor, found in late 2005 PowerBooks. + +config SENSORS_AMS_I2C + bool "I2C variant" + depends on SENSORS_AMS && I2C + default y + help + I2C variant of motion sensor, found in early 2005 PowerBooks and + iBooks. + +config SENSORS_AMS_MOUSE + bool "Support for mouse mode with motion sensor" + depends on SENSORS_AMS && INPUT + help + Support for mouse emulation with motion sensor. + +config SENSORS_AMS_HDPARK + bool "Park hard disk heads on freefall or shock" + depends on SENSORS_AMS = y && BROKEN + default y + help + Park the internal hard disk's heads if a free fall or shock is + detected. This can help to prevent data loss in case of a + head crash, altough nobody can guarantee anything. + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" depends on HWMON
drivers/hwmon/Makefile +7 -0
@@ -49,6 +49,13 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o +ams-y := ams-core.o +ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o +ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o +ams-$(CONFIG_SENSORS_AMS_MOUSE) += ams-mouse.o +ams-$(CONFIG_SENSORS_AMS_HDPARK) += ams-hdpark.o +obj-$(CONFIG_SENSORS_AMS) += ams.o + ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG endif
Hi Michael,
drivers/hwmon/ams-core.c line 64
Quoted 8 lines · Michael Hanselmann
drivers/hwmon/ams-core.c +7
-0+ +if (x) + *x = tmpx; +if (y) + *y = tmpy; +if (z) + *z = tmpz;
All callers of ams_sensors use all 3 arguments, why bother with temps?
drivers/hwmon/ams-mouse.c line 65
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ + ams.idev->name ="Apple Motion Sensor" ; + ams.idev->id.bustype =BUS_I2C ;
Should it be always BUS_I2C? Maybe it should be BUS_HOST in case of PMU?
drivers/hwmon/ams-mouse.c line 66
Quoted 1 line
drivers/hwmon/ams-mouse.c +1
-0+ ams.idev->id.vendor =0 ;
Please add:
ams.idev->cdev.dev = &ams.of_dev->dev;
so that the input device has a proper parent in sysfs hierarchy.
Signature · 1 line
Dmitry
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…
Below you find the latest revision of my AMS driver.
I was about to merge the below, then this comes along. Now what?
From: Stelian Pop <stelian at popies.net>
This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks (the series the PowerBook5,6 falls into, later ones have a slightly different one the driver doesn't handle) are supported. The accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick.
In the future (once the proper API exist in the block layer) this driver will tell the laptop disk to protect its heads in the event of a catastrophic fall.
drivers/hwmon/Kconfig | 17 + drivers/hwmon/Makefile | 1 drivers/hwmon/ams.c | 544 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 562 insertions(+)
drivers/hwmon/ams.c new file +544 -0
@@ -0,0 +1,544 @@ +/* +* Apple Motion Sensor driver +* +* Copyright (C) 2005 Stelian Pop (stelian at popies.net) +* +* Clean room implementation based on the reverse engineered OSX driver by +* Johannes Berg <johannes at sipsolutions.net>, documentation available at +* http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/kthread.h> + +#include <asm/of_device.h> +#include <asm/pmac_feature.h> + +MODULE_DESCRIPTION ("Apple Motion Sensor driver" ); +MODULE_AUTHOR ("Stelian Pop" ); +MODULE_LICENSE ("GPL" ); + +static unsigned int mouse; +module_param (mouse, bool,0 ); +MODULE_PARM_DESC (mouse,"enable the input class device on module load" ); + +/* AMS registers */ +#define AMS_COMMAND 0x00/* command register */ +#define AMS_STATUS 0x01/* status register */ +#define AMS_CTRL1 0x02/* read control 1 (number of values) */ +#define AMS_CTRL2 0x03/* read control 2 (offset?) */ +#define AMS_CTRL3 0x04/* read control 3 (size of each value?) */ +#define AMS_DATA1 0x05/* read data 1 */ +#define AMS_DATA2 0x06/* read data 2 */ +#define AMS_DATA3 0x07/* read data 3 */ +#define AMS_DATA4 0x08/* read data 4 */ +#define AMS_DATAX 0x20/* data X */ +#define AMS_DATAY 0x21/* data Y */ +#define AMS_DATAZ 0x22/* data Z */ +#define AMS_FREEFALL 0x24/* freefall int control */ +#define AMS_SHOCK 0x25/* shock int control */ +#define AMS_SENSLOW 0x26/* sensitivity low limit */ +#define AMS_SENSHIGH 0x27/* sensitivity high limit */ +#define AMS_CTRLX 0x28/* control X */ +#define AMS_CTRLY 0x29/* control Y */ +#define AMS_CTRLZ 0x2A/* control Z */ +#define AMS_UNKNOWN1 0x2B/* unknown 1 */ +#define AMS_UNKNOWN2 0x2C/* unknown 2 */ +#define AMS_UNKNOWN3 0x2D/* unknown 3 */ +#define AMS_VENDOR 0x2E/* vendor */ + +/* AMS commands - use with the AMS_COMMAND register */ +enum ams_cmd { +AMS_CMD_NOOP =0 , +AMS_CMD_VERSION , +AMS_CMD_READMEM , +AMS_CMD_WRITEMEM , +AMS_CMD_ERASEMEM , +AMS_CMD_READEE , +AMS_CMD_WRITEEE , +AMS_CMD_RESET , +AMS_CMD_START , +}; + +struct ams { +char init;/* has it been initialized ? */ +int bus;/* i2c bus */ +int address;/* i2c address */ +struct i2c_client client;/* i2c client */ +struct of_device *of_dev;/* sysfs device */ +char vflag;/* use orient1 or orient2 ? */ +u32 orient1;/* orientation words */ +u32 orient2; +int irq1;/* first irq line */ +int irq2;/* second irq line */ +struct work_struct worker;/* worker thread */ +struct input_dev *idev;/* input device */ +int xcalib;/* calibrated null value for x */ +int ycalib;/* calibrated null value for y */ +int zcalib;/* calibrated null value for z */ +struct mutex input_mutex; +struct task_struct *kthread;/* kthread for input */ +}; + +static struct ams ams; + +static int ams_attach (struct i2c_adapter *adapter); +static int ams_detach (struct i2c_adapter *adapter); + +static struct i2c_driver ams_driver = { + .driver = { + .name ="ams" , + .owner =THIS_MODULE , + }, + .attach_adapter = ams_attach, + .detach_adapter = ams_detach, +}; + +static inline s32 ams_read (u8 reg) +{ +return i2c_smbus_read_byte_data (&ams.client, reg); +} + +static inline int ams_write (u8 reg,u8 value) +{ +return i2c_smbus_write_byte_data (&ams.client, reg, value); +} + +static int ams_cmd (enum ams_cmd cmd) +{ +s32 result; +int i; + +ams_write (AMS_COMMAND , cmd); +for (i =0 ; i <10 ; i++) { +mdelay (5 ); + result =ams_read (AMS_COMMAND ); +if (result =0 || result &0x80 ) +return 0 ; + } +return -1 ; +} + +static void ams_sensors (s8 *x,s8 *y,s8 *z) +{ +u32 orient; + + *x =ams_read (AMS_DATAX ); + *y =ams_read (AMS_DATAY ); + *z =ams_read (AMS_DATAZ ); + + orient = ams.vflag ? ams.orient2 : ams.orient1; +if (orient &0x80 ) { +s8 tmp = *x; + *x = *y; + *y = tmp; + } +if (orient &0x04 ) + *z = ~*z; +if (orient &0x02 ) + *y = ~*y; +if (orient &0x01 ) + *x = ~*x; + +/* printk(KERN_DEBUG "ams: Sensors (%d, %d, %d)\n", *x, *y, *z); */ +} + +static ssize_t ams_show_x (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , x); +return retval; +} +static DEVICE_ATTR(x, S_IRUGO, ams_show_x, NULL); + +static ssize_t ams_show_y (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , y); +return retval; +} +static DEVICE_ATTR(y, S_IRUGO, ams_show_y, NULL); + +static ssize_t ams_show_z (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , z); +return retval; +} + +static DEVICE_ATTR(z, S_IRUGO, ams_show_z, NULL); + +static int ams_mouse_kthread (void *data) +{ +s8 x, y, z; + +while (!kthread_should_stop ()) { +ams_sensors (&x, &y, &z); + +input_report_abs (ams.idev,ABS_X , x - ams.xcalib); +input_report_abs (ams.idev,ABS_Y , y - ams.ycalib); +input_report_abs (ams.idev,ABS_Z , z - ams.zcalib); + +input_sync (ams.idev); + +msleep (25 ); + } +return 0 ; +} + +static void ams_mouse_enable (void ) +{ +s8 x, y, z; + +mutex_lock (&ams.input_mutex); + +if (ams.idev) + goto out; + +ams_sensors (&x, &y, &z); + ams.xcalib = x; + ams.ycalib = y; + ams.zcalib = z; + + ams.idev =input_allocate_device (); +if (!ams.idev) + goto out; + + ams.idev->name ="Apple Motion Sensor" ; + ams.idev->id.bustype =BUS_I2C ; + ams.idev->id.vendor =0 ; + +input_set_abs_params (ams.idev,ABS_X ,-50 ,50 ,3 ,0 ); +input_set_abs_params (ams.idev,ABS_Y ,-50 ,50 ,3 ,0 ); +input_set_abs_params (ams.idev,ABS_Z ,-50 ,50 ,3 ,0 ); + +set_bit (EV_ABS , ams.idev->evbit); +set_bit (EV_KEY , ams.idev->evbit); +set_bit (BTN_TOUCH , ams.idev->keybit); + +if (input_register_device (ams.idev)) { +input_free_device (ams.idev); + ams.idev =NULL ; + goto out; + } + + ams.kthread =kthread_run (ams_mouse_kthread,NULL ,"kams" ); +if (IS_ERR (ams.kthread)) { +input_unregister_device (ams.idev); + ams.idev =NULL ; + goto out; + } +out: +mutex_unlock (&ams.input_mutex); +} + +static void ams_mouse_disable (void ) +{ +mutex_lock (&ams.input_mutex); + +if (!ams.idev) + goto out; + +kthread_stop (ams.kthread); + +input_unregister_device (ams.idev); + + ams.idev =NULL ; +out: +mutex_unlock (&ams.input_mutex); +} + +static ssize_t ams_show_mouse (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , mouse); +} + +static ssize_t ams_store_mouse (struct device *dev,struct device_attribute *attr, +const char *buf,size_t count) +{ +if (sscanf (buf,"%d\n" , &mouse) !=1 ) +return -EINVAL ; + + mouse = !!mouse; + +if (mouse) +ams_mouse_enable (); +else +ams_mouse_disable (); + +return count; +} +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, ams_show_mouse, ams_store_mouse); + +static void ams_worker (void *data) +{ +/* TODO: park hard disk drives like OSX ? */ +if (ams_read (AMS_FREEFALL ) &0x80 ) +printk (KERN_INFO "ams: freefall interrupt\n" ); +if (ams_read (AMS_SHOCK ) &0x80 ) +printk (KERN_INFO "ams: shock interrupt\n" ); + +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); +} + +static irqreturn_t ams_interrupt (int irq,void *devid,struct pt_regs *regs) +{ +if (ams.init) +schedule_work (&ams.worker); +return IRQ_HANDLED ; +} + +static int ams_attach (struct i2c_adapter *adapter) +{ +int vmaj, vmin; +unsigned long bus; + +if (ams.init) +return 0 ; + +if (strncmp (adapter->name,"uni-n" ,5 )) +return -ENODEV ; + bus =simple_strtoul (adapter->name +6 ,NULL ,10 ); +if (bus != ams.bus) +return -ENODEV ; + + ams.client.addr = ams.address; + ams.client.adapter = adapter; + ams.client.driver = &ams_driver; +strcpy (ams.client.name,"Apple Motion Sensor" ); + +if (ams_cmd (AMS_CMD_RESET )) { +printk (KERN_INFO "ams: Failed to reset the device\n" ); +return -ENODEV ; + } + +if (ams_cmd (AMS_CMD_START )) { +printk (KERN_INFO "ams: Failed to start the device\n" ); +return -ENODEV ; + } + +/* get version/vendor information */ +ams_write (AMS_CTRL1 ,0x02 ); +ams_write (AMS_CTRL2 ,0x85 ); +ams_write (AMS_CTRL3 ,0x01 ); + +ams_cmd (AMS_CMD_READMEM ); + + vmaj =ams_read (AMS_DATA1 ); + vmin =ams_read (AMS_DATA2 ); +if (vmaj !=1 || vmin !=52 ) { +printk (KERN_INFO "ams: Incorrect device version (%d.%d)\n" , + vmaj, vmin); +return -ENODEV ; + } + +ams_cmd (AMS_CMD_VERSION ); + + vmaj =ams_read (AMS_DATA1 ); + vmin =ams_read (AMS_DATA2 ); +if (vmaj !=0 || vmin !=1 ) { +printk (KERN_INFO "ams: Incorrect firmware version (%d.%d)\n" , + vmaj, vmin); +return -ENODEV ; + } + +if (ams_read (AMS_VENDOR ) &0x10 ) + ams.vflag =1 ; + +/* write initial values */ +ams_write (AMS_SENSLOW ,0x15 ); +ams_write (AMS_SENSHIGH ,0x60 ); +ams_write (AMS_CTRLX ,0x08 ); +ams_write (AMS_CTRLY ,0x0F ); +ams_write (AMS_CTRLZ ,0x4F ); +ams_write (AMS_UNKNOWN1 ,0x14 ); + +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); + +/* enable interrupts */ +ams_write (AMS_CTRLX ,0x88 ); +ams_write (AMS_CTRLY ,0x8F ); +ams_write (AMS_CTRLZ ,0xCF ); + +if (i2c_attach_client (&ams.client)) { +printk (KERN_INFO "ams: Failed to attach the client\n" ); +return -ENODEV ; + } + +if (mouse) +ams_mouse_enable (); + + ams.init =1 ; + +printk (KERN_INFO "ams: Apple Motion Sensor enabled\n" ); + +return 0 ; +} + +static int ams_detach (struct i2c_adapter *adapter) +{ +if (!ams.init) +return 0 ; + +if (ams.idev) +ams_mouse_disable (); + +i2c_detach_client (&ams.client); + +/* disable and ack interrupts */ +ams_write (AMS_CTRLX ,0x08 ); +ams_write (AMS_CTRLY ,0x0F ); +ams_write (AMS_CTRLZ ,0x4F ); +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); + + ams.init =0 ; + +printk (KERN_INFO "ams: Apple Motion Sensor disabled\n" ); + +return 0 ; +} + +static int __init ams_init (void ) +{ +struct device_node * np; +u32 *prop; +int retval = -ENODEV ; + + np =of_find_node_by_name (NULL ,"accelerometer" ); +if (!np) + goto out; +if (!device_is_compatible (np,"AAPL,accelerometer_1" )) + goto out; + + prop = (u32 *)get_property (np,"orientation" ,NULL ); +if (!prop) { + retval = -EIO ; + goto out; + } + ams.orient1 = *prop; + ams.orient2 = *(prop +1 ); + + prop = (u32 *)get_property (np,"reg" ,NULL ); +if (!prop) + goto out; + +/* look for bus either by path or using "reg" */ +if (strstr (np->full_name,"/i2c-bus@" ) !=NULL ) { +const char *tmp_bus = (strstr (np->full_name,"/i2c-bus@" ) +9 ); + ams.bus = tmp_bus[0 ]-'0' ; + }else { + ams.bus = ((*prop) >>8 ) &0x0f ; + } + ams.address = ((*prop) &0xff ) >>1 ; + + np =of_find_node_by_name (NULL ,"accelerometer-1" ); +if (!np || np->n_intrs <1 ) + goto out; + + ams.irq1 = np->intrs[0 ].line; + + np =of_find_node_by_name (NULL ,"accelerometer-2" ); +if (!np || np->n_intrs <1 ) + goto out; + + ams.irq2 = np->intrs[0 ].line; + +if (request_irq (ams.irq1, ams_interrupt,0 ,"accelerometer-1" , +NULL <0 )) + goto out; + +if (request_irq (ams.irq2, ams_interrupt,0 ,"accelerometer-2" , +NULL <0 )) + goto free_irq1; + +INIT_WORK (&ams.worker, ams_worker,NULL ); + +if ((ams.of_dev =of_platform_device_create (np,"ams" ,NULL )) =NULL ) + goto free_irq2; + +mutex_init (&ams.input_mutex); + +device_create_file (&ams.of_dev->dev, &dev_attr_x); +device_create_file (&ams.of_dev->dev, &dev_attr_y); +device_create_file (&ams.of_dev->dev, &dev_attr_z); +device_create_file (&ams.of_dev->dev, &dev_attr_mouse); + +if (i2c_add_driver (&ams_driver) <0 ) + goto free_device_files; + +return 0 ; + +free_device_files: +device_remove_file (&ams.of_dev->dev, &dev_attr_x); +device_remove_file (&ams.of_dev->dev, &dev_attr_y); +device_remove_file (&ams.of_dev->dev, &dev_attr_z); +device_remove_file (&ams.of_dev->dev, &dev_attr_mouse); +mutex_destroy (&ams.input_mutex); +free_irq2: +free_irq (ams.irq2,NULL ); +free_irq1: +free_irq (ams.irq1,NULL ); +out: +return retval; +} + +static void __exit ams_exit(void) +{ +i2c_del_driver (&ams_driver); + +free_irq (ams.irq1,NULL ); +free_irq (ams.irq2,NULL ); + +device_remove_file (&ams.of_dev->dev, &dev_attr_x); +device_remove_file (&ams.of_dev->dev, &dev_attr_y); +device_remove_file (&ams.of_dev->dev, &dev_attr_z); +device_remove_file (&ams.of_dev->dev, &dev_attr_mouse); + +mutex_destroy (&ams.input_mutex); + +of_device_unregister (ams.of_dev); +} + +module_init (ams_init); +module_exit (ams_exit);
drivers/hwmon/Kconfig renamed from drivers/hwmon/Kconfig~apple-motion-sensor-driver +17 -0
@@ -507,6 +507,23 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_AMS + tristate "Apple Motion Sensor (ams)" + depends on HWMON && INPUT && PPC32 + default n + help + This driver provides support for the Apple Motion Sensor (ams), + which provides an accelerometer and other misc. data. + Some Apple PowerBooks (the series the PowerBook5,6 falls into, + later ones have a slightly different one the driver doesn't handle) + are supported. The accelerometer data is readable 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 ams. + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" depends on HWMON
drivers/hwmon/Makefile renamed from drivers/hwmon/Makefile~apple-motion-sensor-driver +1 -0
@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025 obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_AMS) += ams.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
_
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…
Below you find the latest revision of my AMS driver.
I was about to merge the below, then this comes along. Now what?
Michael's driver is a new version that adds support for non-i2c (PMU based) chips found on new machines. It would be nice to have it, though it definitely needs a bit of review from Stelian, the original author.
What I would suggest at this point is to merge Stelian's original driver, as you were about todo, then have Michael produce a patch against it to be reviewed that could then be merged later on. I'm fine having that "later on" still be in the 2.6.18 timeframe since we aren't talking about anything critical here, just a fairly minor driver update that is nice to have, but we can decide that later based on the reviews of Michael's changes).
Cheers,
Ben.
Quoted 616 lines
From: Stelian Pop <stelian at popies.net>
This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks (the series the PowerBook5,6 falls into, later ones have a slightly different one the driver doesn't handle) are supported. The accelerometer data is readable via sysfs.
This driver also provides an absolute input class device, allowing the laptop to act as a pinball machine-esque joystick.
In the future (once the proper API exist in the block layer) this driver will tell the laptop disk to protect its heads in the event of a catastrophic fall.
Signed-off-by Stelian Pop <stelian at popies.net>Signed-off-by Johannes Berg <johannes at sipsolutions.net>Cc Benjamin Herrenschmidt <benh at kernel.crashing.org>Cc Paul Mackerras <paulus at samba.org>Cc Dmitry Torokhov <dtor_core at ameritech.net>Cc Robert Love <rml at novell.com>Cc Jean Delvare <khali at linux-fr.org>drivers/hwmon/Kconfig | 17 + drivers/hwmon/Makefile | 1 drivers/hwmon/ams.c | 544 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 562 insertions(+)diff -puN
/dev/nulldrivers/hwmon/ams.cdrivers/hwmon/ams.c new file +544
-0@@ -0,0 +1,544 @@ +/* +* Apple Motion Sensor driver +* +* Copyright (C) 2005 Stelian Pop (stelian at popies.net) +* +* Clean room implementation based on the reverse engineered OSX driver by +* Johannes Berg <johannes at sipsolutions.net>, documentation available at +* http://johannes.sipsolutions.net/PowerBook/Apple_Motion_Sensor_Specification +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/kthread.h> + +#include <asm/of_device.h> +#include <asm/pmac_feature.h> + +MODULE_DESCRIPTION ("Apple Motion Sensor driver" ); +MODULE_AUTHOR ("Stelian Pop" ); +MODULE_LICENSE ("GPL" ); + +static unsigned int mouse; +module_param (mouse, bool,0 ); +MODULE_PARM_DESC (mouse,"enable the input class device on module load" ); + +/* AMS registers */ +#define AMS_COMMAND 0x00/* command register */ +#define AMS_STATUS 0x01/* status register */ +#define AMS_CTRL1 0x02/* read control 1 (number of values) */ +#define AMS_CTRL2 0x03/* read control 2 (offset?) */ +#define AMS_CTRL3 0x04/* read control 3 (size of each value?) */ +#define AMS_DATA1 0x05/* read data 1 */ +#define AMS_DATA2 0x06/* read data 2 */ +#define AMS_DATA3 0x07/* read data 3 */ +#define AMS_DATA4 0x08/* read data 4 */ +#define AMS_DATAX 0x20/* data X */ +#define AMS_DATAY 0x21/* data Y */ +#define AMS_DATAZ 0x22/* data Z */ +#define AMS_FREEFALL 0x24/* freefall int control */ +#define AMS_SHOCK 0x25/* shock int control */ +#define AMS_SENSLOW 0x26/* sensitivity low limit */ +#define AMS_SENSHIGH 0x27/* sensitivity high limit */ +#define AMS_CTRLX 0x28/* control X */ +#define AMS_CTRLY 0x29/* control Y */ +#define AMS_CTRLZ 0x2A/* control Z */ +#define AMS_UNKNOWN1 0x2B/* unknown 1 */ +#define AMS_UNKNOWN2 0x2C/* unknown 2 */ +#define AMS_UNKNOWN3 0x2D/* unknown 3 */ +#define AMS_VENDOR 0x2E/* vendor */ + +/* AMS commands - use with the AMS_COMMAND register */ +enum ams_cmd { +AMS_CMD_NOOP =0 , +AMS_CMD_VERSION , +AMS_CMD_READMEM , +AMS_CMD_WRITEMEM , +AMS_CMD_ERASEMEM , +AMS_CMD_READEE , +AMS_CMD_WRITEEE , +AMS_CMD_RESET , +AMS_CMD_START , +}; + +struct ams { +char init;/* has it been initialized ? */ +int bus;/* i2c bus */ +int address;/* i2c address */ +struct i2c_client client;/* i2c client */ +struct of_device *of_dev;/* sysfs device */ +char vflag;/* use orient1 or orient2 ? */ +u32 orient1;/* orientation words */ +u32 orient2; +int irq1;/* first irq line */ +int irq2;/* second irq line */ +struct work_struct worker;/* worker thread */ +struct input_dev *idev;/* input device */ +int xcalib;/* calibrated null value for x */ +int ycalib;/* calibrated null value for y */ +int zcalib;/* calibrated null value for z */ +struct mutex input_mutex; +struct task_struct *kthread;/* kthread for input */ +}; + +static struct ams ams; + +static int ams_attach (struct i2c_adapter *adapter); +static int ams_detach (struct i2c_adapter *adapter); + +static struct i2c_driver ams_driver = { + .driver = { + .name ="ams" , + .owner =THIS_MODULE , + }, + .attach_adapter = ams_attach, + .detach_adapter = ams_detach, +}; + +static inline s32 ams_read (u8 reg) +{ +return i2c_smbus_read_byte_data (&ams.client, reg); +} + +static inline int ams_write (u8 reg,u8 value) +{ +return i2c_smbus_write_byte_data (&ams.client, reg, value); +} + +static int ams_cmd (enum ams_cmd cmd) +{ +s32 result; +int i; + +ams_write (AMS_COMMAND , cmd); +for (i =0 ; i <10 ; i++) { +mdelay (5 ); + result =ams_read (AMS_COMMAND ); +if (result =0 || result &0x80 ) +return 0 ; + } +return -1 ; +} + +static void ams_sensors (s8 *x,s8 *y,s8 *z) +{ +u32 orient; + + *x =ams_read (AMS_DATAX ); + *y =ams_read (AMS_DATAY ); + *z =ams_read (AMS_DATAZ ); + + orient = ams.vflag ? ams.orient2 : ams.orient1; +if (orient &0x80 ) { +s8 tmp = *x; + *x = *y; + *y = tmp; + } +if (orient &0x04 ) + *z = ~*z; +if (orient &0x02 ) + *y = ~*y; +if (orient &0x01 ) + *x = ~*x; + +/* printk(KERN_DEBUG "ams: Sensors (%d, %d, %d)\n", *x, *y, *z); */ +} + +static ssize_t ams_show_x (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , x); +return retval; +} +static DEVICE_ATTR(x, S_IRUGO, ams_show_x, NULL); + +static ssize_t ams_show_y (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , y); +return retval; +} +static DEVICE_ATTR(y, S_IRUGO, ams_show_y, NULL); + +static ssize_t ams_show_z (struct device *dev,struct device_attribute *attr, +char *buf) +{ +int retval; +s8 x, y, z; + +ams_sensors (&x, &y, &z); + retval =snprintf (buf,PAGE_SIZE ,"%d\n" , z); +return retval; +} + +static DEVICE_ATTR(z, S_IRUGO, ams_show_z, NULL); + +static int ams_mouse_kthread (void *data) +{ +s8 x, y, z; + +while (!kthread_should_stop ()) { +ams_sensors (&x, &y, &z); + +input_report_abs (ams.idev,ABS_X , x - ams.xcalib); +input_report_abs (ams.idev,ABS_Y , y - ams.ycalib); +input_report_abs (ams.idev,ABS_Z , z - ams.zcalib); + +input_sync (ams.idev); + +msleep (25 ); + } +return 0 ; +} + +static void ams_mouse_enable (void ) +{ +s8 x, y, z; + +mutex_lock (&ams.input_mutex); + +if (ams.idev) + goto out; + +ams_sensors (&x, &y, &z); + ams.xcalib = x; + ams.ycalib = y; + ams.zcalib = z; + + ams.idev =input_allocate_device (); +if (!ams.idev) + goto out; + + ams.idev->name ="Apple Motion Sensor" ; + ams.idev->id.bustype =BUS_I2C ; + ams.idev->id.vendor =0 ; + +input_set_abs_params (ams.idev,ABS_X ,-50 ,50 ,3 ,0 ); +input_set_abs_params (ams.idev,ABS_Y ,-50 ,50 ,3 ,0 ); +input_set_abs_params (ams.idev,ABS_Z ,-50 ,50 ,3 ,0 ); + +set_bit (EV_ABS , ams.idev->evbit); +set_bit (EV_KEY , ams.idev->evbit); +set_bit (BTN_TOUCH , ams.idev->keybit); + +if (input_register_device (ams.idev)) { +input_free_device (ams.idev); + ams.idev =NULL ; + goto out; + } + + ams.kthread =kthread_run (ams_mouse_kthread,NULL ,"kams" ); +if (IS_ERR (ams.kthread)) { +input_unregister_device (ams.idev); + ams.idev =NULL ; + goto out; + } +out: +mutex_unlock (&ams.input_mutex); +} + +static void ams_mouse_disable (void ) +{ +mutex_lock (&ams.input_mutex); + +if (!ams.idev) + goto out; + +kthread_stop (ams.kthread); + +input_unregister_device (ams.idev); + + ams.idev =NULL ; +out: +mutex_unlock (&ams.input_mutex); +} + +static ssize_t ams_show_mouse (struct device *dev,struct device_attribute *attr, +char *buf) +{ +return sprintf (buf,"%d\n" , mouse); +} + +static ssize_t ams_store_mouse (struct device *dev,struct device_attribute *attr, +const char *buf,size_t count) +{ +if (sscanf (buf,"%d\n" , &mouse) !=1 ) +return -EINVAL ; + + mouse = !!mouse; + +if (mouse) +ams_mouse_enable (); +else +ams_mouse_disable (); + +return count; +} +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, ams_show_mouse, ams_store_mouse); + +static void ams_worker (void *data) +{ +/* TODO: park hard disk drives like OSX ? */ +if (ams_read (AMS_FREEFALL ) &0x80 ) +printk (KERN_INFO "ams: freefall interrupt\n" ); +if (ams_read (AMS_SHOCK ) &0x80 ) +printk (KERN_INFO "ams: shock interrupt\n" ); + +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); +} + +static irqreturn_t ams_interrupt (int irq,void *devid,struct pt_regs *regs) +{ +if (ams.init) +schedule_work (&ams.worker); +return IRQ_HANDLED ; +} + +static int ams_attach (struct i2c_adapter *adapter) +{ +int vmaj, vmin; +unsigned long bus; + +if (ams.init) +return 0 ; + +if (strncmp (adapter->name,"uni-n" ,5 )) +return -ENODEV ; + bus =simple_strtoul (adapter->name +6 ,NULL ,10 ); +if (bus != ams.bus) +return -ENODEV ; + + ams.client.addr = ams.address; + ams.client.adapter = adapter; + ams.client.driver = &ams_driver; +strcpy (ams.client.name,"Apple Motion Sensor" ); + +if (ams_cmd (AMS_CMD_RESET )) { +printk (KERN_INFO "ams: Failed to reset the device\n" ); +return -ENODEV ; + } + +if (ams_cmd (AMS_CMD_START )) { +printk (KERN_INFO "ams: Failed to start the device\n" ); +return -ENODEV ; + } + +/* get version/vendor information */ +ams_write (AMS_CTRL1 ,0x02 ); +ams_write (AMS_CTRL2 ,0x85 ); +ams_write (AMS_CTRL3 ,0x01 ); + +ams_cmd (AMS_CMD_READMEM ); + + vmaj =ams_read (AMS_DATA1 ); + vmin =ams_read (AMS_DATA2 ); +if (vmaj !=1 || vmin !=52 ) { +printk (KERN_INFO "ams: Incorrect device version (%d.%d)\n" , + vmaj, vmin); +return -ENODEV ; + } + +ams_cmd (AMS_CMD_VERSION ); + + vmaj =ams_read (AMS_DATA1 ); + vmin =ams_read (AMS_DATA2 ); +if (vmaj !=0 || vmin !=1 ) { +printk (KERN_INFO "ams: Incorrect firmware version (%d.%d)\n" , + vmaj, vmin); +return -ENODEV ; + } + +if (ams_read (AMS_VENDOR ) &0x10 ) + ams.vflag =1 ; + +/* write initial values */ +ams_write (AMS_SENSLOW ,0x15 ); +ams_write (AMS_SENSHIGH ,0x60 ); +ams_write (AMS_CTRLX ,0x08 ); +ams_write (AMS_CTRLY ,0x0F ); +ams_write (AMS_CTRLZ ,0x4F ); +ams_write (AMS_UNKNOWN1 ,0x14 ); + +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); + +/* enable interrupts */ +ams_write (AMS_CTRLX ,0x88 ); +ams_write (AMS_CTRLY ,0x8F ); +ams_write (AMS_CTRLZ ,0xCF ); + +if (i2c_attach_client (&ams.client)) { +printk (KERN_INFO "ams: Failed to attach the client\n" ); +return -ENODEV ; + } + +if (mouse) +ams_mouse_enable (); + + ams.init =1 ; + +printk (KERN_INFO "ams: Apple Motion Sensor enabled\n" ); + +return 0 ; +} + +static int ams_detach (struct i2c_adapter *adapter) +{ +if (!ams.init) +return 0 ; + +if (ams.idev) +ams_mouse_disable (); + +i2c_detach_client (&ams.client); + +/* disable and ack interrupts */ +ams_write (AMS_CTRLX ,0x08 ); +ams_write (AMS_CTRLY ,0x0F ); +ams_write (AMS_CTRLZ ,0x4F ); +ams_write (AMS_FREEFALL ,0 ); +ams_write (AMS_SHOCK ,0 ); + + ams.init =0 ; + +printk (KERN_INFO "ams: Apple Motion Sensor disabled\n" ); + +return 0 ; +} + +static int __init ams_init (void ) +{ +struct device_node * np; +u32 *prop; +int retval = -ENODEV ; + + np =of_find_node_by_name (NULL ,"accelerometer" ); +if (!np) + goto out; +if (!device_is_compatible (np,"AAPL,accelerometer_1" )) + goto out; + + prop = (u32 *)get_property (np,"orientation" ,NULL ); +if (!prop) { + retval = -EIO ; + goto out; + } + ams.orient1 = *prop; + ams.orient2 = *(prop +1 ); + + prop = (u32 *)get_property (np,"reg" ,NULL ); +if (!prop) + goto out; + +/* look for bus either by path or using "reg" */ +if (strstr (np->full_name,"/i2c-bus@" ) !=NULL ) { +const char *tmp_bus = (strstr (np->full_name,"/i2c-bus@" ) +9 ); + ams.bus = tmp_bus[0 ]-'0' ; + }else { + ams.bus = ((*prop) >>8 ) &0x0f ; + } + ams.address = ((*prop) &0xff ) >>1 ; + + np =of_find_node_by_name (NULL ,"accelerometer-1" ); +if (!np || np->n_intrs <1 ) + goto out; + + ams.irq1 = np->intrs[0 ].line; + + np =of_find_node_by_name (NULL ,"accelerometer-2" ); +if (!np || np->n_intrs <1 ) + goto out; + + ams.irq2 = np->intrs[0 ].line; + +if (request_irq (ams.irq1, ams_interrupt,0 ,"accelerometer-1" , +NULL <0 )) + goto out; + +if (request_irq (ams.irq2, ams_interrupt,0 ,"accelerometer-2" , +NULL <0 )) + goto free_irq1; + +INIT_WORK (&ams.worker, ams_worker,NULL ); + +if ((ams.of_dev =of_platform_device_create (np,"ams" ,NULL )) =NULL ) + goto free_irq2; + +mutex_init (&ams.input_mutex); + +device_create_file (&ams.of_dev->dev, &dev_attr_x); +device_create_file (&ams.of_dev->dev, &dev_attr_y); +device_create_file (&ams.of_dev->dev, &dev_attr_z); +device_create_file (&ams.of_dev->dev, &dev_attr_mouse); + +if (i2c_add_driver (&ams_driver) <0 ) + goto free_device_files; + +return 0 ; + +free_device_files: +device_remove_file (&ams.of_dev->dev, &dev_attr_x); +device_remove_file (&ams.of_dev->dev, &dev_attr_y); +device_remove_file (&ams.of_dev->dev, &dev_attr_z); +device_remove_file (&ams.of_dev->dev, &dev_attr_mouse); +mutex_destroy (&ams.input_mutex); +free_irq2: +free_irq (ams.irq2,NULL ); +free_irq1: +free_irq (ams.irq1,NULL ); +out: +return retval; +} + +static void __exit ams_exit(void) +{ +i2c_del_driver (&ams_driver); + +free_irq (ams.irq1,NULL ); +free_irq (ams.irq2,NULL ); + +device_remove_file (&ams.of_dev->dev, &dev_attr_x); +device_remove_file (&ams.of_dev->dev, &dev_attr_y); +device_remove_file (&ams.of_dev->dev, &dev_attr_z); +device_remove_file (&ams.of_dev->dev, &dev_attr_mouse); + +mutex_destroy (&ams.input_mutex); + +of_device_unregister (ams.of_dev); +} + +module_init (ams_init); +module_exit (ams_exit);diff -puN
drivers/hwmon/Kconfig~apple-motion-sensor-driverdrivers/hwmon/Kconfigdrivers/hwmon/Kconfig renamed from drivers/hwmon/Kconfig~apple-motion-sensor-driver +17
-0@@ -507,6 +507,23 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_AMS + tristate "Apple Motion Sensor (ams)" + depends on HWMON && INPUT && PPC32 + default n + help + This driver provides support for the Apple Motion Sensor (ams), + which provides an accelerometer and other misc. data. + Some Apple PowerBooks (the series the PowerBook5,6 falls into, + later ones have a slightly different one the driver doesn't handle) + are supported. The accelerometer data is readable 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 ams. + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" depends on HWMONdiff -puN
drivers/hwmon/Makefile~apple-motion-sensor-driverdrivers/hwmon/Makefiledrivers/hwmon/Makefile renamed from drivers/hwmon/Makefile~apple-motion-sensor-driver +1
-0@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025 obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_AMS) += ams.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o_
Quoted 12 lines · On Mon, 03 Jul 2006 13:47:28 +1000 Benjamin Herrenschmidt <benh at…
Quoted 6 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…
Below you find the latest revision of my AMS driver.
I was about to merge the below, then this comes along. Now what?
Michael's driver is a new version that adds support for non-i2c (PMU based) chips found on new machines. It would be nice to have it, though it definitely needs a bit of review from Stelian, the original author.
Quoted 7 lines
What I would suggest at this point is to merge Stelian's original driver, as you were about todo, then have Michael produce a patch against it to be reviewed that could then be merged later on. I'm fine having that "later on" still be in the 2.6.18 timeframe since we aren't talking about anything critical here, just a fairly minor driver update that is nice to have, but we can decide that later based on the reviews of Michael's changes).
Yes, but it wouldn't be a "patch against it" - Michael's patch adds multiple files with different names. So it would be a revert-and-redo.
Hello Andrew
Quoted 11 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…
Below you find the latest revision of my AMS driver.
I was about to merge the below, then this comes along. Now what?
From: Stelian Pop <stelian at popies.net>
This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks
I just noticed yesterday that Stelian sent a patch to lkml in May. My work is based on his separate driver from his website.
Given the fact that my driver includes all of his functionality and that replacing his with mine later in the process would mean to remove whole files again, I'd suggest to wait until I've fixed the outstanding issues (as seen in this thread) and then to merge mine.
Greets,
Michael
Le lundi 03 juillet 2006 ? 08:56 +0200, Michael Hanselmann a ?crit :
Quoted 15 lines
Hello Andrew
Quoted 11 lines · Andrew Morton
Quoted 1 line · On Mon, 3 Jul 2006 00:26:49 +0200 Michael Hanselmann <linux-kernel at…
Below you find the latest revision of my AMS driver.
I was about to merge the below, then this comes along. Now what?
From: Stelian Pop <stelian at popies.net>
This driver provides support for the Apple Motion Sensor (ams), which provides an accelerometer and other misc. data. Some Apple PowerBooks
Quoted 7 lines
I just noticed yesterday that Stelian sent a patch to lkml in May. My work is based on his separate driver from his website.
Given the fact that my driver includes all of his functionality and that replacing his with mine later in the process would mean to remove whole files again, I'd suggest to wait until I've fixed the outstanding issues (as seen in this thread) and then to merge mine.
I have no problems with this approach. I'll take a look at your patch ASAP.
Stelian.
Signature · 1 line
Stelian Pop <stelian at popies.net>
Hi Michael,
Quoted 7 lines
Below you find the latest revision of my AMS driver. AMS stands for Apple Motion Sensor and it's included in the 2005 revisions of Apple iBooks and PowerBooks. The driver implements the PMU and I2C variants. MacBooks have another variant, which is not handled by this driver, mainly because it's totally different and I don't have access to a MacBook.
Quoted 6 lines
Some of the code is based on the I2C ams driver from Stelian Pop, whom I'd like to thank for his work.
HD parking is marked BROKEN as of now, because it'll need changes to the block, IDE and SCSI layers. The infrastructure works, tough (tested with my own HD parking code). That's why I left it in there.
I'd rather leave it out for now, and merge it when it has a chance to work. Merging non-working code is confusing at best.
Quoted 3 lines
I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.
2.6.19 at best.
drivers/hwmon/Kconfig config SENSORS_HDAPS · line 512
Quoted 10 lines
diff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Kconfig linux-2.6.17-git20/drivers/hwmon/Kconfig
drivers/hwmon/Kconfig +3
-0@@ -507,6 +507,42 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_AMS + tristate "Motion sensor driver" + default y
No, not everyone has this device. We don't have a default for other hardware monitoring drivers.
This should depend on HWMON, and probably EXPERIMENTAL too, until it gets some wider testing.
Also please respect the alphabetical order.
Quoted 40 lines
drivers/hwmon/Kconfig +33
-0+ help + Support for the motion sensor included in PowerBooks. + +config SENSORS_AMS_PMU + bool "PMU variant" + depends on SENSORS_AMS && ADB_PMU + default y + help + PMU variant of motion sensor, found in late 2005 PowerBooks. + +config SENSORS_AMS_I2C + bool "I2C variant" + depends on SENSORS_AMS && I2C + default y + help + I2C variant of motion sensor, found in early 2005 PowerBooks and + iBooks. + +config SENSORS_AMS_MOUSE + bool "Support for mouse mode with motion sensor" + depends on SENSORS_AMS && INPUT + help + Support for mouse emulation with motion sensor. + +config SENSORS_AMS_HDPARK + bool "Park hard disk heads on freefall or shock" + depends on SENSORS_AMS = y && BROKEN + default y + help + Park the internal hard disk's heads if a free fall or shock is + detected. This can help to prevent data loss in case of a + head crash, altough nobody can guarantee anything. + config HWMON_DEBUG_CHIP bool "Hardware Monitoring Chip debugging messages" depends on HWMONdiff -Nrup --exclude-from linux-exclude-from linux-2.6.17-git20.orig/drivers/hwmon/Makefile linux-2.6.17-git20/drivers/hwmon/Makefile
drivers/hwmon/Makefile obj-$(CONFIG_SENSORS_VT8231) += vt8231.o · line 57
Quoted 9 lines
drivers/hwmon/Makefile +6
-0@@ -49,6 +49,13 @@ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o +ams-y := ams-core.o +ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o +ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o +ams-$(CONFIG_SENSORS_AMS_MOUSE) += ams-mouse.o +ams-$(CONFIG_SENSORS_AMS_HDPARK) += ams-hdpark.o +obj-$(CONFIG_SENSORS_AMS) += ams.o
If you are going to have many source files and a composite module, please create your own subdirectory under drivers/hwmon and put all your stuff here. The kernel build system is notoriously bad at handling multiple composite modules within the same subdirectory.
I won't have the time to review such a big and complex driver within several weeks, so people who want this driver in 2.6.19 will have to review it by themselves.
Thanks,
Signature · 1 line
Jean Delvare
Quoted 3 lines
I'd rather leave it out for now, and merge it when it has a chance to work. Merging non-working code is confusing at best.
:)
Quoted 5 lines
Quoted 3 lines
I want this driver to be included in -mm as soon as possible, to get test feedback and to get it included in 2.6.19 (or maybe 2.6.18? Who knows. ;)). Thus I'd like to get your comments, suggestions, etc. on it.
2.6.19 at best.
Oh, it's just a driver update for some powermac specific thingy, so depending how long 2.6.18 takes, I have no problem as the powermac maintainer letting that in once it's been properly fixed and reviewed. (Which I don't have time to do right now neither, but possibly later).
There shouldn't be significant changes to the i2c side of it from Stelian's driver anyway, more like a new backend using the PMU.
Quoted 11 lines
drivers/hwmon/Kconfig config SENSORS_HDAPS · line 512
Quoted 3 lines
drivers/hwmon/Kconfig +3
-0+config SENSORS_AMS + tristate "Motion sensor driver" + default yNo, not everyone has this device. We don't have a default for other hardware monitoring drivers.
This should depend on HWMON, and probably EXPERIMENTAL too, until it gets some wider testing.
Also please respect the alphabetical order.
It can stay default y as lons as it depends on PPC_PMAC (which it should do anyway)
Quoted 4 lines
If you are going to have many source files and a composite module, please create your own subdirectory under
drivers/hwmonand put all your stuff here. The kernel build system is notoriously bad at handling multiple composite modules within the same subdirectory.
Agreed.
Cheers,
Ben.
Le lundi 03 juillet 2006 ? 00:26 +0200, Michael Hanselmann a ?crit :
Quoted 1 line
[AMS patch]
Okay, I've tried the driver and it oopsed on rmmod:
Unable to handle kernel paging request for data at address 0x00000000 Faulting instruction address: 0xc01cfd1c Oops: Kernel access of bad area, sig: 11 [#1] Modules linked in: ams nfsd exportfs lockd sunrpc appletouch bcm43xx ohci1394 ie ee80211softmac ieee80211 pcmcia ieee80211_crypt yenta_socket rsrc_nonstatic pcmc ia_core ohci_hcd ehci_hcd NIP: C01CFD1C LR: C01CFCFC CTR: C01CFC6C REGS: c48b9d90 TRAP: 0300 Not tainted (2.6.17.2) MSR: 00009032 <EE,ME,IR,DR> CR: 22004422 XER: 20000000 DAR: 00000000, DSISR: 42000000
TASK = dd79b340[26173] 'rmmod' THREAD: c48b8000
GPR00: 00100100 C48B9E40 DD79B340 C0CDA828 C00F4CD0 00000001 C48B8000 F22451F8
GPR08: 00000000 00200200 00000000 F2245238 00000000 1001A188 100D0000 00000000
GPR16: 00000000 10104308 100D0000 100B0000 100D0000 100B0000 10013008 00000000
GPR24: C0380000 C0380000 EFFCCC88 C0350000 C0CDA828 F22450D0 00000000 F22450B8 NIP [C01CFD1C] i2c_detach_client+0xb0/0x16c
LR [C01CFCFC] i2c_detach_client+0x90/0x16c
Call Trace:
[C48B9E40] [DDF8077C] 0xddf8077c (unreliable) [C48B9E60] [F2242984] ams_i2c_detach+0x20/0x60 [ams] [C48B9E80] [C01D1024] i2c_del_driver+0x60/0x1d0 [C48B9EB0] [F22429DC] ams_i2c_exit+0x18/0x28 [ams] [C48B9EC0] [F2242428] cleanup_module+0x58/0xd4 [ams] [C48B9ED0] [C0045C70] sys_delete_module+0x180/0x1e8 [C48B9F40] [C000EE7C] ret_from_syscall+0x0/0x38 --- Exception: c01 at 0xff7320c
LR = 0x1000103c
Instruction dump:3b9c0028 3bbf0018 7f83e378 480c4839 38ff0140 815f0140 3c000010 81070004 60000100 3d200020 397f0180 61290200 <91480000> 3c80c038 7fa3eb78 901f0140
I haven't had the time too look into the issue yet.
Below are a few comments on the patch:
drivers/hwmon/ams.h line 56
Quoted 2 lines
drivers/hwmon/ams.h +2
-0+/* calibrated null values */ +int xcalib, ycalib;
Add zcalib too for movement on z-axis.
drivers/hwmon/ams-mouse.c line 36
Quoted 2 lines
drivers/hwmon/ams-mouse.c +2
-0+input_report_abs (ams.idev,ABS_X , x - ams.xcalib); +input_report_abs (ams.idev,ABS_Y , y - ams.ycalib);
Ditto, ABS_Z...
drivers/hwmon/ams-mouse.c line 69
Quoted 2 lines
drivers/hwmon/ams-mouse.c +2
-0+input_set_abs_params (ams.idev,ABS_X ,-50 ,50 ,3 ,0 ); +input_set_abs_params (ams.idev,ABS_Y ,-50 ,50 ,3 ,0 );
And here...
Quoted 10 lines
drivers/hwmon/ams-mouse.c +10
-0+static ssize_t ams_mouse_store_mouse (struct device *dev, +struct device_attribute *attr,const char *buf,size_t count) +{ +int oldmouse = mouse; + +if (sscanf (buf,"%d\n" , &mouse) !=1 ) +return -EINVAL ; + + mouse = !!mouse; +
drivers/hwmon/ams-mouse.c line 124
Quoted 10 lines
drivers/hwmon/ams-mouse.c +10
-0+if (oldmouse != mouse) { +mutex_lock (&ams.lock); + +if (mouse) +ams_mouse_enable (); +else +ams_mouse_disable (); + +mutex_unlock (&ams.lock); + }
No need for oldmouse here. Just call ams_mouse_enable() or disable() in every case.
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);
I would prefer three different files for x, y and z instead of a single one...
drivers/hwmon/ams-pmu.c line 173
Quoted 4 lines
drivers/hwmon/ams-pmu.c +4
-0+ result =ams_sensor_attach (); +if (result <0 ) { + goto exit; + }
No need for braces.
drivers/hwmon/Kconfig config SENSORS_HDAPS · line 535
Quoted 5 lines
drivers/hwmon/Kconfig +5
-0+config SENSORS_AMS_MOUSE + bool "Support for mouse mode with motion sensor" + depends on SENSORS_AMS && INPUT + help + Support for mouse emulation with motion sensor.
Is it really necessary to have a config option here ?
Stelian.
Signature · 1 line
Stelian Pop <stelian at popies.net>
Quoted 2 lines
Is it really necessary to have a config option here ?
Same goes, imo, for the hdpark functionality (once we get it through all layers, I agree with leaving it out for now completely). That's what the thing was designed for after all.
johannes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 828 bytes
Desc: This is a digitally signed message part
Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20060703/c013b9ad/attachment.bin
Quoted 6 lines · Stelian Pop
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);I would prefer three different files for x, y and z instead of a single one...
Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the ams_sensors function gets much more complicated.
To prevent it from having to read them three times in a row, I joined all three values.
Do you think I should rewrite the ams_sensors function to only get the correct value?
Thanks,
Michael
(All other issues, including those in the other mails, are clear and will be addressed by a new patch)
Quoted 12 lines · Michael Hanselmann
Quoted 6 lines · Stelian Pop
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);I would prefer three different files for x, y and z instead of a single one...
Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the
ams_sensorsfunction gets much more complicated.
Quoted 5 lines
To prevent it from having to read them three times in a row, I joined all three values.
Do you think I should rewrite the
ams_sensorsfunction to only get the correct value?
Cache the values and only re-read from the hardware after a given amount of time has elapsed ?
Quoted 5 lines
Thanks,
Michael(All other issues, including those in the other mails, are clear and will be addressed by a new patch)
Le lundi 03 juillet 2006 ? 20:49 +1000, Benjamin Herrenschmidt a ?crit :
Quoted 13 lines · Michael Hanselmann
Quoted 12 lines · Stelian Pop
Quoted 6 lines
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);I would prefer three different files for x, y and z instead of a single one...
Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the
ams_sensorsfunction gets much more complicated.
Quoted 8 lines
Quoted 5 lines
To prevent it from having to read them three times in a row, I joined all three values.
Do you think I should rewrite the
ams_sensorsfunction to only get the correct value?Cache the values and only re-read from the hardware after a given amount of time has elapsed ?
Seems to be a good idea indeed.
Stelian.
Signature · 1 line
Stelian Pop <stelian at popies.net>
Quoted 17 lines · Stelian Pop
Le lundi 03 juillet 2006 ? 20:49 +1000, Benjamin Herrenschmidt a ?crit :
Quoted 16 lines · Michael Hanselmann
Quoted 15 lines · Stelian Pop
Quoted 6 lines
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);I would prefer three different files for x, y and z instead of a single one...
Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the
ams_sensorsfunction gets much more complicated.To prevent it from having to read them three times in a row, I joined all three values.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
Do you think I should rewrite the
ams_sensorsfunction to only get the correct value?Cache the values and only re-read from the hardware after a given amount of time has elapsed ?
Seems to be a good idea indeed.
But what is the purpose of that attribute anyway? I'd just drop it completely.
Signature · 1 line
Dmitry
Quoted 4 lines · Michael Hanselmann
Quoted 3 lines · Stelian Pop
drivers/hwmon/ams-mouse.c line 130
Quoted 3 lines
drivers/hwmon/ams-mouse.c +3
-0+ +static DEVICE_ATTR(mouse, S_IRUGO | S_IWUSR, + ams_mouse_show_mouse, ams_mouse_store_mouse);
[I think the following discussion is about ams_show_xyz, not ams_mouse_show_mouse.]
Quoted 9 lines
Quoted 2 lines
I would prefer three different files for x, y and z instead of a single one...
Because of the way the values are calculated with orientation, that would mean that if a program needs all three, either all values are read three times or the
ams_sensorsfunction gets much more complicated.To prevent it from having to read them three times in a row, I joined all three values.
I don't know what a software will be doing with it, but "displacement" (and its time derivatives "velocity", "acceleration", "jerk") is a vector. Why not write the component-wise representation of the vector into a single sysfs attribute? Especially if this keeps the kernel code simple and small. I suppose even userspace code which evaluates the attribute is in many cases simpler (and more precise anyway) if only a single attribute is used.
Signature · 3 lines
Stefan Richter -===-=-=- -=- ==-- http://arcgraph.de/sr/
Quoted 3 lines · Michael Hanselmann
+???????set_bit(EV_ABS,
ams.idev->evbit);
+???????set_bit(EV_KEY,ams.idev->evbit);
+???????set_bit(BTN_TOUCH,ams.idev->keybit);
BTW, what is BTN_TOUCH doing here? I don't see it being reported anywhere...
Just drop it off and have ams be a "joystick" instead of a "mouse" ;) Joystick interface is probably better suited anyway.
Signature · 1 line
Dmitry
Hello
Quoted 1 line · Michael Hanselmann
Below you find the latest revision of my AMS driver.
Here's another patch with lots of fixes. In random order:
- I2C crash: Moved client remove call, unable to test myself
- Z axis for mouse
- Removed config option for mouse
- Renamed "xyz" attribute to "current" after discussion with Johannes Berg (johill). I don't like caching here, thus one attribute with all values. Users might want to use it in scripts.
- Removed hdpark stuff for now
- Changed dependencies of SENSORS_AMS, moved to its own directory
- Set bustype correctly
- Cleaned up ams_sensors function
Is this better now?
Todo:
- Rewrite I2C attach code (I don't have the I2C hardware myself, thus I can't do it right now)
- Maybe rewrite mouse emulation without kthread, but with workqueue (Any comments on this?)
Thanks,
Michael
---
drivers/hwmon/ams/ams-core.c +242 -0
@@ -0,0 +1,242 @@ +/* +* Apple Motion Sensor driver +* +* Copyright (C) 2005 Stelian Pop (stelian at popies.net) +* Copyright (C) 2006 Michael Hanselmann (linux-kernel at hansmi.ch) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* 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/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <asm/pmac_pfunc.h> + +#include "ams.h" + +/* There is only one motion sensor per machine */ +struct ams ams; + +static unsigned int verbose; +module_param (verbose, bool,0644 ); +MODULE_PARM_DESC (verbose,"Show free falls and shocks in kernel output" ); + +/* Call with lock held! */ +void ams_sensors (s8 *x,s8 *y,s8 *z) +{ +u32 orient = ams.vflag? ams.orient1 : ams.orient2; + +if (orient &0x80 ) +/* X and Y swapped */ + ams.get_xyz (y, x, z); +else + ams.get_xyz (x, y, z); + +if (orient &0x04 ) + *z = ~(*z); +if (orient &0x02 ) + *y = ~(*y); +if (orient &0x01 ) + *x = ~(*x); +} + +static ssize_t ams_show_current (struct device *dev, +struct device_attribute *attr,char *buf) +{ +s8 x, y, z; + +mutex_lock (&ams.lock); +ams_sensors (&x, &y, &z); +mutex_unlock (&ams.lock); + +return snprintf (buf,PAGE_SIZE ,"%d %d %d\n" , x, y, z); +} + +static DEVICE_ATTR(current, S_IRUGO, ams_show_current, NULL); + +static void ams_handle_irq (void *data) +{ +enum ams_irq irq = *((enum ams_irq *)data); +unsigned long flags; + +spin_lock_irqsave (&ams.irq_lock, flags); + + ams.worker_irqs |= irq; +schedule_work (&ams.worker); + +spin_unlock_irqrestore (&ams.irq_lock, flags); +} + +static enum ams_irq ams_freefall_irq_data =AMS_IRQ_FREEFALL ; +static struct pmf_irq_client ams_freefall_client = { + .owner =THIS_MODULE , + .handler = ams_handle_irq, + .data = &ams_freefall_irq_data, +}; + +static enum ams_irq ams_shock_irq_data =AMS_IRQ_SHOCK ; +static struct pmf_irq_client ams_shock_client = { + .owner =THIS_MODULE , + .handler = ams_handle_irq, + .data = &ams_shock_irq_data, +}; + +/* Once hard disk parking is implemented in the kernel, this function can +* trigger it. +*/ +static void ams_worker (void *data) +{ +mutex_lock (&ams.lock); + +if (ams.has_device) { +if (ams.worker_irqs &AMS_IRQ_FREEFALL ) { +if (verbose) +printk (KERN_INFO "ams: freefall detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_FREEFALL ; + ams.clear_irq (AMS_IRQ_FREEFALL ); + } + +if (ams.worker_irqs &AMS_IRQ_SHOCK ) { +if (verbose) +printk (KERN_INFO "ams: shock detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_SHOCK ; + ams.clear_irq (AMS_IRQ_SHOCK ); + } + } + +mutex_unlock (&ams.lock); +} + +/* Call with lock held! */ +int ams_sensor_attach (void ) +{ +int result; +u32 *prop; + +/* Get orientation */ + prop = (u32 *)get_property (ams.of_node,"orientation" ,NULL ); +if (!prop) +return -ENODEV ; + ams.orient1 = *prop; + ams.orient2 = *(prop +1 ); + +/* Register freefall interrupt handler */ + result =pmf_register_irq_client (ams.of_node, +"accel-int-1" , + &ams_freefall_client); +if (result <0 ) +return -ENODEV ; + +/* Reset saved irqs */ + ams.worker_irqs =0 ; + +/* Register shock interrupt handler */ + result =pmf_register_irq_client (ams.of_node, +"accel-int-2" , + &ams_shock_client); +if (result <0 ) { +pmf_unregister_irq_client (&ams_freefall_client); +return -ENODEV ; + } + +/* Create device */ + ams.of_dev =of_platform_device_create (ams.of_node,"ams" ,NULL ); +if (!ams.of_dev) { +pmf_unregister_irq_client (&ams_shock_client); +pmf_unregister_irq_client (&ams_freefall_client); +return -ENODEV ; + } + +/* Create attributes */ +device_create_file (&ams.of_dev->dev, &dev_attr_current); + + ams.vflag = !!(ams.get_vendor () &0x10 ); + +/* Init mouse device */ +ams_mouse_init (); + +return 0 ; +} + +int __init ams_init (void ) +{ +struct device_node *np; + +spin_lock_init (&ams.irq_lock); +mutex_init (&ams.lock); +INIT_WORK (&ams.worker, ams_worker,NULL ); + +#ifdef CONFIG_SENSORS_AMS_I2C + np =of_find_node_by_name (NULL ,"accelerometer" ); +if (np &&device_is_compatible (np,"AAPL,accelerometer_1" )) +/* Found I2C motion sensor */ +return ams_i2c_init (np); +#endif + +#ifdef CONFIG_SENSORS_AMS_PMU + np =of_find_node_by_name (NULL ,"sms" ); +if (np &&device_is_compatible (np,"sms" )) +/* Found PMU motion sensor */ +return ams_pmu_init (np); +#endif + +printk (KERN_ERR "ams: No motion sensor found.\n" ); + +return -ENODEV ; +} + +void ams_exit (void ) +{ +mutex_lock (&ams.lock); + +if (ams.has_device) { +/* Remove mouse device */ +ams_mouse_exit (); + +/* Shut down implementation */ + ams.exit (); + +/* Cancel interrupt worker +* +* We do this after ams.exit(), because an interrupt might +* have arrived before disabling them. +*/ +cancel_delayed_work (&ams.worker); +flush_scheduled_work (); + +/* Remove attributes */ +device_remove_file (&ams.of_dev->dev, &dev_attr_current); + +/* Remove device */ +of_device_unregister (ams.of_dev); + +/* Remove handler */ +pmf_unregister_irq_client (&ams_shock_client); +pmf_unregister_irq_client (&ams_freefall_client); + } + +mutex_unlock (&ams.lock); +} + +MODULE_AUTHOR ("Stelian Pop, Michael Hanselmann" ); +MODULE_DESCRIPTION ("Apple Motion Sensor driver" ); +MODULE_LICENSE ("GPL" ); + +module_init (ams_init); +module_exit (ams_exit);
drivers/hwmon/ams/ams.h +71 -0
71 lines not on this page · raw message
drivers/hwmon/ams/ams-i2c.c +289 -0
289 lines not on this page · raw message
drivers/hwmon/ams/ams-mouse.c +145 -0
145 lines not on this page · raw message
drivers/hwmon/ams/ams-pmu.c +202 -0
202 lines not on this page · raw message
drivers/hwmon/ams/Makefile +8 -0
@@ -0,0 +1,8 @@ +# +# Makefile for Apple Motion Sensor driver +# + +ams-y := ams-core.o ams-mouse.o +ams-$(CONFIG_SENSORS_AMS_PMU) += ams-pmu.o +ams-$(CONFIG_SENSORS_AMS_I2C) += ams-i2c.o +obj-$(CONFIG_SENSORS_AMS) += ams.o
drivers/hwmon/Kconfig +22 -0
@@ -94,6 +94,28 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_AMS + tristate "Apple Motion Sensor driver" + depends on HWMON && PPC_PMAC && INPUT && EXPERIMENTAL + default y + help + Support for the motion sensor included in PowerBooks. + +config SENSORS_AMS_PMU + bool "PMU variant" + depends on SENSORS_AMS && ADB_PMU + default y + help + PMU variant of motion sensor, found in late 2005 PowerBooks. + +config SENSORS_AMS_I2C + bool "I2C variant" + depends on SENSORS_AMS && I2C + default y + help + I2C variant of motion sensor, found in early 2005 PowerBooks and + iBooks. + config SENSORS_ASB100 tristate "Asus ASB100 Bach" depends on HWMON && I2C && EXPERIMENTAL
drivers/hwmon/Makefile +1 -0
@@ -18,6 +18,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025 obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
drivers/hwmon/ams/ams-core.c line 82
Quoted 12 lines · you
drivers/hwmon/ams/ams-core.c +12
-0+static void ams_handle_irq (void *data) +{ +enum ams_irq irq = *((enum ams_irq *)data); +unsigned long flags; + +spin_lock_irqsave (&ams.irq_lock, flags); + + ams.worker_irqs |= irq; +schedule_work (&ams.worker); + +spin_unlock_irqrestore (&ams.irq_lock, flags); +}
I would say this is racy.
Locks should be added as shown below.
Review
Quoted 2 lines
(quoted diff) +2
-0+static void ams_worker(void *data) +{
unsigned long flags;
drivers/hwmon/ams/ams-core.c line 105
Quoted 3 lines
drivers/hwmon/ams/ams-core.c +3
-0+mutex_lock (&ams.lock); + +if (ams.has_device) {
spin_lock_irqsave(&ams.irq_lock, flags);
Quoted 5 lines
drivers/hwmon/ams/ams-core.c +5
-0+if (ams.worker_irqs &AMS_IRQ_FREEFALL ) { +if (verbose) +printk (KERN_INFO "ams: freefall detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_FREEFALL ;
drivers/hwmon/ams/ams-core.c line 122
Quoted 10 lines
drivers/hwmon/ams/ams-core.c +10
-0+ ams.clear_irq (AMS_IRQ_FREEFALL ); + } + +if (ams.worker_irqs &AMS_IRQ_SHOCK ) { +if (verbose) +printk (KERN_INFO "ams: shock detected!\n" ); + + ams.worker_irqs &= ~AMS_IRQ_SHOCK ; + ams.clear_irq (AMS_IRQ_SHOCK ); +
spin_unlock_irqrestore(&ams.irq_lock, flags);
drivers/hwmon/ams/ams-core.c line 67
Quoted 4 lines
drivers/hwmon/ams/ams-core.c +4
-0+ } + +mutex_unlock (&ams.lock); +}
Otherwise an IRQ could trigger while the bottom half is executing and corrupt the ams.worker_irqs field, because it is modified non-atomically.
Signature · 1 line
Greetings Michael.