diff options
Diffstat (limited to 'drivers/firewire/fw-device-cdev.c')
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index 12471444f1bd..e3c4a52a44a9 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -73,9 +73,11 @@ struct client { | |||
73 | u32 version; | 73 | u32 version; |
74 | struct fw_device *device; | 74 | struct fw_device *device; |
75 | spinlock_t lock; | 75 | spinlock_t lock; |
76 | u32 resource_handle; | ||
76 | struct list_head handler_list; | 77 | struct list_head handler_list; |
77 | struct list_head request_list; | 78 | struct list_head request_list; |
78 | struct list_head transaction_list; | 79 | struct list_head transaction_list; |
80 | struct list_head descriptor_list; | ||
79 | u32 request_serial; | 81 | u32 request_serial; |
80 | struct list_head event_list; | 82 | struct list_head event_list; |
81 | wait_queue_head_t wait; | 83 | wait_queue_head_t wait; |
@@ -119,6 +121,7 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
119 | INIT_LIST_HEAD(&client->handler_list); | 121 | INIT_LIST_HEAD(&client->handler_list); |
120 | INIT_LIST_HEAD(&client->request_list); | 122 | INIT_LIST_HEAD(&client->request_list); |
121 | INIT_LIST_HEAD(&client->transaction_list); | 123 | INIT_LIST_HEAD(&client->transaction_list); |
124 | INIT_LIST_HEAD(&client->descriptor_list); | ||
122 | spin_lock_init(&client->lock); | 125 | spin_lock_init(&client->lock); |
123 | init_waitqueue_head(&client->wait); | 126 | init_waitqueue_head(&client->wait); |
124 | 127 | ||
@@ -542,6 +545,87 @@ static int ioctl_initiate_bus_reset(struct client *client, void __user *arg) | |||
542 | return fw_core_initiate_bus_reset(client->device->card, short_reset); | 545 | return fw_core_initiate_bus_reset(client->device->card, short_reset); |
543 | } | 546 | } |
544 | 547 | ||
548 | struct descriptor { | ||
549 | struct fw_descriptor d; | ||
550 | struct list_head link; | ||
551 | u32 handle; | ||
552 | u32 data[0]; | ||
553 | }; | ||
554 | |||
555 | static int ioctl_add_descriptor(struct client *client, void __user *arg) | ||
556 | { | ||
557 | struct fw_cdev_add_descriptor request; | ||
558 | struct descriptor *descriptor; | ||
559 | unsigned long flags; | ||
560 | int retval; | ||
561 | |||
562 | if (copy_from_user(&request, arg, sizeof request)) | ||
563 | return -EFAULT; | ||
564 | |||
565 | if (request.length > 256) | ||
566 | return -EINVAL; | ||
567 | |||
568 | descriptor = | ||
569 | kmalloc(sizeof *descriptor + request.length * 4, GFP_KERNEL); | ||
570 | if (descriptor == NULL) | ||
571 | return -ENOMEM; | ||
572 | |||
573 | if (copy_from_user(descriptor->data, | ||
574 | u64_to_uptr(request.data), request.length * 4)) { | ||
575 | kfree(descriptor); | ||
576 | return -EFAULT; | ||
577 | } | ||
578 | |||
579 | descriptor->d.length = request.length; | ||
580 | descriptor->d.immediate = request.immediate; | ||
581 | descriptor->d.key = request.key; | ||
582 | descriptor->d.data = descriptor->data; | ||
583 | |||
584 | retval = fw_core_add_descriptor(&descriptor->d); | ||
585 | if (retval < 0) { | ||
586 | kfree(descriptor); | ||
587 | return retval; | ||
588 | } | ||
589 | |||
590 | spin_lock_irqsave(&client->lock, flags); | ||
591 | list_add_tail(&descriptor->link, &client->descriptor_list); | ||
592 | descriptor->handle = client->resource_handle++; | ||
593 | spin_unlock_irqrestore(&client->lock, flags); | ||
594 | |||
595 | request.handle = descriptor->handle; | ||
596 | if (copy_to_user(arg, &request, sizeof request)) | ||
597 | return -EFAULT; | ||
598 | |||
599 | return 0; | ||
600 | } | ||
601 | |||
602 | static int ioctl_remove_descriptor(struct client *client, void __user *arg) | ||
603 | { | ||
604 | struct fw_cdev_remove_descriptor request; | ||
605 | struct descriptor *d; | ||
606 | unsigned long flags; | ||
607 | |||
608 | if (copy_from_user(&request, arg, sizeof request)) | ||
609 | return -EFAULT; | ||
610 | |||
611 | spin_lock_irqsave(&client->lock, flags); | ||
612 | list_for_each_entry(d, &client->descriptor_list, link) { | ||
613 | if (d->handle == request.handle) { | ||
614 | list_del(&d->link); | ||
615 | break; | ||
616 | } | ||
617 | } | ||
618 | spin_unlock_irqrestore(&client->lock, flags); | ||
619 | |||
620 | if (&d->link == &client->descriptor_list) | ||
621 | return -EINVAL; | ||
622 | |||
623 | fw_core_remove_descriptor(&d->d); | ||
624 | kfree(d); | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
545 | static void | 629 | static void |
546 | iso_callback(struct fw_iso_context *context, u32 cycle, | 630 | iso_callback(struct fw_iso_context *context, u32 cycle, |
547 | size_t header_length, void *header, void *data) | 631 | size_t header_length, void *header, void *data) |
@@ -731,6 +815,10 @@ dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg) | |||
731 | return ioctl_send_response(client, arg); | 815 | return ioctl_send_response(client, arg); |
732 | case FW_CDEV_IOC_INITIATE_BUS_RESET: | 816 | case FW_CDEV_IOC_INITIATE_BUS_RESET: |
733 | return ioctl_initiate_bus_reset(client, arg); | 817 | return ioctl_initiate_bus_reset(client, arg); |
818 | case FW_CDEV_IOC_ADD_DESCRIPTOR: | ||
819 | return ioctl_add_descriptor(client, arg); | ||
820 | case FW_CDEV_IOC_REMOVE_DESCRIPTOR: | ||
821 | return ioctl_remove_descriptor(client, arg); | ||
734 | case FW_CDEV_IOC_CREATE_ISO_CONTEXT: | 822 | case FW_CDEV_IOC_CREATE_ISO_CONTEXT: |
735 | return ioctl_create_iso_context(client, arg); | 823 | return ioctl_create_iso_context(client, arg); |
736 | case FW_CDEV_IOC_QUEUE_ISO: | 824 | case FW_CDEV_IOC_QUEUE_ISO: |
@@ -811,6 +899,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
811 | struct request *r, *next_r; | 899 | struct request *r, *next_r; |
812 | struct event *e, *next_e; | 900 | struct event *e, *next_e; |
813 | struct response *t, *next_t; | 901 | struct response *t, *next_t; |
902 | struct descriptor *d, *next_d; | ||
814 | unsigned long flags; | 903 | unsigned long flags; |
815 | 904 | ||
816 | if (client->buffer.pages) | 905 | if (client->buffer.pages) |
@@ -835,6 +924,11 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
835 | kfree(t); | 924 | kfree(t); |
836 | } | 925 | } |
837 | 926 | ||
927 | list_for_each_entry_safe(d, next_d, &client->descriptor_list, link) { | ||
928 | fw_core_remove_descriptor(&d->d); | ||
929 | kfree(d); | ||
930 | } | ||
931 | |||
838 | /* FIXME: We should wait for the async tasklets to stop | 932 | /* FIXME: We should wait for the async tasklets to stop |
839 | * running before freeing the memory. */ | 933 | * running before freeing the memory. */ |
840 | 934 | ||