diff options
| author | Paul Mackerras <paulus@samba.org> | 2007-05-07 23:37:51 -0400 |
|---|---|---|
| committer | Paul Mackerras <paulus@samba.org> | 2007-05-07 23:37:51 -0400 |
| commit | 02bbc0f09c90cefdb2837605c96a66c5ce4ba2e1 (patch) | |
| tree | 04ef573cd4de095c500c9fc3477f4278c0b36300 /drivers/input/joydev.c | |
| parent | 7487a2245b8841c77ba9db406cf99a483b9334e9 (diff) | |
| parent | 5b94f675f57e4ff16c8fda09088d7480a84dcd91 (diff) | |
Merge branch 'linux-2.6'
Diffstat (limited to 'drivers/input/joydev.c')
| -rw-r--r-- | drivers/input/joydev.c | 187 |
1 files changed, 113 insertions, 74 deletions
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 9f3529ad3fda..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,31 +550,54 @@ 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.kset.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.kset.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)); |
| 555 | joydev->exist = 0; | 594 | joydev->exist = 0; |
| 556 | 595 | ||
| 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 | } |
