| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 1 | /* | 
|  | 2 | * Apple Motion Sensor driver (joystick emulation) | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2005 Stelian Pop (stelian@popies.net) | 
|  | 5 | * Copyright (C) 2006 Michael Hanselmann (linux-kernel@hansmi.ch) | 
|  | 6 | * | 
|  | 7 | * This program is free software; you can redistribute it and/or modify | 
|  | 8 | * it under the terms of the GNU General Public License as published by | 
|  | 9 | * the Free Software Foundation; either version 2 of the License, or | 
|  | 10 | * (at your option) any later version. | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <linux/module.h> | 
|  | 14 |  | 
|  | 15 | #include <linux/types.h> | 
|  | 16 | #include <linux/errno.h> | 
|  | 17 | #include <linux/init.h> | 
|  | 18 | #include <linux/delay.h> | 
|  | 19 |  | 
|  | 20 | #include "ams.h" | 
|  | 21 |  | 
|  | 22 | static unsigned int joystick; | 
|  | 23 | module_param(joystick, bool, 0644); | 
|  | 24 | MODULE_PARM_DESC(joystick, "Enable the input class device on module load"); | 
|  | 25 |  | 
|  | 26 | static unsigned int invert; | 
|  | 27 | module_param(invert, bool, 0644); | 
|  | 28 | MODULE_PARM_DESC(invert, "Invert input data on X and Y axis"); | 
|  | 29 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 30 | static void ams_idev_poll(struct input_polled_dev *dev) | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 31 | { | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 32 | struct input_dev *idev = dev->input; | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 33 | s8 x, y, z; | 
|  | 34 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 35 | mutex_lock(&ams_info.lock); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 36 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 37 | ams_sensors(&x, &y, &z); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 38 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 39 | x -= ams_info.xcalib; | 
|  | 40 | y -= ams_info.ycalib; | 
|  | 41 | z -= ams_info.zcalib; | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 42 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 43 | input_report_abs(idev, ABS_X, invert ? -x : x); | 
|  | 44 | input_report_abs(idev, ABS_Y, invert ? -y : y); | 
|  | 45 | input_report_abs(idev, ABS_Z, z); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 46 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 47 | input_sync(idev); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 48 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 49 | mutex_unlock(&ams_info.lock); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 50 | } | 
|  | 51 |  | 
|  | 52 | /* Call with ams_info.lock held! */ | 
|  | 53 | static void ams_input_enable(void) | 
|  | 54 | { | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 55 | struct input_dev *input; | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 56 | s8 x, y, z; | 
|  | 57 |  | 
|  | 58 | if (ams_info.idev) | 
|  | 59 | return; | 
|  | 60 |  | 
|  | 61 | ams_sensors(&x, &y, &z); | 
|  | 62 | ams_info.xcalib = x; | 
|  | 63 | ams_info.ycalib = y; | 
|  | 64 | ams_info.zcalib = z; | 
|  | 65 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 66 | ams_info.idev = input_allocate_polled_device(); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 67 | if (!ams_info.idev) | 
|  | 68 | return; | 
|  | 69 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 70 | ams_info.idev->poll = ams_idev_poll; | 
|  | 71 | ams_info.idev->poll_interval = 25; | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 72 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 73 | input = ams_info.idev->input; | 
|  | 74 | input->name = "Apple Motion Sensor"; | 
|  | 75 | input->id.bustype = ams_info.bustype; | 
|  | 76 | input->id.vendor = 0; | 
|  | 77 | input->dev.parent = &ams_info.of_dev->dev; | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 78 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 79 | input_set_abs_params(input, ABS_X, -50, 50, 3, 0); | 
|  | 80 | input_set_abs_params(input, ABS_Y, -50, 50, 3, 0); | 
|  | 81 | input_set_abs_params(input, ABS_Z, -50, 50, 3, 0); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 82 |  | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 83 | set_bit(EV_ABS, input->evbit); | 
|  | 84 | set_bit(EV_KEY, input->evbit); | 
|  | 85 | set_bit(BTN_TOUCH, input->keybit); | 
|  | 86 |  | 
|  | 87 | if (input_register_polled_device(ams_info.idev)) { | 
|  | 88 | input_free_polled_device(ams_info.idev); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 89 | ams_info.idev = NULL; | 
|  | 90 | return; | 
|  | 91 | } | 
|  | 92 | } | 
|  | 93 |  | 
|  | 94 | /* Call with ams_info.lock held! */ | 
|  | 95 | static void ams_input_disable(void) | 
|  | 96 | { | 
|  | 97 | if (ams_info.idev) { | 
| Dmitry Torokhov | 3fdbc34 | 2007-09-26 00:01:41 -0400 | [diff] [blame] | 98 | input_unregister_polled_device(ams_info.idev); | 
|  | 99 | input_free_polled_device(ams_info.idev); | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 100 | ams_info.idev = NULL; | 
|  | 101 | } | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | static ssize_t ams_input_show_joystick(struct device *dev, | 
|  | 105 | struct device_attribute *attr, char *buf) | 
|  | 106 | { | 
|  | 107 | return sprintf(buf, "%d\n", joystick); | 
|  | 108 | } | 
|  | 109 |  | 
|  | 110 | static ssize_t ams_input_store_joystick(struct device *dev, | 
|  | 111 | struct device_attribute *attr, const char *buf, size_t count) | 
|  | 112 | { | 
|  | 113 | if (sscanf(buf, "%d\n", &joystick) != 1) | 
|  | 114 | return -EINVAL; | 
|  | 115 |  | 
|  | 116 | mutex_lock(&ams_info.lock); | 
|  | 117 |  | 
|  | 118 | if (joystick) | 
|  | 119 | ams_input_enable(); | 
|  | 120 | else | 
|  | 121 | ams_input_disable(); | 
|  | 122 |  | 
|  | 123 | mutex_unlock(&ams_info.lock); | 
|  | 124 |  | 
|  | 125 | return count; | 
|  | 126 | } | 
|  | 127 |  | 
|  | 128 | static DEVICE_ATTR(joystick, S_IRUGO | S_IWUSR, | 
|  | 129 | ams_input_show_joystick, ams_input_store_joystick); | 
|  | 130 |  | 
|  | 131 | /* Call with ams_info.lock held! */ | 
|  | 132 | int ams_input_init(void) | 
|  | 133 | { | 
|  | 134 | int result; | 
|  | 135 |  | 
|  | 136 | result = device_create_file(&ams_info.of_dev->dev, &dev_attr_joystick); | 
|  | 137 |  | 
|  | 138 | if (!result && joystick) | 
|  | 139 | ams_input_enable(); | 
|  | 140 | return result; | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | /* Call with ams_info.lock held! */ | 
| Al Viro | d7bde2f | 2007-02-09 16:38:50 +0000 | [diff] [blame] | 144 | void ams_input_exit(void) | 
| Stelian Pop | dcb69dd | 2006-12-12 18:18:30 +0100 | [diff] [blame] | 145 | { | 
|  | 146 | ams_input_disable(); | 
|  | 147 | device_remove_file(&ams_info.of_dev->dev, &dev_attr_joystick); | 
|  | 148 | } |