| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * The input core | 
|  | 3 | * | 
|  | 4 | * Copyright (c) 1999-2002 Vojtech Pavlik | 
|  | 5 | */ | 
|  | 6 |  | 
|  | 7 | /* | 
|  | 8 | * This program is free software; you can redistribute it and/or modify it | 
|  | 9 | * under the terms of the GNU General Public License version 2 as published by | 
|  | 10 | * the Free Software Foundation. | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <linux/init.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 14 | #include <linux/smp_lock.h> | 
|  | 15 | #include <linux/input.h> | 
|  | 16 | #include <linux/module.h> | 
|  | 17 | #include <linux/random.h> | 
|  | 18 | #include <linux/major.h> | 
|  | 19 | #include <linux/proc_fs.h> | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 20 | #include <linux/seq_file.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | #include <linux/interrupt.h> | 
|  | 22 | #include <linux/poll.h> | 
|  | 23 | #include <linux/device.h> | 
| Jes Sorensen | e676c23 | 2006-02-19 00:21:46 -0500 | [diff] [blame] | 24 | #include <linux/mutex.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 25 |  | 
|  | 26 | MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); | 
|  | 27 | MODULE_DESCRIPTION("Input core"); | 
|  | 28 | MODULE_LICENSE("GPL"); | 
|  | 29 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | #define INPUT_DEVICES	256 | 
|  | 31 |  | 
|  | 32 | static LIST_HEAD(input_dev_list); | 
|  | 33 | static LIST_HEAD(input_handler_list); | 
|  | 34 |  | 
|  | 35 | static struct input_handler *input_table[8]; | 
|  | 36 |  | 
| Dmitry Torokhov | 0e739d2 | 2006-07-06 00:22:43 -0400 | [diff] [blame] | 37 | /** | 
|  | 38 | * input_event() - report new input event | 
| Dmitry Torokhov | 1447190 | 2006-11-02 23:26:55 -0500 | [diff] [blame] | 39 | * @dev: device that generated the event | 
| Dmitry Torokhov | 0e739d2 | 2006-07-06 00:22:43 -0400 | [diff] [blame] | 40 | * @type: type of the event | 
|  | 41 | * @code: event code | 
|  | 42 | * @value: value of the event | 
|  | 43 | * | 
|  | 44 | * This function should be used by drivers implementing various input devices | 
|  | 45 | * See also input_inject_event() | 
|  | 46 | */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 47 | void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) | 
|  | 48 | { | 
|  | 49 | struct input_handle *handle; | 
|  | 50 |  | 
|  | 51 | if (type > EV_MAX || !test_bit(type, dev->evbit)) | 
|  | 52 | return; | 
|  | 53 |  | 
|  | 54 | add_input_randomness(type, code, value); | 
|  | 55 |  | 
|  | 56 | switch (type) { | 
|  | 57 |  | 
|  | 58 | case EV_SYN: | 
|  | 59 | switch (code) { | 
|  | 60 | case SYN_CONFIG: | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 61 | if (dev->event) | 
|  | 62 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | break; | 
|  | 64 |  | 
|  | 65 | case SYN_REPORT: | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 66 | if (dev->sync) | 
|  | 67 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 68 | dev->sync = 1; | 
|  | 69 | break; | 
|  | 70 | } | 
|  | 71 | break; | 
|  | 72 |  | 
|  | 73 | case EV_KEY: | 
|  | 74 |  | 
|  | 75 | if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) | 
|  | 76 | return; | 
|  | 77 |  | 
|  | 78 | if (value == 2) | 
|  | 79 | break; | 
|  | 80 |  | 
|  | 81 | change_bit(code, dev->key); | 
|  | 82 |  | 
|  | 83 | if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) { | 
|  | 84 | dev->repeat_key = code; | 
|  | 85 | mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY])); | 
|  | 86 | } | 
|  | 87 |  | 
|  | 88 | break; | 
|  | 89 |  | 
| Richard Purdie | 3158106 | 2005-09-06 15:19:06 -0700 | [diff] [blame] | 90 | case EV_SW: | 
|  | 91 |  | 
|  | 92 | if (code > SW_MAX || !test_bit(code, dev->swbit) || !!test_bit(code, dev->sw) == value) | 
|  | 93 | return; | 
|  | 94 |  | 
|  | 95 | change_bit(code, dev->sw); | 
|  | 96 |  | 
|  | 97 | break; | 
|  | 98 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | case EV_ABS: | 
|  | 100 |  | 
|  | 101 | if (code > ABS_MAX || !test_bit(code, dev->absbit)) | 
|  | 102 | return; | 
|  | 103 |  | 
|  | 104 | if (dev->absfuzz[code]) { | 
|  | 105 | if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && | 
|  | 106 | (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) | 
|  | 107 | return; | 
|  | 108 |  | 
|  | 109 | if ((value > dev->abs[code] - dev->absfuzz[code]) && | 
|  | 110 | (value < dev->abs[code] + dev->absfuzz[code])) | 
|  | 111 | value = (dev->abs[code] * 3 + value) >> 2; | 
|  | 112 |  | 
|  | 113 | if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && | 
|  | 114 | (value < dev->abs[code] + (dev->absfuzz[code] << 1))) | 
|  | 115 | value = (dev->abs[code] + value) >> 1; | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 | if (dev->abs[code] == value) | 
|  | 119 | return; | 
|  | 120 |  | 
|  | 121 | dev->abs[code] = value; | 
|  | 122 | break; | 
|  | 123 |  | 
|  | 124 | case EV_REL: | 
|  | 125 |  | 
|  | 126 | if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) | 
|  | 127 | return; | 
|  | 128 |  | 
|  | 129 | break; | 
|  | 130 |  | 
|  | 131 | case EV_MSC: | 
|  | 132 |  | 
|  | 133 | if (code > MSC_MAX || !test_bit(code, dev->mscbit)) | 
|  | 134 | return; | 
|  | 135 |  | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 136 | if (dev->event) | 
|  | 137 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 138 |  | 
|  | 139 | break; | 
|  | 140 |  | 
|  | 141 | case EV_LED: | 
|  | 142 |  | 
|  | 143 | if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) | 
|  | 144 | return; | 
|  | 145 |  | 
|  | 146 | change_bit(code, dev->led); | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 147 |  | 
|  | 148 | if (dev->event) | 
|  | 149 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 |  | 
|  | 151 | break; | 
|  | 152 |  | 
|  | 153 | case EV_SND: | 
|  | 154 |  | 
|  | 155 | if (code > SND_MAX || !test_bit(code, dev->sndbit)) | 
|  | 156 | return; | 
|  | 157 |  | 
| Dmitry Torokhov | 8fdc194 | 2006-04-29 01:13:48 -0400 | [diff] [blame] | 158 | if (!!test_bit(code, dev->snd) != !!value) | 
|  | 159 | change_bit(code, dev->snd); | 
|  | 160 |  | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 161 | if (dev->event) | 
|  | 162 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 163 |  | 
|  | 164 | break; | 
|  | 165 |  | 
|  | 166 | case EV_REP: | 
|  | 167 |  | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 168 | if (code > REP_MAX || value < 0 || dev->rep[code] == value) | 
|  | 169 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 170 |  | 
|  | 171 | dev->rep[code] = value; | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 172 | if (dev->event) | 
|  | 173 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 174 |  | 
|  | 175 | break; | 
|  | 176 |  | 
|  | 177 | case EV_FF: | 
| Anssi Hannula | 509ca1a | 2006-07-19 01:40:22 -0400 | [diff] [blame] | 178 |  | 
|  | 179 | if (value < 0) | 
|  | 180 | return; | 
|  | 181 |  | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 182 | if (dev->event) | 
|  | 183 | dev->event(dev, type, code, value); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 184 | break; | 
|  | 185 | } | 
|  | 186 |  | 
|  | 187 | if (type != EV_SYN) | 
|  | 188 | dev->sync = 0; | 
|  | 189 |  | 
|  | 190 | if (dev->grab) | 
|  | 191 | dev->grab->handler->event(dev->grab, type, code, value); | 
|  | 192 | else | 
|  | 193 | list_for_each_entry(handle, &dev->h_list, d_node) | 
|  | 194 | if (handle->open) | 
|  | 195 | handle->handler->event(handle, type, code, value); | 
|  | 196 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 197 | EXPORT_SYMBOL(input_event); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 |  | 
| Dmitry Torokhov | 0e739d2 | 2006-07-06 00:22:43 -0400 | [diff] [blame] | 199 | /** | 
|  | 200 | * input_inject_event() - send input event from input handler | 
|  | 201 | * @handle: input handle to send event through | 
|  | 202 | * @type: type of the event | 
|  | 203 | * @code: event code | 
|  | 204 | * @value: value of the event | 
|  | 205 | * | 
|  | 206 | * Similar to input_event() but will ignore event if device is "grabbed" and handle | 
|  | 207 | * injecting event is not the one that owns the device. | 
|  | 208 | */ | 
|  | 209 | void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) | 
|  | 210 | { | 
|  | 211 | if (!handle->dev->grab || handle->dev->grab == handle) | 
|  | 212 | input_event(handle->dev, type, code, value); | 
|  | 213 | } | 
|  | 214 | EXPORT_SYMBOL(input_inject_event); | 
|  | 215 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 216 | static void input_repeat_key(unsigned long data) | 
|  | 217 | { | 
|  | 218 | struct input_dev *dev = (void *) data; | 
|  | 219 |  | 
|  | 220 | if (!test_bit(dev->repeat_key, dev->key)) | 
|  | 221 | return; | 
|  | 222 |  | 
|  | 223 | input_event(dev, EV_KEY, dev->repeat_key, 2); | 
|  | 224 | input_sync(dev); | 
|  | 225 |  | 
|  | 226 | if (dev->rep[REP_PERIOD]) | 
|  | 227 | mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_PERIOD])); | 
|  | 228 | } | 
|  | 229 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 230 | int input_grab_device(struct input_handle *handle) | 
|  | 231 | { | 
|  | 232 | if (handle->dev->grab) | 
|  | 233 | return -EBUSY; | 
|  | 234 |  | 
|  | 235 | handle->dev->grab = handle; | 
|  | 236 | return 0; | 
|  | 237 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 238 | EXPORT_SYMBOL(input_grab_device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 239 |  | 
|  | 240 | void input_release_device(struct input_handle *handle) | 
|  | 241 | { | 
| Andrew Morton | a2b2ed2 | 2006-07-15 01:17:38 -0400 | [diff] [blame] | 242 | struct input_dev *dev = handle->dev; | 
| Dmitry Torokhov | c7e8dc6 | 2006-07-06 00:21:03 -0400 | [diff] [blame] | 243 |  | 
| Andrew Morton | a2b2ed2 | 2006-07-15 01:17:38 -0400 | [diff] [blame] | 244 | if (dev->grab == handle) { | 
|  | 245 | dev->grab = NULL; | 
|  | 246 |  | 
|  | 247 | list_for_each_entry(handle, &dev->h_list, d_node) | 
| Dmitry Torokhov | c7e8dc6 | 2006-07-06 00:21:03 -0400 | [diff] [blame] | 248 | if (handle->handler->start) | 
|  | 249 | handle->handler->start(handle); | 
|  | 250 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 251 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 252 | EXPORT_SYMBOL(input_release_device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 253 |  | 
|  | 254 | int input_open_device(struct input_handle *handle) | 
|  | 255 | { | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 256 | struct input_dev *dev = handle->dev; | 
|  | 257 | int err; | 
|  | 258 |  | 
| Jes Sorensen | e676c23 | 2006-02-19 00:21:46 -0500 | [diff] [blame] | 259 | err = mutex_lock_interruptible(&dev->mutex); | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 260 | if (err) | 
|  | 261 | return err; | 
|  | 262 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 263 | handle->open++; | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 264 |  | 
|  | 265 | if (!dev->users++ && dev->open) | 
|  | 266 | err = dev->open(dev); | 
|  | 267 |  | 
|  | 268 | if (err) | 
|  | 269 | handle->open--; | 
|  | 270 |  | 
| Jes Sorensen | e676c23 | 2006-02-19 00:21:46 -0500 | [diff] [blame] | 271 | mutex_unlock(&dev->mutex); | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 272 |  | 
|  | 273 | return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 275 | EXPORT_SYMBOL(input_open_device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 276 |  | 
|  | 277 | int input_flush_device(struct input_handle* handle, struct file* file) | 
|  | 278 | { | 
|  | 279 | if (handle->dev->flush) | 
|  | 280 | return handle->dev->flush(handle->dev, file); | 
|  | 281 |  | 
|  | 282 | return 0; | 
|  | 283 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 284 | EXPORT_SYMBOL(input_flush_device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 285 |  | 
|  | 286 | void input_close_device(struct input_handle *handle) | 
|  | 287 | { | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 288 | struct input_dev *dev = handle->dev; | 
|  | 289 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 290 | input_release_device(handle); | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 291 |  | 
| Jes Sorensen | e676c23 | 2006-02-19 00:21:46 -0500 | [diff] [blame] | 292 | mutex_lock(&dev->mutex); | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 293 |  | 
|  | 294 | if (!--dev->users && dev->close) | 
|  | 295 | dev->close(dev); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | handle->open--; | 
| Dmitry Torokhov | 0fbf87c | 2005-05-29 02:29:25 -0500 | [diff] [blame] | 297 |  | 
| Jes Sorensen | e676c23 | 2006-02-19 00:21:46 -0500 | [diff] [blame] | 298 | mutex_unlock(&dev->mutex); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 300 | EXPORT_SYMBOL(input_close_device); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 301 |  | 
|  | 302 | static void input_link_handle(struct input_handle *handle) | 
|  | 303 | { | 
|  | 304 | list_add_tail(&handle->d_node, &handle->dev->h_list); | 
|  | 305 | list_add_tail(&handle->h_node, &handle->handler->h_list); | 
|  | 306 | } | 
|  | 307 |  | 
|  | 308 | #define MATCH_BIT(bit, max) \ | 
|  | 309 | for (i = 0; i < NBITS(max); i++) \ | 
|  | 310 | if ((id->bit[i] & dev->bit[i]) != id->bit[i]) \ | 
|  | 311 | break; \ | 
|  | 312 | if (i != NBITS(max)) \ | 
|  | 313 | continue; | 
|  | 314 |  | 
| Dmitry Torokhov | 66e6611 | 2006-09-14 01:31:59 -0400 | [diff] [blame] | 315 | static const struct input_device_id *input_match_device(const struct input_device_id *id, | 
|  | 316 | struct input_dev *dev) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 317 | { | 
|  | 318 | int i; | 
|  | 319 |  | 
|  | 320 | for (; id->flags || id->driver_info; id++) { | 
|  | 321 |  | 
|  | 322 | if (id->flags & INPUT_DEVICE_ID_MATCH_BUS) | 
| Dmitry Torokhov | ddc5d34 | 2006-04-26 00:14:19 -0400 | [diff] [blame] | 323 | if (id->bustype != dev->id.bustype) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 324 | continue; | 
|  | 325 |  | 
|  | 326 | if (id->flags & INPUT_DEVICE_ID_MATCH_VENDOR) | 
| Dmitry Torokhov | ddc5d34 | 2006-04-26 00:14:19 -0400 | [diff] [blame] | 327 | if (id->vendor != dev->id.vendor) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | continue; | 
|  | 329 |  | 
|  | 330 | if (id->flags & INPUT_DEVICE_ID_MATCH_PRODUCT) | 
| Dmitry Torokhov | ddc5d34 | 2006-04-26 00:14:19 -0400 | [diff] [blame] | 331 | if (id->product != dev->id.product) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | continue; | 
|  | 333 |  | 
|  | 334 | if (id->flags & INPUT_DEVICE_ID_MATCH_VERSION) | 
| Dmitry Torokhov | ddc5d34 | 2006-04-26 00:14:19 -0400 | [diff] [blame] | 335 | if (id->version != dev->id.version) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 336 | continue; | 
|  | 337 |  | 
|  | 338 | MATCH_BIT(evbit,  EV_MAX); | 
|  | 339 | MATCH_BIT(keybit, KEY_MAX); | 
|  | 340 | MATCH_BIT(relbit, REL_MAX); | 
|  | 341 | MATCH_BIT(absbit, ABS_MAX); | 
|  | 342 | MATCH_BIT(mscbit, MSC_MAX); | 
|  | 343 | MATCH_BIT(ledbit, LED_MAX); | 
|  | 344 | MATCH_BIT(sndbit, SND_MAX); | 
|  | 345 | MATCH_BIT(ffbit,  FF_MAX); | 
| Dmitry Torokhov | ff13f98 | 2005-09-24 02:02:29 -0500 | [diff] [blame] | 346 | MATCH_BIT(swbit,  SW_MAX); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 347 |  | 
|  | 348 | return id; | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | return NULL; | 
|  | 352 | } | 
|  | 353 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 354 | #ifdef CONFIG_PROC_FS | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 355 |  | 
|  | 356 | static struct proc_dir_entry *proc_bus_input_dir; | 
|  | 357 | static DECLARE_WAIT_QUEUE_HEAD(input_devices_poll_wait); | 
|  | 358 | static int input_devices_state; | 
|  | 359 |  | 
|  | 360 | static inline void input_wakeup_procfs_readers(void) | 
|  | 361 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 | input_devices_state++; | 
|  | 363 | wake_up(&input_devices_poll_wait); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 364 | } | 
|  | 365 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 366 | static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 367 | { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 368 | int state = input_devices_state; | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 369 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 370 | poll_wait(file, &input_devices_poll_wait, wait); | 
|  | 371 | if (state != input_devices_state) | 
|  | 372 | return POLLIN | POLLRDNORM; | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 373 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 374 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 375 | } | 
|  | 376 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 377 | static struct list_head *list_get_nth_element(struct list_head *list, loff_t *pos) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 378 | { | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 379 | struct list_head *node; | 
|  | 380 | loff_t i = 0; | 
|  | 381 |  | 
|  | 382 | list_for_each(node, list) | 
|  | 383 | if (i++ == *pos) | 
|  | 384 | return node; | 
|  | 385 |  | 
|  | 386 | return NULL; | 
|  | 387 | } | 
|  | 388 |  | 
|  | 389 | static struct list_head *list_get_next_element(struct list_head *list, struct list_head *element, loff_t *pos) | 
|  | 390 | { | 
|  | 391 | if (element->next == list) | 
|  | 392 | return NULL; | 
|  | 393 |  | 
|  | 394 | ++(*pos); | 
|  | 395 | return element->next; | 
|  | 396 | } | 
|  | 397 |  | 
|  | 398 | static void *input_devices_seq_start(struct seq_file *seq, loff_t *pos) | 
|  | 399 | { | 
|  | 400 | /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ | 
|  | 401 |  | 
|  | 402 | return list_get_nth_element(&input_dev_list, pos); | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | static void *input_devices_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 
|  | 406 | { | 
|  | 407 | return list_get_next_element(&input_dev_list, v, pos); | 
|  | 408 | } | 
|  | 409 |  | 
|  | 410 | static void input_devices_seq_stop(struct seq_file *seq, void *v) | 
|  | 411 | { | 
|  | 412 | /* release lock here */ | 
|  | 413 | } | 
|  | 414 |  | 
|  | 415 | static void input_seq_print_bitmap(struct seq_file *seq, const char *name, | 
|  | 416 | unsigned long *bitmap, int max) | 
|  | 417 | { | 
|  | 418 | int i; | 
|  | 419 |  | 
|  | 420 | for (i = NBITS(max) - 1; i > 0; i--) | 
|  | 421 | if (bitmap[i]) | 
|  | 422 | break; | 
|  | 423 |  | 
|  | 424 | seq_printf(seq, "B: %s=", name); | 
|  | 425 | for (; i >= 0; i--) | 
|  | 426 | seq_printf(seq, "%lx%s", bitmap[i], i > 0 ? " " : ""); | 
|  | 427 | seq_putc(seq, '\n'); | 
|  | 428 | } | 
|  | 429 |  | 
|  | 430 | static int input_devices_seq_show(struct seq_file *seq, void *v) | 
|  | 431 | { | 
|  | 432 | struct input_dev *dev = container_of(v, struct input_dev, node); | 
|  | 433 | const char *path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 434 | struct input_handle *handle; | 
|  | 435 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 436 | seq_printf(seq, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x\n", | 
|  | 437 | dev->id.bustype, dev->id.vendor, dev->id.product, dev->id.version); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 438 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 439 | seq_printf(seq, "N: Name=\"%s\"\n", dev->name ? dev->name : ""); | 
|  | 440 | seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : ""); | 
|  | 441 | seq_printf(seq, "S: Sysfs=%s\n", path ? path : ""); | 
|  | 442 | seq_printf(seq, "H: Handlers="); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 443 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 444 | list_for_each_entry(handle, &dev->h_list, d_node) | 
|  | 445 | seq_printf(seq, "%s ", handle->name); | 
|  | 446 | seq_putc(seq, '\n'); | 
| Dmitry Torokhov | 051b2fe | 2005-09-15 02:01:54 -0500 | [diff] [blame] | 447 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 448 | input_seq_print_bitmap(seq, "EV", dev->evbit, EV_MAX); | 
|  | 449 | if (test_bit(EV_KEY, dev->evbit)) | 
|  | 450 | input_seq_print_bitmap(seq, "KEY", dev->keybit, KEY_MAX); | 
|  | 451 | if (test_bit(EV_REL, dev->evbit)) | 
|  | 452 | input_seq_print_bitmap(seq, "REL", dev->relbit, REL_MAX); | 
|  | 453 | if (test_bit(EV_ABS, dev->evbit)) | 
|  | 454 | input_seq_print_bitmap(seq, "ABS", dev->absbit, ABS_MAX); | 
|  | 455 | if (test_bit(EV_MSC, dev->evbit)) | 
|  | 456 | input_seq_print_bitmap(seq, "MSC", dev->mscbit, MSC_MAX); | 
|  | 457 | if (test_bit(EV_LED, dev->evbit)) | 
|  | 458 | input_seq_print_bitmap(seq, "LED", dev->ledbit, LED_MAX); | 
|  | 459 | if (test_bit(EV_SND, dev->evbit)) | 
|  | 460 | input_seq_print_bitmap(seq, "SND", dev->sndbit, SND_MAX); | 
|  | 461 | if (test_bit(EV_FF, dev->evbit)) | 
|  | 462 | input_seq_print_bitmap(seq, "FF", dev->ffbit, FF_MAX); | 
|  | 463 | if (test_bit(EV_SW, dev->evbit)) | 
|  | 464 | input_seq_print_bitmap(seq, "SW", dev->swbit, SW_MAX); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 465 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 466 | seq_putc(seq, '\n'); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 467 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 468 | kfree(path); | 
|  | 469 | return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 470 | } | 
|  | 471 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 472 | static struct seq_operations input_devices_seq_ops = { | 
|  | 473 | .start	= input_devices_seq_start, | 
|  | 474 | .next	= input_devices_seq_next, | 
|  | 475 | .stop	= input_devices_seq_stop, | 
|  | 476 | .show	= input_devices_seq_show, | 
|  | 477 | }; | 
|  | 478 |  | 
|  | 479 | static int input_proc_devices_open(struct inode *inode, struct file *file) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 480 | { | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 481 | return seq_open(file, &input_devices_seq_ops); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 482 | } | 
|  | 483 |  | 
| Arjan van de Ven | 2b8693c | 2007-02-12 00:55:32 -0800 | [diff] [blame] | 484 | static const struct file_operations input_devices_fileops = { | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 485 | .owner		= THIS_MODULE, | 
|  | 486 | .open		= input_proc_devices_open, | 
|  | 487 | .poll		= input_proc_devices_poll, | 
|  | 488 | .read		= seq_read, | 
|  | 489 | .llseek		= seq_lseek, | 
|  | 490 | .release	= seq_release, | 
|  | 491 | }; | 
|  | 492 |  | 
|  | 493 | static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos) | 
|  | 494 | { | 
|  | 495 | /* acquire lock here ... Yes, we do need locking, I knowi, I know... */ | 
|  | 496 | seq->private = (void *)(unsigned long)*pos; | 
|  | 497 | return list_get_nth_element(&input_handler_list, pos); | 
|  | 498 | } | 
|  | 499 |  | 
|  | 500 | static void *input_handlers_seq_next(struct seq_file *seq, void *v, loff_t *pos) | 
|  | 501 | { | 
|  | 502 | seq->private = (void *)(unsigned long)(*pos + 1); | 
|  | 503 | return list_get_next_element(&input_handler_list, v, pos); | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | static void input_handlers_seq_stop(struct seq_file *seq, void *v) | 
|  | 507 | { | 
|  | 508 | /* release lock here */ | 
|  | 509 | } | 
|  | 510 |  | 
|  | 511 | static int input_handlers_seq_show(struct seq_file *seq, void *v) | 
|  | 512 | { | 
|  | 513 | struct input_handler *handler = container_of(v, struct input_handler, node); | 
|  | 514 |  | 
|  | 515 | seq_printf(seq, "N: Number=%ld Name=%s", | 
|  | 516 | (unsigned long)seq->private, handler->name); | 
|  | 517 | if (handler->fops) | 
|  | 518 | seq_printf(seq, " Minor=%d", handler->minor); | 
|  | 519 | seq_putc(seq, '\n'); | 
|  | 520 |  | 
|  | 521 | return 0; | 
|  | 522 | } | 
|  | 523 | static struct seq_operations input_handlers_seq_ops = { | 
|  | 524 | .start	= input_handlers_seq_start, | 
|  | 525 | .next	= input_handlers_seq_next, | 
|  | 526 | .stop	= input_handlers_seq_stop, | 
|  | 527 | .show	= input_handlers_seq_show, | 
|  | 528 | }; | 
|  | 529 |  | 
|  | 530 | static int input_proc_handlers_open(struct inode *inode, struct file *file) | 
|  | 531 | { | 
|  | 532 | return seq_open(file, &input_handlers_seq_ops); | 
|  | 533 | } | 
|  | 534 |  | 
| Arjan van de Ven | 2b8693c | 2007-02-12 00:55:32 -0800 | [diff] [blame] | 535 | static const struct file_operations input_handlers_fileops = { | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 536 | .owner		= THIS_MODULE, | 
|  | 537 | .open		= input_proc_handlers_open, | 
|  | 538 | .read		= seq_read, | 
|  | 539 | .llseek		= seq_lseek, | 
|  | 540 | .release	= seq_release, | 
|  | 541 | }; | 
| Luke Kosewski | e334016fc | 2005-06-01 02:39:28 -0500 | [diff] [blame] | 542 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 543 | static int __init input_proc_init(void) | 
|  | 544 | { | 
|  | 545 | struct proc_dir_entry *entry; | 
|  | 546 |  | 
|  | 547 | proc_bus_input_dir = proc_mkdir("input", proc_bus); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 548 | if (!proc_bus_input_dir) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 | return -ENOMEM; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 550 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 551 | proc_bus_input_dir->owner = THIS_MODULE; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 552 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 553 | entry = create_proc_entry("devices", 0, proc_bus_input_dir); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 554 | if (!entry) | 
|  | 555 | goto fail1; | 
|  | 556 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 557 | entry->owner = THIS_MODULE; | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 558 | entry->proc_fops = &input_devices_fileops; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 559 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 560 | entry = create_proc_entry("handlers", 0, proc_bus_input_dir); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 561 | if (!entry) | 
|  | 562 | goto fail2; | 
|  | 563 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 564 | entry->owner = THIS_MODULE; | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 565 | entry->proc_fops = &input_handlers_fileops; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 566 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 567 | return 0; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 568 |  | 
|  | 569 | fail2:	remove_proc_entry("devices", proc_bus_input_dir); | 
|  | 570 | fail1: remove_proc_entry("input", proc_bus); | 
|  | 571 | return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 572 | } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 573 |  | 
| Andrew Morton | beffbdc | 2005-07-01 23:54:30 -0500 | [diff] [blame] | 574 | static void input_proc_exit(void) | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 575 | { | 
|  | 576 | remove_proc_entry("devices", proc_bus_input_dir); | 
|  | 577 | remove_proc_entry("handlers", proc_bus_input_dir); | 
|  | 578 | remove_proc_entry("input", proc_bus); | 
|  | 579 | } | 
|  | 580 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 581 | #else /* !CONFIG_PROC_FS */ | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 582 | static inline void input_wakeup_procfs_readers(void) { } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 583 | static inline int input_proc_init(void) { return 0; } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 584 | static inline void input_proc_exit(void) { } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 585 | #endif | 
|  | 586 |  | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 587 | #define INPUT_DEV_STRING_ATTR_SHOW(name)					\ | 
|  | 588 | static ssize_t input_dev_show_##name(struct class_device *dev, char *buf)	\ | 
|  | 589 | {										\ | 
|  | 590 | struct input_dev *input_dev = to_input_dev(dev);			\ | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 591 | \ | 
| Dmitry Torokhov | 1efa770 | 2007-02-18 01:40:37 -0500 | [diff] [blame] | 592 | return scnprintf(buf, PAGE_SIZE, "%s\n",				\ | 
|  | 593 | input_dev->name ? input_dev->name : "");		\ | 
| Greg Kroah-Hartman | 629b77a | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 594 | }										\ | 
|  | 595 | static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_##name, NULL); | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 596 |  | 
|  | 597 | INPUT_DEV_STRING_ATTR_SHOW(name); | 
|  | 598 | INPUT_DEV_STRING_ATTR_SHOW(phys); | 
|  | 599 | INPUT_DEV_STRING_ATTR_SHOW(uniq); | 
|  | 600 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 601 | static int input_print_modalias_bits(char *buf, int size, | 
|  | 602 | char name, unsigned long *bm, | 
|  | 603 | unsigned int min_bit, unsigned int max_bit) | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 604 | { | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 605 | int len = 0, i; | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 606 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 607 | len += snprintf(buf, max(size, 0), "%c", name); | 
|  | 608 | for (i = min_bit; i < max_bit; i++) | 
|  | 609 | if (bm[LONG(i)] & BIT(i)) | 
|  | 610 | len += snprintf(buf + len, max(size - len, 0), "%X,", i); | 
| Kay Sievers | bd37e5a | 2006-01-05 13:19:55 +0100 | [diff] [blame] | 611 | return len; | 
|  | 612 | } | 
|  | 613 |  | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 614 | static int input_print_modalias(char *buf, int size, struct input_dev *id, | 
|  | 615 | int add_cr) | 
| Kay Sievers | bd37e5a | 2006-01-05 13:19:55 +0100 | [diff] [blame] | 616 | { | 
|  | 617 | int len; | 
|  | 618 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 619 | len = snprintf(buf, max(size, 0), | 
|  | 620 | "input:b%04Xv%04Xp%04Xe%04X-", | 
|  | 621 | id->id.bustype, id->id.vendor, | 
|  | 622 | id->id.product, id->id.version); | 
| Kay Sievers | bd37e5a | 2006-01-05 13:19:55 +0100 | [diff] [blame] | 623 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 624 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 625 | 'e', id->evbit, 0, EV_MAX); | 
|  | 626 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 627 | 'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX); | 
|  | 628 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 629 | 'r', id->relbit, 0, REL_MAX); | 
|  | 630 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 631 | 'a', id->absbit, 0, ABS_MAX); | 
|  | 632 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 633 | 'm', id->mscbit, 0, MSC_MAX); | 
|  | 634 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 635 | 'l', id->ledbit, 0, LED_MAX); | 
|  | 636 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 637 | 's', id->sndbit, 0, SND_MAX); | 
|  | 638 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 639 | 'f', id->ffbit, 0, FF_MAX); | 
|  | 640 | len += input_print_modalias_bits(buf + len, size - len, | 
|  | 641 | 'w', id->swbit, 0, SW_MAX); | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 642 |  | 
|  | 643 | if (add_cr) | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 644 | len += snprintf(buf + len, max(size - len, 0), "\n"); | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 645 |  | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 646 | return len; | 
|  | 647 | } | 
|  | 648 |  | 
|  | 649 | static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf) | 
|  | 650 | { | 
|  | 651 | struct input_dev *id = to_input_dev(dev); | 
| Kay Sievers | bd37e5a | 2006-01-05 13:19:55 +0100 | [diff] [blame] | 652 | ssize_t len; | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 653 |  | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 654 | len = input_print_modalias(buf, PAGE_SIZE, id, 1); | 
|  | 655 |  | 
| Richard Purdie | 8a3cf45 | 2006-06-26 01:48:21 -0400 | [diff] [blame] | 656 | return min_t(int, len, PAGE_SIZE); | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 657 | } | 
|  | 658 | static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL); | 
|  | 659 |  | 
| Greg Kroah-Hartman | 629b77a | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 660 | static struct attribute *input_dev_attrs[] = { | 
|  | 661 | &class_device_attr_name.attr, | 
|  | 662 | &class_device_attr_phys.attr, | 
|  | 663 | &class_device_attr_uniq.attr, | 
| Rusty Russell | 1d8f430 | 2005-12-07 21:40:34 +0100 | [diff] [blame] | 664 | &class_device_attr_modalias.attr, | 
| Greg Kroah-Hartman | 629b77a | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 665 | NULL | 
|  | 666 | }; | 
|  | 667 |  | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 668 | static struct attribute_group input_dev_attr_group = { | 
| Greg Kroah-Hartman | 629b77a | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 669 | .attrs	= input_dev_attrs, | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 670 | }; | 
|  | 671 |  | 
|  | 672 | #define INPUT_DEV_ID_ATTR(name)							\ | 
|  | 673 | static ssize_t input_dev_show_id_##name(struct class_device *dev, char *buf)	\ | 
|  | 674 | {										\ | 
|  | 675 | struct input_dev *input_dev = to_input_dev(dev);			\ | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 676 | return scnprintf(buf, PAGE_SIZE, "%04x\n", input_dev->id.name);		\ | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 677 | }										\ | 
|  | 678 | static CLASS_DEVICE_ATTR(name, S_IRUGO, input_dev_show_id_##name, NULL); | 
|  | 679 |  | 
|  | 680 | INPUT_DEV_ID_ATTR(bustype); | 
|  | 681 | INPUT_DEV_ID_ATTR(vendor); | 
|  | 682 | INPUT_DEV_ID_ATTR(product); | 
|  | 683 | INPUT_DEV_ID_ATTR(version); | 
|  | 684 |  | 
|  | 685 | static struct attribute *input_dev_id_attrs[] = { | 
|  | 686 | &class_device_attr_bustype.attr, | 
|  | 687 | &class_device_attr_vendor.attr, | 
|  | 688 | &class_device_attr_product.attr, | 
|  | 689 | &class_device_attr_version.attr, | 
|  | 690 | NULL | 
|  | 691 | }; | 
|  | 692 |  | 
|  | 693 | static struct attribute_group input_dev_id_attr_group = { | 
|  | 694 | .name	= "id", | 
|  | 695 | .attrs	= input_dev_id_attrs, | 
|  | 696 | }; | 
|  | 697 |  | 
| Dmitry Torokhov | 969b21c | 2006-04-02 00:09:34 -0500 | [diff] [blame] | 698 | static int input_print_bitmap(char *buf, int buf_size, unsigned long *bitmap, | 
|  | 699 | int max, int add_cr) | 
|  | 700 | { | 
|  | 701 | int i; | 
|  | 702 | int len = 0; | 
|  | 703 |  | 
|  | 704 | for (i = NBITS(max) - 1; i > 0; i--) | 
|  | 705 | if (bitmap[i]) | 
|  | 706 | break; | 
|  | 707 |  | 
|  | 708 | for (; i >= 0; i--) | 
|  | 709 | len += snprintf(buf + len, max(buf_size - len, 0), | 
|  | 710 | "%lx%s", bitmap[i], i > 0 ? " " : ""); | 
|  | 711 |  | 
|  | 712 | if (add_cr) | 
|  | 713 | len += snprintf(buf + len, max(buf_size - len, 0), "\n"); | 
|  | 714 |  | 
|  | 715 | return len; | 
|  | 716 | } | 
|  | 717 |  | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 718 | #define INPUT_DEV_CAP_ATTR(ev, bm)						\ | 
|  | 719 | static ssize_t input_dev_show_cap_##bm(struct class_device *dev, char *buf)	\ | 
|  | 720 | {										\ | 
|  | 721 | struct input_dev *input_dev = to_input_dev(dev);			\ | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 722 | int len = input_print_bitmap(buf, PAGE_SIZE,				\ | 
|  | 723 | input_dev->bm##bit, ev##_MAX, 1);		\ | 
|  | 724 | return min_t(int, len, PAGE_SIZE);					\ | 
| Dmitry Torokhov | 5c1e9a6 | 2005-09-15 02:01:55 -0500 | [diff] [blame] | 725 | }										\ | 
|  | 726 | static CLASS_DEVICE_ATTR(bm, S_IRUGO, input_dev_show_cap_##bm, NULL); | 
|  | 727 |  | 
|  | 728 | INPUT_DEV_CAP_ATTR(EV, ev); | 
|  | 729 | INPUT_DEV_CAP_ATTR(KEY, key); | 
|  | 730 | INPUT_DEV_CAP_ATTR(REL, rel); | 
|  | 731 | INPUT_DEV_CAP_ATTR(ABS, abs); | 
|  | 732 | INPUT_DEV_CAP_ATTR(MSC, msc); | 
|  | 733 | INPUT_DEV_CAP_ATTR(LED, led); | 
|  | 734 | INPUT_DEV_CAP_ATTR(SND, snd); | 
|  | 735 | INPUT_DEV_CAP_ATTR(FF, ff); | 
|  | 736 | INPUT_DEV_CAP_ATTR(SW, sw); | 
|  | 737 |  | 
|  | 738 | static struct attribute *input_dev_caps_attrs[] = { | 
|  | 739 | &class_device_attr_ev.attr, | 
|  | 740 | &class_device_attr_key.attr, | 
|  | 741 | &class_device_attr_rel.attr, | 
|  | 742 | &class_device_attr_abs.attr, | 
|  | 743 | &class_device_attr_msc.attr, | 
|  | 744 | &class_device_attr_led.attr, | 
|  | 745 | &class_device_attr_snd.attr, | 
|  | 746 | &class_device_attr_ff.attr, | 
|  | 747 | &class_device_attr_sw.attr, | 
|  | 748 | NULL | 
|  | 749 | }; | 
|  | 750 |  | 
|  | 751 | static struct attribute_group input_dev_caps_attr_group = { | 
|  | 752 | .name	= "capabilities", | 
|  | 753 | .attrs	= input_dev_caps_attrs, | 
|  | 754 | }; | 
|  | 755 |  | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 756 | static void input_dev_release(struct class_device *class_dev) | 
|  | 757 | { | 
|  | 758 | struct input_dev *dev = to_input_dev(class_dev); | 
|  | 759 |  | 
| Anssi Hannula | 509ca1a | 2006-07-19 01:40:22 -0400 | [diff] [blame] | 760 | input_ff_destroy(dev); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 761 | kfree(dev); | 
| Anssi Hannula | 509ca1a | 2006-07-19 01:40:22 -0400 | [diff] [blame] | 762 |  | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 763 | module_put(THIS_MODULE); | 
|  | 764 | } | 
|  | 765 |  | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 766 | /* | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 767 | * Input uevent interface - loading event handlers based on | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 768 | * device bitfields. | 
|  | 769 | */ | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 770 | static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index, | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 771 | char *buffer, int buffer_size, int *cur_len, | 
|  | 772 | const char *name, unsigned long *bitmap, int max) | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 773 | { | 
|  | 774 | if (*cur_index >= num_envp - 1) | 
|  | 775 | return -ENOMEM; | 
|  | 776 |  | 
|  | 777 | envp[*cur_index] = buffer + *cur_len; | 
|  | 778 |  | 
|  | 779 | *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name); | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 780 | if (*cur_len >= buffer_size) | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 781 | return -ENOMEM; | 
|  | 782 |  | 
|  | 783 | *cur_len += input_print_bitmap(buffer + *cur_len, | 
|  | 784 | max(buffer_size - *cur_len, 0), | 
| Dmitry Torokhov | 2db6687 | 2006-04-02 00:09:26 -0500 | [diff] [blame] | 785 | bitmap, max, 0) + 1; | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 786 | if (*cur_len > buffer_size) | 
|  | 787 | return -ENOMEM; | 
|  | 788 |  | 
|  | 789 | (*cur_index)++; | 
|  | 790 | return 0; | 
|  | 791 | } | 
|  | 792 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 793 | static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_index, | 
|  | 794 | char *buffer, int buffer_size, int *cur_len, | 
|  | 795 | struct input_dev *dev) | 
|  | 796 | { | 
|  | 797 | if (*cur_index >= num_envp - 1) | 
|  | 798 | return -ENOMEM; | 
|  | 799 |  | 
|  | 800 | envp[*cur_index] = buffer + *cur_len; | 
|  | 801 |  | 
|  | 802 | *cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), | 
|  | 803 | "MODALIAS="); | 
|  | 804 | if (*cur_len >= buffer_size) | 
|  | 805 | return -ENOMEM; | 
|  | 806 |  | 
|  | 807 | *cur_len += input_print_modalias(buffer + *cur_len, | 
|  | 808 | max(buffer_size - *cur_len, 0), | 
|  | 809 | dev, 0) + 1; | 
|  | 810 | if (*cur_len > buffer_size) | 
|  | 811 | return -ENOMEM; | 
|  | 812 |  | 
|  | 813 | (*cur_index)++; | 
|  | 814 | return 0; | 
|  | 815 | } | 
|  | 816 |  | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 817 | #define INPUT_ADD_HOTPLUG_VAR(fmt, val...)				\ | 
|  | 818 | do {								\ | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 819 | int err = add_uevent_var(envp, num_envp, &i,		\ | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 820 | buffer, buffer_size, &len,	\ | 
|  | 821 | fmt, val);			\ | 
|  | 822 | if (err)						\ | 
|  | 823 | return err;					\ | 
|  | 824 | } while (0) | 
|  | 825 |  | 
|  | 826 | #define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max)				\ | 
|  | 827 | do {								\ | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 828 | int err = input_add_uevent_bm_var(envp, num_envp, &i,	\ | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 829 | buffer, buffer_size, &len,	\ | 
|  | 830 | name, bm, max);			\ | 
|  | 831 | if (err)						\ | 
|  | 832 | return err;					\ | 
|  | 833 | } while (0) | 
|  | 834 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 835 | #define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev)				\ | 
|  | 836 | do {								\ | 
|  | 837 | int err = input_add_uevent_modalias_var(envp,		\ | 
|  | 838 | num_envp, &i,			\ | 
|  | 839 | buffer, buffer_size, &len,	\ | 
|  | 840 | dev);				\ | 
|  | 841 | if (err)						\ | 
|  | 842 | return err;					\ | 
|  | 843 | } while (0) | 
|  | 844 |  | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 845 | static int input_dev_uevent(struct class_device *cdev, char **envp, | 
|  | 846 | int num_envp, char *buffer, int buffer_size) | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 847 | { | 
|  | 848 | struct input_dev *dev = to_input_dev(cdev); | 
|  | 849 | int i = 0; | 
|  | 850 | int len = 0; | 
|  | 851 |  | 
|  | 852 | INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x", | 
|  | 853 | dev->id.bustype, dev->id.vendor, | 
|  | 854 | dev->id.product, dev->id.version); | 
|  | 855 | if (dev->name) | 
|  | 856 | INPUT_ADD_HOTPLUG_VAR("NAME=\"%s\"", dev->name); | 
|  | 857 | if (dev->phys) | 
|  | 858 | INPUT_ADD_HOTPLUG_VAR("PHYS=\"%s\"", dev->phys); | 
| Dmitry Torokhov | 08de1f0 | 2005-11-08 21:34:29 -0800 | [diff] [blame] | 859 | if (dev->uniq) | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 860 | INPUT_ADD_HOTPLUG_VAR("UNIQ=\"%s\"", dev->uniq); | 
|  | 861 |  | 
|  | 862 | INPUT_ADD_HOTPLUG_BM_VAR("EV=", dev->evbit, EV_MAX); | 
|  | 863 | if (test_bit(EV_KEY, dev->evbit)) | 
|  | 864 | INPUT_ADD_HOTPLUG_BM_VAR("KEY=", dev->keybit, KEY_MAX); | 
|  | 865 | if (test_bit(EV_REL, dev->evbit)) | 
|  | 866 | INPUT_ADD_HOTPLUG_BM_VAR("REL=", dev->relbit, REL_MAX); | 
|  | 867 | if (test_bit(EV_ABS, dev->evbit)) | 
|  | 868 | INPUT_ADD_HOTPLUG_BM_VAR("ABS=", dev->absbit, ABS_MAX); | 
|  | 869 | if (test_bit(EV_MSC, dev->evbit)) | 
|  | 870 | INPUT_ADD_HOTPLUG_BM_VAR("MSC=", dev->mscbit, MSC_MAX); | 
|  | 871 | if (test_bit(EV_LED, dev->evbit)) | 
|  | 872 | INPUT_ADD_HOTPLUG_BM_VAR("LED=", dev->ledbit, LED_MAX); | 
|  | 873 | if (test_bit(EV_SND, dev->evbit)) | 
|  | 874 | INPUT_ADD_HOTPLUG_BM_VAR("SND=", dev->sndbit, SND_MAX); | 
|  | 875 | if (test_bit(EV_FF, dev->evbit)) | 
|  | 876 | INPUT_ADD_HOTPLUG_BM_VAR("FF=", dev->ffbit, FF_MAX); | 
|  | 877 | if (test_bit(EV_SW, dev->evbit)) | 
|  | 878 | INPUT_ADD_HOTPLUG_BM_VAR("SW=", dev->swbit, SW_MAX); | 
|  | 879 |  | 
| Dmitry Torokhov | ac648a6 | 2006-04-02 00:09:51 -0500 | [diff] [blame] | 880 | INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev); | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 881 |  | 
| Kay Sievers | bd37e5a | 2006-01-05 13:19:55 +0100 | [diff] [blame] | 882 | envp[i] = NULL; | 
| Dmitry Torokhov | a7fadbe | 2005-09-15 02:01:57 -0500 | [diff] [blame] | 883 | return 0; | 
|  | 884 | } | 
|  | 885 |  | 
| Greg Kroah-Hartman | ea9f240 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 886 | struct class input_class = { | 
|  | 887 | .name			= "input", | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 888 | .release		= input_dev_release, | 
| Kay Sievers | 312c004 | 2005-11-16 09:00:00 +0100 | [diff] [blame] | 889 | .uevent			= input_dev_uevent, | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 890 | }; | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 891 | EXPORT_SYMBOL_GPL(input_class); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 892 |  | 
| Dmitry Torokhov | 1447190 | 2006-11-02 23:26:55 -0500 | [diff] [blame] | 893 | /** | 
|  | 894 | * input_allocate_device - allocate memory for new input device | 
|  | 895 | * | 
|  | 896 | * Returns prepared struct input_dev or NULL. | 
|  | 897 | * | 
|  | 898 | * NOTE: Use input_free_device() to free devices that have not been | 
|  | 899 | * registered; input_unregister_device() should be used for already | 
|  | 900 | * registered devices. | 
|  | 901 | */ | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 902 | struct input_dev *input_allocate_device(void) | 
|  | 903 | { | 
|  | 904 | struct input_dev *dev; | 
|  | 905 |  | 
|  | 906 | dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL); | 
|  | 907 | if (dev) { | 
| Greg Kroah-Hartman | ea9f240 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 908 | dev->cdev.class = &input_class; | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 909 | class_device_initialize(&dev->cdev); | 
| Dmitry Torokhov | f60d2b1 | 2006-06-26 01:48:36 -0400 | [diff] [blame] | 910 | mutex_init(&dev->mutex); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 911 | INIT_LIST_HEAD(&dev->h_list); | 
|  | 912 | INIT_LIST_HEAD(&dev->node); | 
| Dmitry Torokhov | 655816e | 2006-09-14 01:32:14 -0400 | [diff] [blame] | 913 |  | 
|  | 914 | __module_get(THIS_MODULE); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 915 | } | 
|  | 916 |  | 
|  | 917 | return dev; | 
|  | 918 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 919 | EXPORT_SYMBOL(input_allocate_device); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 920 |  | 
| Dmitry Torokhov | 1447190 | 2006-11-02 23:26:55 -0500 | [diff] [blame] | 921 | /** | 
|  | 922 | * input_free_device - free memory occupied by input_dev structure | 
|  | 923 | * @dev: input device to free | 
|  | 924 | * | 
|  | 925 | * This function should only be used if input_register_device() | 
|  | 926 | * was not called yet or if it failed. Once device was registered | 
|  | 927 | * use input_unregister_device() and memory will be freed once last | 
|  | 928 | * refrence to the device is dropped. | 
|  | 929 | * | 
|  | 930 | * Device should be allocated by input_allocate_device(). | 
|  | 931 | * | 
|  | 932 | * NOTE: If there are references to the input device then memory | 
|  | 933 | * will not be freed until last reference is dropped. | 
|  | 934 | */ | 
| Dmitry Torokhov | f60d2b1 | 2006-06-26 01:48:36 -0400 | [diff] [blame] | 935 | void input_free_device(struct input_dev *dev) | 
|  | 936 | { | 
|  | 937 | if (dev) { | 
|  | 938 |  | 
|  | 939 | mutex_lock(&dev->mutex); | 
|  | 940 | dev->name = dev->phys = dev->uniq = NULL; | 
|  | 941 | mutex_unlock(&dev->mutex); | 
|  | 942 |  | 
|  | 943 | input_put_device(dev); | 
|  | 944 | } | 
|  | 945 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 946 | EXPORT_SYMBOL(input_free_device); | 
| Dmitry Torokhov | f60d2b1 | 2006-06-26 01:48:36 -0400 | [diff] [blame] | 947 |  | 
| Dmitry Torokhov | 5f94548 | 2005-11-02 22:51:46 -0500 | [diff] [blame] | 948 | int input_register_device(struct input_dev *dev) | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 949 | { | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 950 | static atomic_t input_no = ATOMIC_INIT(0); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 951 | struct input_handle *handle; | 
|  | 952 | struct input_handler *handler; | 
| Dmitry Torokhov | 66e6611 | 2006-09-14 01:31:59 -0400 | [diff] [blame] | 953 | const struct input_device_id *id; | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 954 | const char *path; | 
|  | 955 | int error; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 956 |  | 
| Dmitry Torokhov | 5f94548 | 2005-11-02 22:51:46 -0500 | [diff] [blame] | 957 | set_bit(EV_SYN, dev->evbit); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 958 |  | 
|  | 959 | /* | 
|  | 960 | * If delay and period are pre-set by the driver, then autorepeating | 
|  | 961 | * is handled by the driver itself and we don't do it in input.c. | 
|  | 962 | */ | 
|  | 963 |  | 
|  | 964 | init_timer(&dev->timer); | 
|  | 965 | if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { | 
|  | 966 | dev->timer.data = (long) dev; | 
|  | 967 | dev->timer.function = input_repeat_key; | 
|  | 968 | dev->rep[REP_DELAY] = 250; | 
|  | 969 | dev->rep[REP_PERIOD] = 33; | 
|  | 970 | } | 
|  | 971 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 972 | list_add_tail(&dev->node, &input_dev_list); | 
|  | 973 |  | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 974 | snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id), | 
|  | 975 | "input%ld", (unsigned long) atomic_inc_return(&input_no) - 1); | 
|  | 976 |  | 
|  | 977 | error = class_device_add(&dev->cdev); | 
|  | 978 | if (error) | 
|  | 979 | return error; | 
|  | 980 |  | 
|  | 981 | error = sysfs_create_group(&dev->cdev.kobj, &input_dev_attr_group); | 
|  | 982 | if (error) | 
|  | 983 | goto fail1; | 
|  | 984 |  | 
|  | 985 | error = sysfs_create_group(&dev->cdev.kobj, &input_dev_id_attr_group); | 
|  | 986 | if (error) | 
|  | 987 | goto fail2; | 
|  | 988 |  | 
|  | 989 | error = sysfs_create_group(&dev->cdev.kobj, &input_dev_caps_attr_group); | 
|  | 990 | if (error) | 
|  | 991 | goto fail3; | 
|  | 992 |  | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 993 | path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL); | 
|  | 994 | printk(KERN_INFO "input: %s as %s\n", | 
|  | 995 | dev->name ? dev->name : "Unspecified device", path ? path : "N/A"); | 
|  | 996 | kfree(path); | 
| Greg Kroah-Hartman | 1020402 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 997 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 998 | list_for_each_entry(handler, &input_handler_list, node) | 
|  | 999 | if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) | 
|  | 1000 | if ((id = input_match_device(handler->id_table, dev))) | 
| Dmitry Torokhov | c7e8dc6 | 2006-07-06 00:21:03 -0400 | [diff] [blame] | 1001 | if ((handle = handler->connect(handler, dev, id))) { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1002 | input_link_handle(handle); | 
| Dmitry Torokhov | c7e8dc6 | 2006-07-06 00:21:03 -0400 | [diff] [blame] | 1003 | if (handler->start) | 
|  | 1004 | handler->start(handle); | 
|  | 1005 | } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1006 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1007 | input_wakeup_procfs_readers(); | 
| Dmitry Torokhov | 5f94548 | 2005-11-02 22:51:46 -0500 | [diff] [blame] | 1008 |  | 
|  | 1009 | return 0; | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 1010 |  | 
|  | 1011 | fail3:	sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); | 
|  | 1012 | fail2:	sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); | 
|  | 1013 | fail1:	class_device_del(&dev->cdev); | 
|  | 1014 | return error; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1015 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 1016 | EXPORT_SYMBOL(input_register_device); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1017 |  | 
|  | 1018 | void input_unregister_device(struct input_dev *dev) | 
|  | 1019 | { | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 1020 | struct list_head *node, *next; | 
| Dmitry Torokhov | 6d2750c | 2006-09-10 21:56:06 -0400 | [diff] [blame] | 1021 | int code; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1022 |  | 
| Dmitry Torokhov | 6d2750c | 2006-09-10 21:56:06 -0400 | [diff] [blame] | 1023 | for (code = 0; code <= KEY_MAX; code++) | 
|  | 1024 | if (test_bit(code, dev->key)) | 
|  | 1025 | input_report_key(dev, code, 0); | 
|  | 1026 | input_sync(dev); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1027 |  | 
|  | 1028 | del_timer_sync(&dev->timer); | 
|  | 1029 |  | 
|  | 1030 | list_for_each_safe(node, next, &dev->h_list) { | 
|  | 1031 | struct input_handle * handle = to_handle(node); | 
|  | 1032 | list_del_init(&handle->d_node); | 
|  | 1033 | list_del_init(&handle->h_node); | 
|  | 1034 | handle->handler->disconnect(handle); | 
|  | 1035 | } | 
|  | 1036 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1037 | list_del_init(&dev->node); | 
|  | 1038 |  | 
| Dmitry Torokhov | 5f94548 | 2005-11-02 22:51:46 -0500 | [diff] [blame] | 1039 | sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group); | 
|  | 1040 | sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group); | 
| Dmitry Torokhov | bd0ef23 | 2005-11-20 00:56:31 -0500 | [diff] [blame] | 1041 | sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 1042 |  | 
| Dmitry Torokhov | e7374e4 | 2006-06-27 08:30:31 -0400 | [diff] [blame] | 1043 | class_device_unregister(&dev->cdev); | 
|  | 1044 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1045 | input_wakeup_procfs_readers(); | 
|  | 1046 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 1047 | EXPORT_SYMBOL(input_unregister_device); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1048 |  | 
| Dmitry Torokhov | 4263cf0 | 2006-09-14 01:32:39 -0400 | [diff] [blame] | 1049 | int input_register_handler(struct input_handler *handler) | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1050 | { | 
|  | 1051 | struct input_dev *dev; | 
|  | 1052 | struct input_handle *handle; | 
| Dmitry Torokhov | 66e6611 | 2006-09-14 01:31:59 -0400 | [diff] [blame] | 1053 | const struct input_device_id *id; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1054 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1055 | INIT_LIST_HEAD(&handler->h_list); | 
|  | 1056 |  | 
| Dmitry Torokhov | 4263cf0 | 2006-09-14 01:32:39 -0400 | [diff] [blame] | 1057 | if (handler->fops != NULL) { | 
|  | 1058 | if (input_table[handler->minor >> 5]) | 
|  | 1059 | return -EBUSY; | 
|  | 1060 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1061 | input_table[handler->minor >> 5] = handler; | 
| Dmitry Torokhov | 4263cf0 | 2006-09-14 01:32:39 -0400 | [diff] [blame] | 1062 | } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1063 |  | 
|  | 1064 | list_add_tail(&handler->node, &input_handler_list); | 
|  | 1065 |  | 
|  | 1066 | list_for_each_entry(dev, &input_dev_list, node) | 
|  | 1067 | if (!handler->blacklist || !input_match_device(handler->blacklist, dev)) | 
|  | 1068 | if ((id = input_match_device(handler->id_table, dev))) | 
| Dmitry Torokhov | b6d786d | 2006-07-19 01:08:51 -0400 | [diff] [blame] | 1069 | if ((handle = handler->connect(handler, dev, id))) { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1070 | input_link_handle(handle); | 
| Dmitry Torokhov | b6d786d | 2006-07-19 01:08:51 -0400 | [diff] [blame] | 1071 | if (handler->start) | 
|  | 1072 | handler->start(handle); | 
|  | 1073 | } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1074 |  | 
|  | 1075 | input_wakeup_procfs_readers(); | 
| Dmitry Torokhov | 4263cf0 | 2006-09-14 01:32:39 -0400 | [diff] [blame] | 1076 | return 0; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1077 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 1078 | EXPORT_SYMBOL(input_register_handler); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1079 |  | 
|  | 1080 | void input_unregister_handler(struct input_handler *handler) | 
|  | 1081 | { | 
| Dmitry Torokhov | 1e0afb2 | 2006-06-26 01:48:47 -0400 | [diff] [blame] | 1082 | struct list_head *node, *next; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1083 |  | 
|  | 1084 | list_for_each_safe(node, next, &handler->h_list) { | 
|  | 1085 | struct input_handle * handle = to_handle_h(node); | 
|  | 1086 | list_del_init(&handle->h_node); | 
|  | 1087 | list_del_init(&handle->d_node); | 
|  | 1088 | handler->disconnect(handle); | 
|  | 1089 | } | 
|  | 1090 |  | 
|  | 1091 | list_del_init(&handler->node); | 
|  | 1092 |  | 
|  | 1093 | if (handler->fops != NULL) | 
|  | 1094 | input_table[handler->minor >> 5] = NULL; | 
|  | 1095 |  | 
|  | 1096 | input_wakeup_procfs_readers(); | 
|  | 1097 | } | 
| Dmitry Torokhov | ca56fe0 | 2006-06-26 01:49:21 -0400 | [diff] [blame] | 1098 | EXPORT_SYMBOL(input_unregister_handler); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1099 |  | 
|  | 1100 | static int input_open_file(struct inode *inode, struct file *file) | 
|  | 1101 | { | 
|  | 1102 | struct input_handler *handler = input_table[iminor(inode) >> 5]; | 
| Arjan van de Ven | 99ac48f | 2006-03-28 01:56:41 -0800 | [diff] [blame] | 1103 | const struct file_operations *old_fops, *new_fops = NULL; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1104 | int err; | 
|  | 1105 |  | 
|  | 1106 | /* No load-on-demand here? */ | 
|  | 1107 | if (!handler || !(new_fops = fops_get(handler->fops))) | 
|  | 1108 | return -ENODEV; | 
|  | 1109 |  | 
|  | 1110 | /* | 
|  | 1111 | * That's _really_ odd. Usually NULL ->open means "nothing special", | 
|  | 1112 | * not "no device". Oh, well... | 
|  | 1113 | */ | 
|  | 1114 | if (!new_fops->open) { | 
|  | 1115 | fops_put(new_fops); | 
|  | 1116 | return -ENODEV; | 
|  | 1117 | } | 
|  | 1118 | old_fops = file->f_op; | 
|  | 1119 | file->f_op = new_fops; | 
|  | 1120 |  | 
|  | 1121 | err = new_fops->open(inode, file); | 
|  | 1122 |  | 
|  | 1123 | if (err) { | 
|  | 1124 | fops_put(file->f_op); | 
|  | 1125 | file->f_op = fops_get(old_fops); | 
|  | 1126 | } | 
|  | 1127 | fops_put(old_fops); | 
|  | 1128 | return err; | 
|  | 1129 | } | 
|  | 1130 |  | 
| Arjan van de Ven | 2b8693c | 2007-02-12 00:55:32 -0800 | [diff] [blame] | 1131 | static const struct file_operations input_fops = { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1132 | .owner = THIS_MODULE, | 
|  | 1133 | .open = input_open_file, | 
|  | 1134 | }; | 
|  | 1135 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1136 | static int __init input_init(void) | 
|  | 1137 | { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1138 | int err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1139 |  | 
| Greg Kroah-Hartman | ea9f240 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1140 | err = class_register(&input_class); | 
| Dmitry Torokhov | d19fbe8 | 2005-09-15 02:01:39 -0500 | [diff] [blame] | 1141 | if (err) { | 
|  | 1142 | printk(KERN_ERR "input: unable to register input_dev class\n"); | 
|  | 1143 | return err; | 
|  | 1144 | } | 
|  | 1145 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1146 | err = input_proc_init(); | 
|  | 1147 | if (err) | 
| Greg Kroah-Hartman | b0fdfeb | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1148 | goto fail1; | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1149 |  | 
|  | 1150 | err = register_chrdev(INPUT_MAJOR, "input", &input_fops); | 
|  | 1151 | if (err) { | 
|  | 1152 | printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); | 
| Greg Kroah-Hartman | b0fdfeb | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1153 | goto fail2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1154 | } | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1155 |  | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1156 | return 0; | 
|  | 1157 |  | 
| Greg Kroah-Hartman | b0fdfeb | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1158 | fail2:	input_proc_exit(); | 
| Greg Kroah-Hartman | ea9f240 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1159 | fail1:	class_unregister(&input_class); | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1160 | return err; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1161 | } | 
|  | 1162 |  | 
|  | 1163 | static void __exit input_exit(void) | 
|  | 1164 | { | 
| Dmitry Torokhov | f96b434 | 2005-06-30 00:50:29 -0500 | [diff] [blame] | 1165 | input_proc_exit(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1166 | unregister_chrdev(INPUT_MAJOR, "input"); | 
| Greg Kroah-Hartman | ea9f240 | 2005-10-27 22:25:43 -0700 | [diff] [blame] | 1167 | class_unregister(&input_class); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1168 | } | 
|  | 1169 |  | 
|  | 1170 | subsys_initcall(input_init); | 
|  | 1171 | module_exit(input_exit); |