diff options
Diffstat (limited to 'drivers/input/joydev.c')
-rw-r--r-- | drivers/input/joydev.c | 185 |
1 files changed, 112 insertions, 73 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index d2482e4f71be..9bcc5425049b 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c | |||
@@ -43,7 +43,7 @@ struct joydev { | |||
43 | char name[16]; | 43 | char name[16]; |
44 | struct input_handle handle; | 44 | struct input_handle handle; |
45 | wait_queue_head_t wait; | 45 | wait_queue_head_t wait; |
46 | struct list_head list; | 46 | struct list_head client_list; |
47 | struct js_corr corr[ABS_MAX + 1]; | 47 | struct js_corr corr[ABS_MAX + 1]; |
48 | struct JS_DATA_SAVE_TYPE glue; | 48 | struct JS_DATA_SAVE_TYPE glue; |
49 | int nabs; | 49 | int nabs; |
@@ -55,7 +55,7 @@ struct joydev { | |||
55 | __s16 abs[ABS_MAX + 1]; | 55 | __s16 abs[ABS_MAX + 1]; |
56 | }; | 56 | }; |
57 | 57 | ||
58 | struct joydev_list { | 58 | struct joydev_client { |
59 | struct js_event buffer[JOYDEV_BUFFER_SIZE]; | 59 | struct js_event buffer[JOYDEV_BUFFER_SIZE]; |
60 | int head; | 60 | int head; |
61 | int tail; | 61 | int tail; |
@@ -87,7 +87,7 @@ static int joydev_correct(int value, struct js_corr *corr) | |||
87 | static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) | 87 | static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) |
88 | { | 88 | { |
89 | struct joydev *joydev = handle->private; | 89 | struct joydev *joydev = handle->private; |
90 | struct joydev_list *list; | 90 | struct joydev_client *client; |
91 | struct js_event event; | 91 | struct js_event event; |
92 | 92 | ||
93 | switch (type) { | 93 | switch (type) { |
@@ -115,15 +115,15 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne | |||
115 | 115 | ||
116 | event.time = jiffies_to_msecs(jiffies); | 116 | event.time = jiffies_to_msecs(jiffies); |
117 | 117 | ||
118 | list_for_each_entry(list, &joydev->list, node) { | 118 | list_for_each_entry(client, &joydev->client_list, node) { |
119 | 119 | ||
120 | memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); | 120 | memcpy(client->buffer + client->head, &event, sizeof(struct js_event)); |
121 | 121 | ||
122 | if (list->startup == joydev->nabs + joydev->nkey) | 122 | if (client->startup == joydev->nabs + joydev->nkey) |
123 | if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) | 123 | if (client->tail == (client->head = (client->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) |
124 | list->startup = 0; | 124 | client->startup = 0; |
125 | 125 | ||
126 | kill_fasync(&list->fasync, SIGIO, POLL_IN); | 126 | kill_fasync(&client->fasync, SIGIO, POLL_IN); |
127 | } | 127 | } |
128 | 128 | ||
129 | wake_up_interruptible(&joydev->wait); | 129 | wake_up_interruptible(&joydev->wait); |
@@ -132,9 +132,9 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne | |||
132 | static int joydev_fasync(int fd, struct file *file, int on) | 132 | static int joydev_fasync(int fd, struct file *file, int on) |
133 | { | 133 | { |
134 | int retval; | 134 | int retval; |
135 | struct joydev_list *list = file->private_data; | 135 | struct joydev_client *client = file->private_data; |
136 | 136 | ||
137 | retval = fasync_helper(fd, file, on, &list->fasync); | 137 | retval = fasync_helper(fd, file, on, &client->fasync); |
138 | 138 | ||
139 | return retval < 0 ? retval : 0; | 139 | return retval < 0 ? retval : 0; |
140 | } | 140 | } |
@@ -145,60 +145,73 @@ static void joydev_free(struct joydev *joydev) | |||
145 | kfree(joydev); | 145 | kfree(joydev); |
146 | } | 146 | } |
147 | 147 | ||
148 | static int joydev_release(struct inode * inode, struct file * file) | 148 | static int joydev_release(struct inode *inode, struct file *file) |
149 | { | 149 | { |
150 | struct joydev_list *list = file->private_data; | 150 | struct joydev_client *client = file->private_data; |
151 | struct joydev *joydev = client->joydev; | ||
151 | 152 | ||
152 | joydev_fasync(-1, file, 0); | 153 | joydev_fasync(-1, file, 0); |
153 | 154 | ||
154 | list_del(&list->node); | 155 | list_del(&client->node); |
156 | kfree(client); | ||
155 | 157 | ||
156 | if (!--list->joydev->open) { | 158 | if (!--joydev->open) { |
157 | if (list->joydev->exist) | 159 | if (joydev->exist) |
158 | input_close_device(&list->joydev->handle); | 160 | input_close_device(&joydev->handle); |
159 | else | 161 | else |
160 | joydev_free(list->joydev); | 162 | joydev_free(joydev); |
161 | } | 163 | } |
162 | 164 | ||
163 | kfree(list); | ||
164 | return 0; | 165 | return 0; |
165 | } | 166 | } |
166 | 167 | ||
167 | static int joydev_open(struct inode *inode, struct file *file) | 168 | static int joydev_open(struct inode *inode, struct file *file) |
168 | { | 169 | { |
169 | struct joydev_list *list; | 170 | struct joydev_client *client; |
171 | struct joydev *joydev; | ||
170 | int i = iminor(inode) - JOYDEV_MINOR_BASE; | 172 | int i = iminor(inode) - JOYDEV_MINOR_BASE; |
173 | int error; | ||
174 | |||
175 | if (i >= JOYDEV_MINORS) | ||
176 | return -ENODEV; | ||
171 | 177 | ||
172 | if (i >= JOYDEV_MINORS || !joydev_table[i]) | 178 | joydev = joydev_table[i]; |
179 | if (!joydev || !joydev->exist) | ||
173 | return -ENODEV; | 180 | return -ENODEV; |
174 | 181 | ||
175 | if (!(list = kzalloc(sizeof(struct joydev_list), GFP_KERNEL))) | 182 | client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); |
183 | if (!client) | ||
176 | return -ENOMEM; | 184 | return -ENOMEM; |
177 | 185 | ||
178 | list->joydev = joydev_table[i]; | 186 | client->joydev = joydev; |
179 | list_add_tail(&list->node, &joydev_table[i]->list); | 187 | list_add_tail(&client->node, &joydev->client_list); |
180 | file->private_data = list; | ||
181 | 188 | ||
182 | if (!list->joydev->open++) | 189 | if (!joydev->open++ && joydev->exist) { |
183 | if (list->joydev->exist) | 190 | error = input_open_device(&joydev->handle); |
184 | input_open_device(&list->joydev->handle); | 191 | if (error) { |
192 | list_del(&client->node); | ||
193 | kfree(client); | ||
194 | return error; | ||
195 | } | ||
196 | } | ||
185 | 197 | ||
198 | file->private_data = client; | ||
186 | return 0; | 199 | return 0; |
187 | } | 200 | } |
188 | 201 | ||
189 | static ssize_t joydev_write(struct file * file, const char __user * buffer, size_t count, loff_t *ppos) | 202 | static ssize_t joydev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) |
190 | { | 203 | { |
191 | return -EINVAL; | 204 | return -EINVAL; |
192 | } | 205 | } |
193 | 206 | ||
194 | static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) | 207 | static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) |
195 | { | 208 | { |
196 | struct joydev_list *list = file->private_data; | 209 | struct joydev_client *client = file->private_data; |
197 | struct joydev *joydev = list->joydev; | 210 | struct joydev *joydev = client->joydev; |
198 | struct input_dev *input = joydev->handle.dev; | 211 | struct input_dev *input = joydev->handle.dev; |
199 | int retval = 0; | 212 | int retval = 0; |
200 | 213 | ||
201 | if (!list->joydev->exist) | 214 | if (!joydev->exist) |
202 | return -ENODEV; | 215 | return -ENODEV; |
203 | 216 | ||
204 | if (count < sizeof(struct js_event)) | 217 | if (count < sizeof(struct js_event)) |
@@ -217,56 +230,55 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo | |||
217 | if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) | 230 | if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) |
218 | return -EFAULT; | 231 | return -EFAULT; |
219 | 232 | ||
220 | list->startup = 0; | 233 | client->startup = 0; |
221 | list->tail = list->head; | 234 | client->tail = client->head; |
222 | 235 | ||
223 | return sizeof(struct JS_DATA_TYPE); | 236 | return sizeof(struct JS_DATA_TYPE); |
224 | } | 237 | } |
225 | 238 | ||
226 | if (list->startup == joydev->nabs + joydev->nkey && | 239 | if (client->startup == joydev->nabs + joydev->nkey && |
227 | list->head == list->tail && (file->f_flags & O_NONBLOCK)) | 240 | client->head == client->tail && (file->f_flags & O_NONBLOCK)) |
228 | return -EAGAIN; | 241 | return -EAGAIN; |
229 | 242 | ||
230 | retval = wait_event_interruptible(list->joydev->wait, | 243 | retval = wait_event_interruptible(joydev->wait, |
231 | !list->joydev->exist || | 244 | !joydev->exist || |
232 | list->startup < joydev->nabs + joydev->nkey || | 245 | client->startup < joydev->nabs + joydev->nkey || |
233 | list->head != list->tail); | 246 | client->head != client->tail); |
234 | |||
235 | if (retval) | 247 | if (retval) |
236 | return retval; | 248 | return retval; |
237 | 249 | ||
238 | if (!list->joydev->exist) | 250 | if (!joydev->exist) |
239 | return -ENODEV; | 251 | return -ENODEV; |
240 | 252 | ||
241 | while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { | 253 | while (client->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { |
242 | 254 | ||
243 | struct js_event event; | 255 | struct js_event event; |
244 | 256 | ||
245 | event.time = jiffies_to_msecs(jiffies); | 257 | event.time = jiffies_to_msecs(jiffies); |
246 | 258 | ||
247 | if (list->startup < joydev->nkey) { | 259 | if (client->startup < joydev->nkey) { |
248 | event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; | 260 | event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; |
249 | event.number = list->startup; | 261 | event.number = client->startup; |
250 | event.value = !!test_bit(joydev->keypam[event.number], input->key); | 262 | event.value = !!test_bit(joydev->keypam[event.number], input->key); |
251 | } else { | 263 | } else { |
252 | event.type = JS_EVENT_AXIS | JS_EVENT_INIT; | 264 | event.type = JS_EVENT_AXIS | JS_EVENT_INIT; |
253 | event.number = list->startup - joydev->nkey; | 265 | event.number = client->startup - joydev->nkey; |
254 | event.value = joydev->abs[event.number]; | 266 | event.value = joydev->abs[event.number]; |
255 | } | 267 | } |
256 | 268 | ||
257 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) | 269 | if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) |
258 | return -EFAULT; | 270 | return -EFAULT; |
259 | 271 | ||
260 | list->startup++; | 272 | client->startup++; |
261 | retval += sizeof(struct js_event); | 273 | retval += sizeof(struct js_event); |
262 | } | 274 | } |
263 | 275 | ||
264 | while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { | 276 | while (client->head != client->tail && retval + sizeof(struct js_event) <= count) { |
265 | 277 | ||
266 | if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) | 278 | if (copy_to_user(buf + retval, client->buffer + client->tail, sizeof(struct js_event))) |
267 | return -EFAULT; | 279 | return -EFAULT; |
268 | 280 | ||
269 | list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); | 281 | client->tail = (client->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); |
270 | retval += sizeof(struct js_event); | 282 | retval += sizeof(struct js_event); |
271 | } | 283 | } |
272 | 284 | ||
@@ -276,11 +288,12 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo | |||
276 | /* No kernel lock - fine */ | 288 | /* No kernel lock - fine */ |
277 | static unsigned int joydev_poll(struct file *file, poll_table *wait) | 289 | static unsigned int joydev_poll(struct file *file, poll_table *wait) |
278 | { | 290 | { |
279 | struct joydev_list *list = file->private_data; | 291 | struct joydev_client *client = file->private_data; |
292 | struct joydev *joydev = client->joydev; | ||
280 | 293 | ||
281 | poll_wait(file, &list->joydev->wait, wait); | 294 | poll_wait(file, &joydev->wait, wait); |
282 | return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ? | 295 | return ((client->head != client->tail || client->startup < joydev->nabs + joydev->nkey) ? |
283 | (POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR)); | 296 | (POLLIN | POLLRDNORM) : 0) | (joydev->exist ? 0 : (POLLHUP | POLLERR)); |
284 | } | 297 | } |
285 | 298 | ||
286 | static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) | 299 | static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __user *argp) |
@@ -374,8 +387,8 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u | |||
374 | #ifdef CONFIG_COMPAT | 387 | #ifdef CONFIG_COMPAT |
375 | static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 388 | static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
376 | { | 389 | { |
377 | struct joydev_list *list = file->private_data; | 390 | struct joydev_client *client = file->private_data; |
378 | struct joydev *joydev = list->joydev; | 391 | struct joydev *joydev = client->joydev; |
379 | void __user *argp = (void __user *)arg; | 392 | void __user *argp = (void __user *)arg; |
380 | s32 tmp32; | 393 | s32 tmp32; |
381 | struct JS_DATA_SAVE_TYPE_32 ds32; | 394 | struct JS_DATA_SAVE_TYPE_32 ds32; |
@@ -428,8 +441,8 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo | |||
428 | 441 | ||
429 | static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) | 442 | static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
430 | { | 443 | { |
431 | struct joydev_list *list = file->private_data; | 444 | struct joydev_client *client = file->private_data; |
432 | struct joydev *joydev = list->joydev; | 445 | struct joydev *joydev = client->joydev; |
433 | void __user *argp = (void __user *)arg; | 446 | void __user *argp = (void __user *)arg; |
434 | 447 | ||
435 | if (!joydev->exist) | 448 | if (!joydev->exist) |
@@ -465,23 +478,26 @@ static const struct file_operations joydev_fops = { | |||
465 | .fasync = joydev_fasync, | 478 | .fasync = joydev_fasync, |
466 | }; | 479 | }; |
467 | 480 | ||
468 | static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, | 481 | static int joydev_connect(struct input_handler *handler, struct input_dev *dev, |
469 | const struct input_device_id *id) | 482 | const struct input_device_id *id) |
470 | { | 483 | { |
471 | struct joydev *joydev; | 484 | struct joydev *joydev; |
472 | struct class_device *cdev; | 485 | struct class_device *cdev; |
486 | dev_t devt; | ||
473 | int i, j, t, minor; | 487 | int i, j, t, minor; |
488 | int error; | ||
474 | 489 | ||
475 | for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); | 490 | for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); |
476 | if (minor == JOYDEV_MINORS) { | 491 | if (minor == JOYDEV_MINORS) { |
477 | printk(KERN_ERR "joydev: no more free joydev devices\n"); | 492 | printk(KERN_ERR "joydev: no more free joydev devices\n"); |
478 | return NULL; | 493 | return -ENFILE; |
479 | } | 494 | } |
480 | 495 | ||
481 | if (!(joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL))) | 496 | joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); |
482 | return NULL; | 497 | if (!joydev) |
498 | return -ENOMEM; | ||
483 | 499 | ||
484 | INIT_LIST_HEAD(&joydev->list); | 500 | INIT_LIST_HEAD(&joydev->client_list); |
485 | init_waitqueue_head(&joydev->wait); | 501 | init_waitqueue_head(&joydev->wait); |
486 | 502 | ||
487 | joydev->minor = minor; | 503 | joydev->minor = minor; |
@@ -534,21 +550,44 @@ static struct input_handle *joydev_connect(struct input_handler *handler, struct | |||
534 | 550 | ||
535 | joydev_table[minor] = joydev; | 551 | joydev_table[minor] = joydev; |
536 | 552 | ||
537 | cdev = class_device_create(&input_class, &dev->cdev, | 553 | devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), |
538 | MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor), | 554 | |
539 | dev->cdev.dev, joydev->name); | 555 | cdev = class_device_create(&input_class, &dev->cdev, devt, |
556 | dev->cdev.dev, joydev->name); | ||
557 | if (IS_ERR(cdev)) { | ||
558 | error = PTR_ERR(cdev); | ||
559 | goto err_free_joydev; | ||
560 | } | ||
540 | 561 | ||
541 | /* temporary symlink to keep userspace happy */ | 562 | /* temporary symlink to keep userspace happy */ |
542 | sysfs_create_link(&input_class.subsys.kobj, &cdev->kobj, | 563 | error = sysfs_create_link(&input_class.subsys.kobj, |
543 | joydev->name); | 564 | &cdev->kobj, joydev->name); |
565 | if (error) | ||
566 | goto err_cdev_destroy; | ||
567 | |||
568 | error = input_register_handle(&joydev->handle); | ||
569 | if (error) | ||
570 | goto err_remove_link; | ||
571 | |||
572 | return 0; | ||
544 | 573 | ||
545 | return &joydev->handle; | 574 | err_remove_link: |
575 | sysfs_remove_link(&input_class.subsys.kobj, joydev->name); | ||
576 | err_cdev_destroy: | ||
577 | class_device_destroy(&input_class, devt); | ||
578 | err_free_joydev: | ||
579 | joydev_table[minor] = NULL; | ||
580 | kfree(joydev); | ||
581 | return error; | ||
546 | } | 582 | } |
547 | 583 | ||
584 | |||
548 | static void joydev_disconnect(struct input_handle *handle) | 585 | static void joydev_disconnect(struct input_handle *handle) |
549 | { | 586 | { |
550 | struct joydev *joydev = handle->private; | 587 | struct joydev *joydev = handle->private; |
551 | struct joydev_list *list; | 588 | struct joydev_client *client; |
589 | |||
590 | input_unregister_handle(handle); | ||
552 | 591 | ||
553 | sysfs_remove_link(&input_class.subsys.kobj, joydev->name); | 592 | sysfs_remove_link(&input_class.subsys.kobj, joydev->name); |
554 | class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); | 593 | class_device_destroy(&input_class, MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + joydev->minor)); |
@@ -557,8 +596,8 @@ static void joydev_disconnect(struct input_handle *handle) | |||
557 | if (joydev->open) { | 596 | if (joydev->open) { |
558 | input_close_device(handle); | 597 | input_close_device(handle); |
559 | wake_up_interruptible(&joydev->wait); | 598 | wake_up_interruptible(&joydev->wait); |
560 | list_for_each_entry(list, &joydev->list, node) | 599 | list_for_each_entry(client, &joydev->client_list, node) |
561 | kill_fasync(&list->fasync, SIGIO, POLL_HUP); | 600 | kill_fasync(&client->fasync, SIGIO, POLL_HUP); |
562 | } else | 601 | } else |
563 | joydev_free(joydev); | 602 | joydev_free(joydev); |
564 | } | 603 | } |