diff options
author | Dominik Brodowski <linux@dominikbrodowski.net> | 2006-01-10 14:50:39 -0500 |
---|---|---|
committer | Dominik Brodowski <linux@dominikbrodowski.net> | 2006-03-31 10:02:06 -0500 |
commit | 360b65b95bae96f854a2413093ee9b79c31203ae (patch) | |
tree | ff5f2dea6055c9616fdec44b7309fc81b8f116ed /drivers/pcmcia/ds.c | |
parent | 855cdf134dfcf2ecb92ac4ad675cf655d8ceb678 (diff) |
[PATCH] pcmcia: make config_t independent, add reference counting
Handle config_t structs independent of struct pcmcia_socket, and add
reference counting for them.
Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net>
Diffstat (limited to 'drivers/pcmcia/ds.c')
-rw-r--r-- | drivers/pcmcia/ds.c | 70 |
1 files changed, 43 insertions, 27 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 5166f00cfe3a..37ba1246c282 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/workqueue.h> | 23 | #include <linux/workqueue.h> |
24 | #include <linux/crc32.h> | 24 | #include <linux/crc32.h> |
25 | #include <linux/firmware.h> | 25 | #include <linux/firmware.h> |
26 | #include <linux/kref.h> | ||
26 | 27 | ||
27 | #define IN_CARD_SERVICES | 28 | #define IN_CARD_SERVICES |
28 | #include <pcmcia/cs_types.h> | 29 | #include <pcmcia/cs_types.h> |
@@ -343,12 +344,19 @@ void pcmcia_put_dev(struct pcmcia_device *p_dev) | |||
343 | put_device(&p_dev->dev); | 344 | put_device(&p_dev->dev); |
344 | } | 345 | } |
345 | 346 | ||
347 | static void pcmcia_release_function(struct kref *ref) | ||
348 | { | ||
349 | struct config_t *c = container_of(ref, struct config_t, ref); | ||
350 | kfree(c); | ||
351 | } | ||
352 | |||
346 | static void pcmcia_release_dev(struct device *dev) | 353 | static void pcmcia_release_dev(struct device *dev) |
347 | { | 354 | { |
348 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); | 355 | struct pcmcia_device *p_dev = to_pcmcia_dev(dev); |
349 | ds_dbg(1, "releasing dev %p\n", p_dev); | 356 | ds_dbg(1, "releasing dev %p\n", p_dev); |
350 | pcmcia_put_socket(p_dev->socket); | 357 | pcmcia_put_socket(p_dev->socket); |
351 | kfree(p_dev->devname); | 358 | kfree(p_dev->devname); |
359 | kref_put(&p_dev->function_config->ref, pcmcia_release_function); | ||
352 | kfree(p_dev); | 360 | kfree(p_dev); |
353 | } | 361 | } |
354 | 362 | ||
@@ -377,30 +385,14 @@ static int pcmcia_device_probe(struct device * dev) | |||
377 | p_drv = to_pcmcia_drv(dev->driver); | 385 | p_drv = to_pcmcia_drv(dev->driver); |
378 | s = p_dev->socket; | 386 | s = p_dev->socket; |
379 | 387 | ||
380 | if ((!p_drv->probe) || (!try_module_get(p_drv->owner))) { | 388 | if ((!p_drv->probe) || (!p_dev->function_config) || |
389 | (!try_module_get(p_drv->owner))) { | ||
381 | ret = -EINVAL; | 390 | ret = -EINVAL; |
382 | goto put_dev; | 391 | goto put_dev; |
383 | } | 392 | } |
384 | 393 | ||
385 | p_dev->state &= ~CLIENT_UNBOUND; | 394 | p_dev->state &= ~CLIENT_UNBOUND; |
386 | 395 | ||
387 | /* set up the device configuration, if it hasn't been done before */ | ||
388 | if (!s->functions) { | ||
389 | cistpl_longlink_mfc_t mfc; | ||
390 | if (pccard_read_tuple(s, p_dev->func, CISTPL_LONGLINK_MFC, | ||
391 | &mfc) == CS_SUCCESS) | ||
392 | s->functions = mfc.nfn; | ||
393 | else | ||
394 | s->functions = 1; | ||
395 | s->config = kzalloc(sizeof(config_t) * s->functions, | ||
396 | GFP_KERNEL); | ||
397 | if (!s->config) { | ||
398 | ret = -ENOMEM; | ||
399 | goto put_module; | ||
400 | } | ||
401 | } | ||
402 | p_dev->function_config = &s->config[p_dev->func]; | ||
403 | |||
404 | ret = p_drv->probe(p_dev); | 396 | ret = p_drv->probe(p_dev); |
405 | if (ret) | 397 | if (ret) |
406 | goto put_module; | 398 | goto put_module; |
@@ -576,7 +568,7 @@ static DECLARE_MUTEX(device_add_lock); | |||
576 | 568 | ||
577 | struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) | 569 | struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int function) |
578 | { | 570 | { |
579 | struct pcmcia_device *p_dev; | 571 | struct pcmcia_device *p_dev, *tmp_dev; |
580 | unsigned long flags; | 572 | unsigned long flags; |
581 | int bus_id_len; | 573 | int bus_id_len; |
582 | 574 | ||
@@ -597,6 +589,8 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f | |||
597 | p_dev->socket = s; | 589 | p_dev->socket = s; |
598 | p_dev->device_no = (s->device_count++); | 590 | p_dev->device_no = (s->device_count++); |
599 | p_dev->func = function; | 591 | p_dev->func = function; |
592 | if (s->functions < function) | ||
593 | s->functions = function; | ||
600 | 594 | ||
601 | p_dev->dev.bus = &pcmcia_bus_type; | 595 | p_dev->dev.bus = &pcmcia_bus_type; |
602 | p_dev->dev.parent = s->dev.dev; | 596 | p_dev->dev.parent = s->dev.dev; |
@@ -611,28 +605,50 @@ struct pcmcia_device * pcmcia_device_add(struct pcmcia_socket *s, unsigned int f | |||
611 | /* compat */ | 605 | /* compat */ |
612 | p_dev->state = CLIENT_UNBOUND; | 606 | p_dev->state = CLIENT_UNBOUND; |
613 | 607 | ||
614 | /* Add to the list in pcmcia_bus_socket */ | 608 | |
615 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | 609 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); |
610 | |||
611 | /* | ||
612 | * p_dev->function_config must be the same for all card functions. | ||
613 | * Note that this is serialized by the device_add_lock, so that | ||
614 | * only one such struct will be created. | ||
615 | */ | ||
616 | list_for_each_entry(tmp_dev, &s->devices_list, socket_device_list) | ||
617 | if (p_dev->func == tmp_dev->func) { | ||
618 | p_dev->function_config = tmp_dev->function_config; | ||
619 | kref_get(&p_dev->function_config->ref); | ||
620 | } | ||
621 | |||
622 | /* Add to the list in pcmcia_bus_socket */ | ||
616 | list_add_tail(&p_dev->socket_device_list, &s->devices_list); | 623 | list_add_tail(&p_dev->socket_device_list, &s->devices_list); |
624 | |||
617 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | 625 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); |
618 | 626 | ||
627 | if (!p_dev->function_config) { | ||
628 | p_dev->function_config = kzalloc(sizeof(struct config_t), | ||
629 | GFP_KERNEL); | ||
630 | if (!p_dev->function_config) | ||
631 | goto err_unreg; | ||
632 | kref_init(&p_dev->function_config->ref); | ||
633 | } | ||
634 | |||
619 | printk(KERN_NOTICE "pcmcia: registering new device %s\n", | 635 | printk(KERN_NOTICE "pcmcia: registering new device %s\n", |
620 | p_dev->devname); | 636 | p_dev->devname); |
621 | 637 | ||
622 | pcmcia_device_query(p_dev); | 638 | pcmcia_device_query(p_dev); |
623 | 639 | ||
624 | if (device_register(&p_dev->dev)) { | 640 | if (device_register(&p_dev->dev)) |
625 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | 641 | goto err_unreg; |
626 | list_del(&p_dev->socket_device_list); | ||
627 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
628 | |||
629 | goto err_free; | ||
630 | } | ||
631 | 642 | ||
632 | up(&device_add_lock); | 643 | up(&device_add_lock); |
633 | 644 | ||
634 | return p_dev; | 645 | return p_dev; |
635 | 646 | ||
647 | err_unreg: | ||
648 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | ||
649 | list_del(&p_dev->socket_device_list); | ||
650 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | ||
651 | |||
636 | err_free: | 652 | err_free: |
637 | kfree(p_dev->devname); | 653 | kfree(p_dev->devname); |
638 | kfree(p_dev); | 654 | kfree(p_dev); |