diff options
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/fw-device-cdev.c | 80 | ||||
-rw-r--r-- | drivers/firewire/fw-device.c | 1 | ||||
-rw-r--r-- | drivers/firewire/fw-device.h | 7 |
3 files changed, 59 insertions, 29 deletions
diff --git a/drivers/firewire/fw-device-cdev.c b/drivers/firewire/fw-device-cdev.c index 54ef27b2adb5..2d84284f7605 100644 --- a/drivers/firewire/fw-device-cdev.c +++ b/drivers/firewire/fw-device-cdev.c | |||
@@ -76,7 +76,6 @@ struct client { | |||
76 | struct list_head request_list; | 76 | struct list_head request_list; |
77 | u32 request_serial; | 77 | u32 request_serial; |
78 | struct list_head event_list; | 78 | struct list_head event_list; |
79 | struct semaphore event_list_sem; | ||
80 | wait_queue_head_t wait; | 79 | wait_queue_head_t wait; |
81 | 80 | ||
82 | struct fw_iso_context *iso_context; | 81 | struct fw_iso_context *iso_context; |
@@ -114,7 +113,6 @@ static int fw_device_op_open(struct inode *inode, struct file *file) | |||
114 | 113 | ||
115 | client->device = fw_device_get(device); | 114 | client->device = fw_device_get(device); |
116 | INIT_LIST_HEAD(&client->event_list); | 115 | INIT_LIST_HEAD(&client->event_list); |
117 | sema_init(&client->event_list_sem, 0); | ||
118 | INIT_LIST_HEAD(&client->handler_list); | 116 | INIT_LIST_HEAD(&client->handler_list); |
119 | INIT_LIST_HEAD(&client->request_list); | 117 | INIT_LIST_HEAD(&client->request_list); |
120 | spin_lock_init(&client->lock); | 118 | spin_lock_init(&client->lock); |
@@ -142,38 +140,41 @@ static void queue_event(struct client *client, struct event *event, | |||
142 | spin_lock_irqsave(&client->lock, flags); | 140 | spin_lock_irqsave(&client->lock, flags); |
143 | 141 | ||
144 | list_add_tail(&event->link, &client->event_list); | 142 | list_add_tail(&event->link, &client->event_list); |
145 | |||
146 | up(&client->event_list_sem); | ||
147 | wake_up_interruptible(&client->wait); | 143 | wake_up_interruptible(&client->wait); |
148 | 144 | ||
149 | spin_unlock_irqrestore(&client->lock, flags); | 145 | spin_unlock_irqrestore(&client->lock, flags); |
150 | } | 146 | } |
151 | 147 | ||
152 | static int dequeue_event(struct client *client, char __user *buffer, size_t count) | 148 | static int |
149 | dequeue_event(struct client *client, char __user *buffer, size_t count) | ||
153 | { | 150 | { |
154 | unsigned long flags; | 151 | unsigned long flags; |
155 | struct event *event; | 152 | struct event *event; |
156 | size_t size, total; | 153 | size_t size, total; |
157 | int i, retval = -EFAULT; | 154 | int i, retval; |
158 | 155 | ||
159 | if (down_interruptible(&client->event_list_sem) < 0) | 156 | retval = wait_event_interruptible(client->wait, |
160 | return -EINTR; | 157 | !list_empty(&client->event_list) || |
158 | fw_device_is_shutdown(client->device)); | ||
159 | if (retval < 0) | ||
160 | return retval; | ||
161 | 161 | ||
162 | spin_lock_irqsave(&client->lock, flags); | 162 | if (list_empty(&client->event_list) && |
163 | fw_device_is_shutdown(client->device)) | ||
164 | return -ENODEV; | ||
163 | 165 | ||
166 | spin_lock_irqsave(&client->lock, flags); | ||
164 | event = container_of(client->event_list.next, struct event, link); | 167 | event = container_of(client->event_list.next, struct event, link); |
165 | list_del(&event->link); | 168 | list_del(&event->link); |
166 | |||
167 | spin_unlock_irqrestore(&client->lock, flags); | 169 | spin_unlock_irqrestore(&client->lock, flags); |
168 | 170 | ||
169 | if (buffer == NULL) | ||
170 | goto out; | ||
171 | |||
172 | total = 0; | 171 | total = 0; |
173 | for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { | 172 | for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) { |
174 | size = min(event->v[i].size, count - total); | 173 | size = min(event->v[i].size, count - total); |
175 | if (copy_to_user(buffer + total, event->v[i].data, size)) | 174 | if (copy_to_user(buffer + total, event->v[i].data, size)) { |
175 | retval = -EFAULT; | ||
176 | goto out; | 176 | goto out; |
177 | } | ||
177 | total += size; | 178 | total += size; |
178 | } | 179 | } |
179 | retval = total; | 180 | retval = total; |
@@ -209,6 +210,22 @@ fill_bus_reset_event(struct fw_cdev_event_bus_reset *event, | |||
209 | } | 210 | } |
210 | 211 | ||
211 | static void | 212 | static void |
213 | for_each_client(struct fw_device *device, | ||
214 | void (*callback)(struct client *client)) | ||
215 | { | ||
216 | struct fw_card *card = device->card; | ||
217 | struct client *c; | ||
218 | unsigned long flags; | ||
219 | |||
220 | spin_lock_irqsave(&card->lock, flags); | ||
221 | |||
222 | list_for_each_entry(c, &device->client_list, link) | ||
223 | callback(c); | ||
224 | |||
225 | spin_unlock_irqrestore(&card->lock, flags); | ||
226 | } | ||
227 | |||
228 | static void | ||
212 | queue_bus_reset_event(struct client *client) | 229 | queue_bus_reset_event(struct client *client) |
213 | { | 230 | { |
214 | struct bus_reset *bus_reset; | 231 | struct bus_reset *bus_reset; |
@@ -228,16 +245,17 @@ queue_bus_reset_event(struct client *client) | |||
228 | 245 | ||
229 | void fw_device_cdev_update(struct fw_device *device) | 246 | void fw_device_cdev_update(struct fw_device *device) |
230 | { | 247 | { |
231 | struct fw_card *card = device->card; | 248 | for_each_client(device, queue_bus_reset_event); |
232 | struct client *c; | 249 | } |
233 | unsigned long flags; | ||
234 | |||
235 | spin_lock_irqsave(&card->lock, flags); | ||
236 | 250 | ||
237 | list_for_each_entry(c, &device->client_list, link) | 251 | static void wake_up_client(struct client *client) |
238 | queue_bus_reset_event(c); | 252 | { |
253 | wake_up_interruptible(&client->wait); | ||
254 | } | ||
239 | 255 | ||
240 | spin_unlock_irqrestore(&card->lock, flags); | 256 | void fw_device_cdev_remove(struct fw_device *device) |
257 | { | ||
258 | for_each_client(device, wake_up_client); | ||
241 | } | 259 | } |
242 | 260 | ||
243 | static int ioctl_get_info(struct client *client, void __user *arg) | 261 | static int ioctl_get_info(struct client *client, void __user *arg) |
@@ -731,8 +749,9 @@ static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma) | |||
731 | static int fw_device_op_release(struct inode *inode, struct file *file) | 749 | static int fw_device_op_release(struct inode *inode, struct file *file) |
732 | { | 750 | { |
733 | struct client *client = file->private_data; | 751 | struct client *client = file->private_data; |
734 | struct address_handler *h, *next; | 752 | struct address_handler *h, *next_h; |
735 | struct request *r, *next_r; | 753 | struct request *r, *next_r; |
754 | struct event *e, *next_e; | ||
736 | unsigned long flags; | 755 | unsigned long flags; |
737 | 756 | ||
738 | if (client->buffer.pages) | 757 | if (client->buffer.pages) |
@@ -741,7 +760,7 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
741 | if (client->iso_context) | 760 | if (client->iso_context) |
742 | fw_iso_context_destroy(client->iso_context); | 761 | fw_iso_context_destroy(client->iso_context); |
743 | 762 | ||
744 | list_for_each_entry_safe(h, next, &client->handler_list, link) { | 763 | list_for_each_entry_safe(h, next_h, &client->handler_list, link) { |
745 | fw_core_remove_address_handler(&h->handler); | 764 | fw_core_remove_address_handler(&h->handler); |
746 | kfree(h); | 765 | kfree(h); |
747 | } | 766 | } |
@@ -755,8 +774,8 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
755 | /* TODO: wait for all transactions to finish so | 774 | /* TODO: wait for all transactions to finish so |
756 | * complete_transaction doesn't try to queue up responses | 775 | * complete_transaction doesn't try to queue up responses |
757 | * after we free client. */ | 776 | * after we free client. */ |
758 | while (!list_empty(&client->event_list)) | 777 | list_for_each_entry_safe(e, next_e, &client->event_list, link) |
759 | dequeue_event(client, NULL, 0); | 778 | kfree(e); |
760 | 779 | ||
761 | spin_lock_irqsave(&client->device->card->lock, flags); | 780 | spin_lock_irqsave(&client->device->card->lock, flags); |
762 | list_del(&client->link); | 781 | list_del(&client->link); |
@@ -771,13 +790,16 @@ static int fw_device_op_release(struct inode *inode, struct file *file) | |||
771 | static unsigned int fw_device_op_poll(struct file *file, poll_table * pt) | 790 | static unsigned int fw_device_op_poll(struct file *file, poll_table * pt) |
772 | { | 791 | { |
773 | struct client *client = file->private_data; | 792 | struct client *client = file->private_data; |
793 | unsigned int mask = 0; | ||
774 | 794 | ||
775 | poll_wait(file, &client->wait, pt); | 795 | poll_wait(file, &client->wait, pt); |
776 | 796 | ||
797 | if (fw_device_is_shutdown(client->device)) | ||
798 | mask |= POLLHUP | POLLERR; | ||
777 | if (!list_empty(&client->event_list)) | 799 | if (!list_empty(&client->event_list)) |
778 | return POLLIN | POLLRDNORM; | 800 | mask |= POLLIN | POLLRDNORM; |
779 | else | 801 | |
780 | return 0; | 802 | return mask; |
781 | } | 803 | } |
782 | 804 | ||
783 | const struct file_operations fw_device_ops = { | 805 | const struct file_operations fw_device_ops = { |
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c index 4ade867db888..4877cdbc58cc 100644 --- a/drivers/firewire/fw-device.c +++ b/drivers/firewire/fw-device.c | |||
@@ -453,6 +453,7 @@ static void fw_device_shutdown(struct work_struct *work) | |||
453 | idr_remove(&fw_device_idr, minor); | 453 | idr_remove(&fw_device_idr, minor); |
454 | up_write(&fw_bus_type.subsys.rwsem); | 454 | up_write(&fw_bus_type.subsys.rwsem); |
455 | 455 | ||
456 | fw_device_cdev_remove(device); | ||
456 | device_remove_file(&device->device, &config_rom_attribute); | 457 | device_remove_file(&device->device, &config_rom_attribute); |
457 | device_for_each_child(&device->device, NULL, shutdown_unit); | 458 | device_for_each_child(&device->device, NULL, shutdown_unit); |
458 | device_unregister(&device->device); | 459 | device_unregister(&device->device); |
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h index 1a3655bea335..ba0e2463e4a7 100644 --- a/drivers/firewire/fw-device.h +++ b/drivers/firewire/fw-device.h | |||
@@ -53,11 +53,18 @@ fw_device(struct device *dev) | |||
53 | return container_of(dev, struct fw_device, device); | 53 | return container_of(dev, struct fw_device, device); |
54 | } | 54 | } |
55 | 55 | ||
56 | static inline int | ||
57 | fw_device_is_shutdown(struct fw_device *device) | ||
58 | { | ||
59 | return atomic_read(&device->state) == FW_DEVICE_SHUTDOWN; | ||
60 | } | ||
61 | |||
56 | struct fw_device *fw_device_get(struct fw_device *device); | 62 | struct fw_device *fw_device_get(struct fw_device *device); |
57 | void fw_device_put(struct fw_device *device); | 63 | void fw_device_put(struct fw_device *device); |
58 | int fw_device_enable_phys_dma(struct fw_device *device); | 64 | int fw_device_enable_phys_dma(struct fw_device *device); |
59 | 65 | ||
60 | void fw_device_cdev_update(struct fw_device *device); | 66 | void fw_device_cdev_update(struct fw_device *device); |
67 | void fw_device_cdev_remove(struct fw_device *device); | ||
61 | 68 | ||
62 | struct fw_device *fw_device_from_devt(dev_t devt); | 69 | struct fw_device *fw_device_from_devt(dev_t devt); |
63 | extern int fw_cdev_major; | 70 | extern int fw_cdev_major; |