diff options
| -rw-r--r-- | drivers/firewire/nosy.c | 111 |
1 files changed, 82 insertions, 29 deletions
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c index ccf9c461bd86..edd729aafeca 100644 --- a/drivers/firewire/nosy.c +++ b/drivers/firewire/nosy.c | |||
| @@ -23,8 +23,10 @@ | |||
| 23 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
| 24 | #include <linux/io.h> | 24 | #include <linux/io.h> |
| 25 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
| 26 | #include <linux/kref.h> | ||
| 26 | #include <linux/miscdevice.h> | 27 | #include <linux/miscdevice.h> |
| 27 | #include <linux/module.h> | 28 | #include <linux/module.h> |
| 29 | #include <linux/mutex.h> | ||
| 28 | #include <linux/pci.h> | 30 | #include <linux/pci.h> |
| 29 | #include <linux/poll.h> | 31 | #include <linux/poll.h> |
| 30 | #include <linux/sched.h> /* required for linux/wait.h */ | 32 | #include <linux/sched.h> /* required for linux/wait.h */ |
| @@ -104,8 +106,30 @@ struct pcilynx { | |||
| 104 | struct list_head client_list; | 106 | struct list_head client_list; |
| 105 | 107 | ||
| 106 | struct miscdevice misc; | 108 | struct miscdevice misc; |
| 109 | struct list_head link; | ||
| 110 | struct kref kref; | ||
| 107 | }; | 111 | }; |
| 108 | 112 | ||
| 113 | static inline struct pcilynx * | ||
| 114 | lynx_get(struct pcilynx *lynx) | ||
| 115 | { | ||
| 116 | kref_get(&lynx->kref); | ||
| 117 | |||
| 118 | return lynx; | ||
| 119 | } | ||
| 120 | |||
| 121 | static void | ||
| 122 | lynx_release(struct kref *kref) | ||
| 123 | { | ||
| 124 | kfree(container_of(kref, struct pcilynx, kref)); | ||
| 125 | } | ||
| 126 | |||
| 127 | static inline void | ||
| 128 | lynx_put(struct pcilynx *lynx) | ||
| 129 | { | ||
| 130 | kref_put(&lynx->kref, lynx_release); | ||
| 131 | } | ||
| 132 | |||
| 109 | struct client { | 133 | struct client { |
| 110 | struct pcilynx *lynx; | 134 | struct pcilynx *lynx; |
| 111 | u32 tcode_mask; | 135 | u32 tcode_mask; |
| @@ -113,8 +137,8 @@ struct client { | |||
| 113 | struct list_head link; | 137 | struct list_head link; |
| 114 | }; | 138 | }; |
| 115 | 139 | ||
| 116 | #define MAX_MINORS 64 | 140 | static DEFINE_MUTEX(card_mutex); |
| 117 | static struct pcilynx *minors[MAX_MINORS]; | 141 | static LIST_HEAD(card_list); |
| 118 | 142 | ||
| 119 | static int | 143 | static int |
| 120 | packet_buffer_init(struct packet_buffer *buffer, size_t capacity) | 144 | packet_buffer_init(struct packet_buffer *buffer, size_t capacity) |
| @@ -139,15 +163,20 @@ packet_buffer_destroy(struct packet_buffer *buffer) | |||
| 139 | } | 163 | } |
| 140 | 164 | ||
| 141 | static int | 165 | static int |
| 142 | packet_buffer_get(struct packet_buffer *buffer, void *data, size_t user_length) | 166 | packet_buffer_get(struct client *client, void *data, size_t user_length) |
| 143 | { | 167 | { |
| 168 | struct packet_buffer *buffer = &client->buffer; | ||
| 144 | size_t length; | 169 | size_t length; |
| 145 | char *end; | 170 | char *end; |
| 146 | 171 | ||
| 147 | if (wait_event_interruptible(buffer->wait, | 172 | if (wait_event_interruptible(buffer->wait, |
| 148 | atomic_read(&buffer->size) > 0)) | 173 | atomic_read(&buffer->size) > 0) || |
| 174 | list_empty(&client->lynx->link)) | ||
| 149 | return -ERESTARTSYS; | 175 | return -ERESTARTSYS; |
| 150 | 176 | ||
| 177 | if (atomic_read(&buffer->size) == 0) | ||
| 178 | return -ENODEV; | ||
| 179 | |||
| 151 | /* FIXME: Check length <= user_length. */ | 180 | /* FIXME: Check length <= user_length. */ |
| 152 | 181 | ||
| 153 | end = buffer->data + buffer->capacity; | 182 | end = buffer->data + buffer->capacity; |
| @@ -265,39 +294,52 @@ nosy_open(struct inode *inode, struct file *file) | |||
| 265 | { | 294 | { |
| 266 | int minor = iminor(inode); | 295 | int minor = iminor(inode); |
| 267 | struct client *client; | 296 | struct client *client; |
| 268 | 297 | struct pcilynx *tmp, *lynx = NULL; | |
| 269 | if (minor > MAX_MINORS || minors[minor] == NULL) | 298 | |
| 299 | mutex_lock(&card_mutex); | ||
| 300 | list_for_each_entry(tmp, &card_list, link) | ||
| 301 | if (tmp->misc.minor == minor) { | ||
| 302 | lynx = lynx_get(tmp); | ||
| 303 | break; | ||
| 304 | } | ||
| 305 | mutex_unlock(&card_mutex); | ||
| 306 | if (lynx == NULL) | ||
| 270 | return -ENODEV; | 307 | return -ENODEV; |
| 271 | 308 | ||
| 272 | client = kmalloc(sizeof *client, GFP_KERNEL); | 309 | client = kmalloc(sizeof *client, GFP_KERNEL); |
| 273 | if (client == NULL) | 310 | if (client == NULL) |
| 274 | return -ENOMEM; | 311 | goto fail; |
| 275 | 312 | ||
| 276 | client->tcode_mask = ~0; | 313 | client->tcode_mask = ~0; |
| 277 | client->lynx = minors[minor]; | 314 | client->lynx = lynx; |
| 278 | INIT_LIST_HEAD(&client->link); | 315 | INIT_LIST_HEAD(&client->link); |
| 279 | 316 | ||
| 280 | if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) { | 317 | if (packet_buffer_init(&client->buffer, 128 * 1024) < 0) |
| 281 | kfree(client); | 318 | goto fail; |
| 282 | return -ENOMEM; | ||
| 283 | } | ||
| 284 | 319 | ||
| 285 | file->private_data = client; | 320 | file->private_data = client; |
| 286 | 321 | ||
| 287 | return 0; | 322 | return 0; |
| 323 | fail: | ||
| 324 | kfree(client); | ||
| 325 | lynx_put(lynx); | ||
| 326 | |||
| 327 | return -ENOMEM; | ||
| 288 | } | 328 | } |
| 289 | 329 | ||
| 290 | static int | 330 | static int |
| 291 | nosy_release(struct inode *inode, struct file *file) | 331 | nosy_release(struct inode *inode, struct file *file) |
| 292 | { | 332 | { |
| 293 | struct client *client = file->private_data; | 333 | struct client *client = file->private_data; |
| 334 | struct pcilynx *lynx = client->lynx; | ||
| 294 | 335 | ||
| 295 | spin_lock_irq(&client->lynx->client_list_lock); | 336 | spin_lock_irq(&lynx->client_list_lock); |
| 296 | list_del_init(&client->link); | 337 | list_del_init(&client->link); |
| 297 | spin_unlock_irq(&client->lynx->client_list_lock); | 338 | spin_unlock_irq(&lynx->client_list_lock); |
| 298 | 339 | ||
| 299 | packet_buffer_destroy(&client->buffer); | 340 | packet_buffer_destroy(&client->buffer); |
| 300 | kfree(client); | 341 | kfree(client); |
| 342 | lynx_put(lynx); | ||
| 301 | 343 | ||
| 302 | return 0; | 344 | return 0; |
| 303 | } | 345 | } |
| @@ -306,13 +348,17 @@ static unsigned int | |||
| 306 | nosy_poll(struct file *file, poll_table *pt) | 348 | nosy_poll(struct file *file, poll_table *pt) |
| 307 | { | 349 | { |
| 308 | struct client *client = file->private_data; | 350 | struct client *client = file->private_data; |
| 351 | unsigned int ret = 0; | ||
| 309 | 352 | ||
| 310 | poll_wait(file, &client->buffer.wait, pt); | 353 | poll_wait(file, &client->buffer.wait, pt); |
| 311 | 354 | ||
| 312 | if (atomic_read(&client->buffer.size) > 0) | 355 | if (atomic_read(&client->buffer.size) > 0) |
| 313 | return POLLIN | POLLRDNORM; | 356 | ret = POLLIN | POLLRDNORM; |
| 314 | else | 357 | |
| 315 | return 0; | 358 | if (list_empty(&client->lynx->link)) |
| 359 | ret |= POLLHUP; | ||
| 360 | |||
| 361 | return ret; | ||
| 316 | } | 362 | } |
| 317 | 363 | ||
| 318 | static ssize_t | 364 | static ssize_t |
| @@ -320,7 +366,7 @@ nosy_read(struct file *file, char *buffer, size_t count, loff_t *offset) | |||
| 320 | { | 366 | { |
| 321 | struct client *client = file->private_data; | 367 | struct client *client = file->private_data; |
| 322 | 368 | ||
| 323 | return packet_buffer_get(&client->buffer, buffer, count); | 369 | return packet_buffer_get(client, buffer, count); |
| 324 | } | 370 | } |
| 325 | 371 | ||
| 326 | static long | 372 | static long |
| @@ -479,16 +525,22 @@ irq_handler(int irq, void *device) | |||
| 479 | static void | 525 | static void |
| 480 | remove_card(struct pci_dev *dev) | 526 | remove_card(struct pci_dev *dev) |
| 481 | { | 527 | { |
| 482 | struct pcilynx *lynx; | 528 | struct pcilynx *lynx = pci_get_drvdata(dev); |
| 529 | struct client *client; | ||
| 483 | 530 | ||
| 484 | lynx = pci_get_drvdata(dev); | 531 | mutex_lock(&card_mutex); |
| 485 | if (!lynx) | 532 | list_del_init(&lynx->link); |
| 486 | return; | 533 | misc_deregister(&lynx->misc); |
| 487 | pci_set_drvdata(dev, NULL); | 534 | mutex_unlock(&card_mutex); |
| 488 | 535 | ||
| 489 | reg_write(lynx, PCI_INT_ENABLE, 0); | 536 | reg_write(lynx, PCI_INT_ENABLE, 0); |
| 490 | free_irq(lynx->pci_device->irq, lynx); | 537 | free_irq(lynx->pci_device->irq, lynx); |
| 491 | 538 | ||
| 539 | spin_lock_irq(&lynx->client_list_lock); | ||
| 540 | list_for_each_entry(client, &lynx->client_list, link) | ||
| 541 | wake_up_interruptible(&client->buffer.wait); | ||
| 542 | spin_unlock_irq(&lynx->client_list_lock); | ||
| 543 | |||
| 492 | pci_free_consistent(lynx->pci_device, sizeof(struct pcl), | 544 | pci_free_consistent(lynx->pci_device, sizeof(struct pcl), |
| 493 | lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); | 545 | lynx->rcv_start_pcl, lynx->rcv_start_pcl_bus); |
| 494 | pci_free_consistent(lynx->pci_device, sizeof(struct pcl), | 546 | pci_free_consistent(lynx->pci_device, sizeof(struct pcl), |
| @@ -498,11 +550,7 @@ remove_card(struct pci_dev *dev) | |||
| 498 | 550 | ||
| 499 | iounmap(lynx->registers); | 551 | iounmap(lynx->registers); |
| 500 | pci_disable_device(dev); | 552 | pci_disable_device(dev); |
| 501 | 553 | lynx_put(lynx); | |
| 502 | minors[lynx->misc.minor] = NULL; | ||
| 503 | misc_deregister(&lynx->misc); | ||
| 504 | |||
| 505 | kfree(lynx); | ||
| 506 | } | 554 | } |
| 507 | 555 | ||
| 508 | #define RCV_BUFFER_SIZE (16 * 1024) | 556 | #define RCV_BUFFER_SIZE (16 * 1024) |
| @@ -536,6 +584,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) | |||
| 536 | 584 | ||
| 537 | spin_lock_init(&lynx->client_list_lock); | 585 | spin_lock_init(&lynx->client_list_lock); |
| 538 | INIT_LIST_HEAD(&lynx->client_list); | 586 | INIT_LIST_HEAD(&lynx->client_list); |
| 587 | kref_init(&lynx->kref); | ||
| 539 | 588 | ||
| 540 | lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), | 589 | lynx->registers = ioremap_nocache(pci_resource_start(dev, 0), |
| 541 | PCILYNX_MAX_REGISTER); | 590 | PCILYNX_MAX_REGISTER); |
| @@ -619,12 +668,16 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused) | |||
| 619 | lynx->misc.minor = MISC_DYNAMIC_MINOR; | 668 | lynx->misc.minor = MISC_DYNAMIC_MINOR; |
| 620 | lynx->misc.name = "nosy"; | 669 | lynx->misc.name = "nosy"; |
| 621 | lynx->misc.fops = &nosy_ops; | 670 | lynx->misc.fops = &nosy_ops; |
| 671 | |||
| 672 | mutex_lock(&card_mutex); | ||
| 622 | ret = misc_register(&lynx->misc); | 673 | ret = misc_register(&lynx->misc); |
| 623 | if (ret) { | 674 | if (ret) { |
| 624 | error("Failed to register misc char device\n"); | 675 | error("Failed to register misc char device\n"); |
| 676 | mutex_unlock(&card_mutex); | ||
| 625 | goto fail_free_irq; | 677 | goto fail_free_irq; |
| 626 | } | 678 | } |
| 627 | minors[lynx->misc.minor] = lynx; | 679 | list_add_tail(&lynx->link, &card_list); |
| 680 | mutex_unlock(&card_mutex); | ||
| 628 | 681 | ||
| 629 | notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); | 682 | notify("Initialized PCILynx IEEE1394 card, irq=%d\n", dev->irq); |
| 630 | 683 | ||
