Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.29-rc7 481 lines 13 kB view raw
1/* 2 * lis3lv02d.c - ST LIS3LV02DL accelerometer driver 3 * 4 * Copyright (C) 2007-2008 Yan Burman 5 * Copyright (C) 2008 Eric Piel 6 * Copyright (C) 2008-2009 Pavel Machek 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23#include <linux/kernel.h> 24#include <linux/init.h> 25#include <linux/dmi.h> 26#include <linux/module.h> 27#include <linux/types.h> 28#include <linux/platform_device.h> 29#include <linux/interrupt.h> 30#include <linux/input.h> 31#include <linux/kthread.h> 32#include <linux/semaphore.h> 33#include <linux/delay.h> 34#include <linux/wait.h> 35#include <linux/poll.h> 36#include <linux/freezer.h> 37#include <linux/uaccess.h> 38#include <linux/miscdevice.h> 39#include <acpi/acpi_drivers.h> 40#include <asm/atomic.h> 41#include "lis3lv02d.h" 42 43#define DRIVER_NAME "lis3lv02d" 44 45/* joystick device poll interval in milliseconds */ 46#define MDPS_POLL_INTERVAL 50 47/* 48 * The sensor can also generate interrupts (DRDY) but it's pretty pointless 49 * because their are generated even if the data do not change. So it's better 50 * to keep the interrupt for the free-fall event. The values are updated at 51 * 40Hz (at the lowest frequency), but as it can be pretty time consuming on 52 * some low processor, we poll the sensor only at 20Hz... enough for the 53 * joystick. 54 */ 55 56struct acpi_lis3lv02d adev = { 57 .misc_wait = __WAIT_QUEUE_HEAD_INITIALIZER(adev.misc_wait), 58}; 59 60EXPORT_SYMBOL_GPL(adev); 61 62static int lis3lv02d_add_fs(struct acpi_device *device); 63 64/** 65 * lis3lv02d_get_axis - For the given axis, give the value converted 66 * @axis: 1,2,3 - can also be negative 67 * @hw_values: raw values returned by the hardware 68 * 69 * Returns the converted value. 70 */ 71static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) 72{ 73 if (axis > 0) 74 return hw_values[axis - 1]; 75 else 76 return -hw_values[-axis - 1]; 77} 78 79/** 80 * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer 81 * @handle: the handle to the device 82 * @x: where to store the X axis value 83 * @y: where to store the Y axis value 84 * @z: where to store the Z axis value 85 * 86 * Note that 40Hz input device can eat up about 10% CPU at 800MHZ 87 */ 88static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) 89{ 90 int position[3]; 91 92 position[0] = adev.read_data(handle, OUTX); 93 position[1] = adev.read_data(handle, OUTY); 94 position[2] = adev.read_data(handle, OUTZ); 95 96 *x = lis3lv02d_get_axis(adev.ac.x, position); 97 *y = lis3lv02d_get_axis(adev.ac.y, position); 98 *z = lis3lv02d_get_axis(adev.ac.z, position); 99} 100 101void lis3lv02d_poweroff(acpi_handle handle) 102{ 103 adev.is_on = 0; 104} 105EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); 106 107void lis3lv02d_poweron(acpi_handle handle) 108{ 109 adev.is_on = 1; 110 adev.init(handle); 111} 112EXPORT_SYMBOL_GPL(lis3lv02d_poweron); 113 114/* 115 * To be called before starting to use the device. It makes sure that the 116 * device will always be on until a call to lis3lv02d_decrease_use(). Not to be 117 * used from interrupt context. 118 */ 119static void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev) 120{ 121 mutex_lock(&dev->lock); 122 dev->usage++; 123 if (dev->usage == 1) { 124 if (!dev->is_on) 125 lis3lv02d_poweron(dev->device->handle); 126 } 127 mutex_unlock(&dev->lock); 128} 129 130/* 131 * To be called whenever a usage of the device is stopped. 132 * It will make sure to turn off the device when there is not usage. 133 */ 134static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev) 135{ 136 mutex_lock(&dev->lock); 137 dev->usage--; 138 if (dev->usage == 0) 139 lis3lv02d_poweroff(dev->device->handle); 140 mutex_unlock(&dev->lock); 141} 142 143static irqreturn_t lis302dl_interrupt(int irq, void *dummy) 144{ 145 /* 146 * Be careful: on some HP laptops the bios force DD when on battery and 147 * the lid is closed. This leads to interrupts as soon as a little move 148 * is done. 149 */ 150 atomic_inc(&adev.count); 151 152 wake_up_interruptible(&adev.misc_wait); 153 kill_fasync(&adev.async_queue, SIGIO, POLL_IN); 154 return IRQ_HANDLED; 155} 156 157static int lis3lv02d_misc_open(struct inode *inode, struct file *file) 158{ 159 int ret; 160 161 if (test_and_set_bit(0, &adev.misc_opened)) 162 return -EBUSY; /* already open */ 163 164 atomic_set(&adev.count, 0); 165 166 /* 167 * The sensor can generate interrupts for free-fall and direction 168 * detection (distinguishable with FF_WU_SRC and DD_SRC) but to keep 169 * the things simple and _fast_ we activate it only for free-fall, so 170 * no need to read register (very slow with ACPI). For the same reason, 171 * we forbid shared interrupts. 172 * 173 * IRQF_TRIGGER_RISING seems pointless on HP laptops because the 174 * io-apic is not configurable (and generates a warning) but I keep it 175 * in case of support for other hardware. 176 */ 177 ret = request_irq(adev.irq, lis302dl_interrupt, IRQF_TRIGGER_RISING, 178 DRIVER_NAME, &adev); 179 180 if (ret) { 181 clear_bit(0, &adev.misc_opened); 182 printk(KERN_ERR DRIVER_NAME ": IRQ%d allocation failed\n", adev.irq); 183 return -EBUSY; 184 } 185 lis3lv02d_increase_use(&adev); 186 printk("lis3: registered interrupt %d\n", adev.irq); 187 return 0; 188} 189 190static int lis3lv02d_misc_release(struct inode *inode, struct file *file) 191{ 192 fasync_helper(-1, file, 0, &adev.async_queue); 193 lis3lv02d_decrease_use(&adev); 194 free_irq(adev.irq, &adev); 195 clear_bit(0, &adev.misc_opened); /* release the device */ 196 return 0; 197} 198 199static ssize_t lis3lv02d_misc_read(struct file *file, char __user *buf, 200 size_t count, loff_t *pos) 201{ 202 DECLARE_WAITQUEUE(wait, current); 203 u32 data; 204 unsigned char byte_data; 205 ssize_t retval = 1; 206 207 if (count < 1) 208 return -EINVAL; 209 210 add_wait_queue(&adev.misc_wait, &wait); 211 while (true) { 212 set_current_state(TASK_INTERRUPTIBLE); 213 data = atomic_xchg(&adev.count, 0); 214 if (data) 215 break; 216 217 if (file->f_flags & O_NONBLOCK) { 218 retval = -EAGAIN; 219 goto out; 220 } 221 222 if (signal_pending(current)) { 223 retval = -ERESTARTSYS; 224 goto out; 225 } 226 227 schedule(); 228 } 229 230 if (data < 255) 231 byte_data = data; 232 else 233 byte_data = 255; 234 235 /* make sure we are not going into copy_to_user() with 236 * TASK_INTERRUPTIBLE state */ 237 set_current_state(TASK_RUNNING); 238 if (copy_to_user(buf, &byte_data, sizeof(byte_data))) 239 retval = -EFAULT; 240 241out: 242 __set_current_state(TASK_RUNNING); 243 remove_wait_queue(&adev.misc_wait, &wait); 244 245 return retval; 246} 247 248static unsigned int lis3lv02d_misc_poll(struct file *file, poll_table *wait) 249{ 250 poll_wait(file, &adev.misc_wait, wait); 251 if (atomic_read(&adev.count)) 252 return POLLIN | POLLRDNORM; 253 return 0; 254} 255 256static int lis3lv02d_misc_fasync(int fd, struct file *file, int on) 257{ 258 return fasync_helper(fd, file, on, &adev.async_queue); 259} 260 261static const struct file_operations lis3lv02d_misc_fops = { 262 .owner = THIS_MODULE, 263 .llseek = no_llseek, 264 .read = lis3lv02d_misc_read, 265 .open = lis3lv02d_misc_open, 266 .release = lis3lv02d_misc_release, 267 .poll = lis3lv02d_misc_poll, 268 .fasync = lis3lv02d_misc_fasync, 269}; 270 271static struct miscdevice lis3lv02d_misc_device = { 272 .minor = MISC_DYNAMIC_MINOR, 273 .name = "freefall", 274 .fops = &lis3lv02d_misc_fops, 275}; 276 277/** 278 * lis3lv02d_joystick_kthread - Kthread polling function 279 * @data: unused - here to conform to threadfn prototype 280 */ 281static int lis3lv02d_joystick_kthread(void *data) 282{ 283 int x, y, z; 284 285 while (!kthread_should_stop()) { 286 lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); 287 input_report_abs(adev.idev, ABS_X, x - adev.xcalib); 288 input_report_abs(adev.idev, ABS_Y, y - adev.ycalib); 289 input_report_abs(adev.idev, ABS_Z, z - adev.zcalib); 290 291 input_sync(adev.idev); 292 293 try_to_freeze(); 294 msleep_interruptible(MDPS_POLL_INTERVAL); 295 } 296 297 return 0; 298} 299 300static int lis3lv02d_joystick_open(struct input_dev *input) 301{ 302 lis3lv02d_increase_use(&adev); 303 adev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d"); 304 if (IS_ERR(adev.kthread)) { 305 lis3lv02d_decrease_use(&adev); 306 return PTR_ERR(adev.kthread); 307 } 308 309 return 0; 310} 311 312static void lis3lv02d_joystick_close(struct input_dev *input) 313{ 314 kthread_stop(adev.kthread); 315 lis3lv02d_decrease_use(&adev); 316} 317 318static inline void lis3lv02d_calibrate_joystick(void) 319{ 320 lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); 321} 322 323int lis3lv02d_joystick_enable(void) 324{ 325 int err; 326 327 if (adev.idev) 328 return -EINVAL; 329 330 adev.idev = input_allocate_device(); 331 if (!adev.idev) 332 return -ENOMEM; 333 334 lis3lv02d_calibrate_joystick(); 335 336 adev.idev->name = "ST LIS3LV02DL Accelerometer"; 337 adev.idev->phys = DRIVER_NAME "/input0"; 338 adev.idev->id.bustype = BUS_HOST; 339 adev.idev->id.vendor = 0; 340 adev.idev->dev.parent = &adev.pdev->dev; 341 adev.idev->open = lis3lv02d_joystick_open; 342 adev.idev->close = lis3lv02d_joystick_close; 343 344 set_bit(EV_ABS, adev.idev->evbit); 345 input_set_abs_params(adev.idev, ABS_X, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); 346 input_set_abs_params(adev.idev, ABS_Y, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); 347 input_set_abs_params(adev.idev, ABS_Z, -adev.mdps_max_val, adev.mdps_max_val, 3, 3); 348 349 err = input_register_device(adev.idev); 350 if (err) { 351 input_free_device(adev.idev); 352 adev.idev = NULL; 353 } 354 355 return err; 356} 357EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); 358 359void lis3lv02d_joystick_disable(void) 360{ 361 if (!adev.idev) 362 return; 363 364 misc_deregister(&lis3lv02d_misc_device); 365 input_unregister_device(adev.idev); 366 adev.idev = NULL; 367} 368EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); 369 370/* 371 * Initialise the accelerometer and the various subsystems. 372 * Should be rather independant of the bus system. 373 */ 374int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) 375{ 376 mutex_init(&dev->lock); 377 lis3lv02d_add_fs(dev->device); 378 lis3lv02d_increase_use(dev); 379 380 if (lis3lv02d_joystick_enable()) 381 printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); 382 383 printk("lis3_init_device: irq %d\n", dev->irq); 384 385 /* if we did not get an IRQ from ACPI - we have nothing more to do */ 386 if (!dev->irq) { 387 printk(KERN_ERR DRIVER_NAME 388 ": No IRQ in ACPI. Disabling /dev/freefall\n"); 389 goto out; 390 } 391 392 printk("lis3: registering device\n"); 393 if (misc_register(&lis3lv02d_misc_device)) 394 printk(KERN_ERR DRIVER_NAME ": misc_register failed\n"); 395out: 396 lis3lv02d_decrease_use(dev); 397 return 0; 398} 399EXPORT_SYMBOL_GPL(lis3lv02d_init_device); 400 401/* Sysfs stuff */ 402static ssize_t lis3lv02d_position_show(struct device *dev, 403 struct device_attribute *attr, char *buf) 404{ 405 int x, y, z; 406 407 lis3lv02d_increase_use(&adev); 408 lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); 409 lis3lv02d_decrease_use(&adev); 410 return sprintf(buf, "(%d,%d,%d)\n", x, y, z); 411} 412 413static ssize_t lis3lv02d_calibrate_show(struct device *dev, 414 struct device_attribute *attr, char *buf) 415{ 416 return sprintf(buf, "(%d,%d,%d)\n", adev.xcalib, adev.ycalib, adev.zcalib); 417} 418 419static ssize_t lis3lv02d_calibrate_store(struct device *dev, 420 struct device_attribute *attr, 421 const char *buf, size_t count) 422{ 423 lis3lv02d_increase_use(&adev); 424 lis3lv02d_calibrate_joystick(); 425 lis3lv02d_decrease_use(&adev); 426 return count; 427} 428 429/* conversion btw sampling rate and the register values */ 430static int lis3lv02dl_df_val[4] = {40, 160, 640, 2560}; 431static ssize_t lis3lv02d_rate_show(struct device *dev, 432 struct device_attribute *attr, char *buf) 433{ 434 u8 ctrl; 435 int val; 436 437 lis3lv02d_increase_use(&adev); 438 adev.read(adev.device->handle, CTRL_REG1, &ctrl); 439 lis3lv02d_decrease_use(&adev); 440 val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; 441 return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); 442} 443 444static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); 445static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show, 446 lis3lv02d_calibrate_store); 447static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL); 448 449static struct attribute *lis3lv02d_attributes[] = { 450 &dev_attr_position.attr, 451 &dev_attr_calibrate.attr, 452 &dev_attr_rate.attr, 453 NULL 454}; 455 456static struct attribute_group lis3lv02d_attribute_group = { 457 .attrs = lis3lv02d_attributes 458}; 459 460 461static int lis3lv02d_add_fs(struct acpi_device *device) 462{ 463 adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); 464 if (IS_ERR(adev.pdev)) 465 return PTR_ERR(adev.pdev); 466 467 return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 468} 469 470int lis3lv02d_remove_fs(void) 471{ 472 sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); 473 platform_device_unregister(adev.pdev); 474 return 0; 475} 476EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); 477 478MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); 479MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); 480MODULE_LICENSE("GPL"); 481