diff options
Diffstat (limited to 'drivers/pcmcia/ds.c')
-rw-r--r-- | drivers/pcmcia/ds.c | 123 |
1 files changed, 122 insertions, 1 deletions
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 35d479b0df64..5701b93b2ddb 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -101,6 +101,9 @@ struct pcmcia_bus_socket { | |||
101 | u8 device_count; /* the number of devices, used | 101 | u8 device_count; /* the number of devices, used |
102 | * only internally and subject | 102 | * only internally and subject |
103 | * to incorrectness and change */ | 103 | * to incorrectness and change */ |
104 | |||
105 | u8 device_add_pending; | ||
106 | struct work_struct device_add; | ||
104 | }; | 107 | }; |
105 | static spinlock_t pcmcia_dev_list_lock; | 108 | static spinlock_t pcmcia_dev_list_lock; |
106 | 109 | ||
@@ -512,6 +515,10 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns | |||
512 | 515 | ||
513 | down(&device_add_lock); | 516 | down(&device_add_lock); |
514 | 517 | ||
518 | /* max of 2 devices per card */ | ||
519 | if (s->device_count == 2) | ||
520 | goto err_put; | ||
521 | |||
515 | p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); | 522 | p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL); |
516 | if (!p_dev) | 523 | if (!p_dev) |
517 | goto err_put; | 524 | goto err_put; |
@@ -537,6 +544,8 @@ static struct pcmcia_device * pcmcia_device_add(struct pcmcia_bus_socket *s, uns | |||
537 | list_add_tail(&p_dev->socket_device_list, &s->devices_list); | 544 | list_add_tail(&p_dev->socket_device_list, &s->devices_list); |
538 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); | 545 | spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags); |
539 | 546 | ||
547 | pcmcia_device_query(p_dev); | ||
548 | |||
540 | if (device_register(&p_dev->dev)) { | 549 | if (device_register(&p_dev->dev)) { |
541 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); | 550 | spin_lock_irqsave(&pcmcia_dev_list_lock, flags); |
542 | list_del(&p_dev->socket_device_list); | 551 | list_del(&p_dev->socket_device_list); |
@@ -591,14 +600,123 @@ static int pcmcia_card_add(struct pcmcia_socket *s) | |||
591 | } | 600 | } |
592 | 601 | ||
593 | 602 | ||
603 | static void pcmcia_delayed_add_pseudo_device(void *data) | ||
604 | { | ||
605 | struct pcmcia_bus_socket *s = data; | ||
606 | pcmcia_device_add(s, 0); | ||
607 | s->device_add_pending = 0; | ||
608 | } | ||
609 | |||
610 | static inline void pcmcia_add_pseudo_device(struct pcmcia_bus_socket *s) | ||
611 | { | ||
612 | if (!s->device_add_pending) { | ||
613 | schedule_work(&s->device_add); | ||
614 | s->device_add_pending = 1; | ||
615 | } | ||
616 | return; | ||
617 | } | ||
618 | |||
619 | |||
620 | static inline int pcmcia_devmatch(struct pcmcia_device *dev, | ||
621 | struct pcmcia_device_id *did) | ||
622 | { | ||
623 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_MANF_ID) { | ||
624 | if ((!dev->has_manf_id) || (dev->manf_id != did->manf_id)) | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_CARD_ID) { | ||
629 | if ((!dev->has_card_id) || (dev->card_id != did->card_id)) | ||
630 | return 0; | ||
631 | } | ||
632 | |||
633 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNCTION) { | ||
634 | if (dev->func != did->function) | ||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID1) { | ||
639 | if (!dev->prod_id[0]) | ||
640 | return 0; | ||
641 | if (strcmp(did->prod_id[0], dev->prod_id[0])) | ||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID2) { | ||
646 | if (!dev->prod_id[1]) | ||
647 | return 0; | ||
648 | if (strcmp(did->prod_id[1], dev->prod_id[1])) | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID3) { | ||
653 | if (!dev->prod_id[2]) | ||
654 | return 0; | ||
655 | if (strcmp(did->prod_id[2], dev->prod_id[2])) | ||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_PROD_ID4) { | ||
660 | if (!dev->prod_id[3]) | ||
661 | return 0; | ||
662 | if (strcmp(did->prod_id[3], dev->prod_id[3])) | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) { | ||
667 | /* handle pseudo multifunction devices: | ||
668 | * there are at most two pseudo multifunction devices. | ||
669 | * if we're matching against the first, schedule a | ||
670 | * call which will then check whether there are two | ||
671 | * pseudo devices, and if not, add the second one. | ||
672 | */ | ||
673 | if (dev->device_no == 0) | ||
674 | pcmcia_add_pseudo_device(dev->socket->pcmcia); | ||
675 | |||
676 | if (dev->device_no != did->device_no) | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) { | ||
681 | if ((!dev->has_func_id) || (dev->func_id != did->func_id)) | ||
682 | return 0; | ||
683 | |||
684 | /* if this is a pseudo-multi-function device, | ||
685 | * we need explicit matches */ | ||
686 | if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) | ||
687 | return 0; | ||
688 | if (dev->device_no) | ||
689 | return 0; | ||
690 | |||
691 | /* also, FUNC_ID matching needs to be activated by userspace | ||
692 | * after it has re-checked that there is no possible module | ||
693 | * with a prod_id/manf_id/card_id match. | ||
694 | */ | ||
695 | if (!dev->allow_func_id_match) | ||
696 | return 0; | ||
697 | } | ||
698 | |||
699 | dev->dev.driver_data = (void *) did; | ||
700 | |||
701 | return 1; | ||
702 | } | ||
703 | |||
704 | |||
594 | static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { | 705 | static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { |
595 | struct pcmcia_device * p_dev = to_pcmcia_dev(dev); | 706 | struct pcmcia_device * p_dev = to_pcmcia_dev(dev); |
596 | struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); | 707 | struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); |
708 | struct pcmcia_device_id *did = p_drv->id_table; | ||
597 | 709 | ||
598 | /* matching by cardmgr */ | 710 | /* matching by cardmgr */ |
599 | if (p_dev->cardmgr == p_drv) | 711 | if (p_dev->cardmgr == p_drv) |
600 | return 1; | 712 | return 1; |
601 | 713 | ||
714 | while (did && did->match_flags) { | ||
715 | if (pcmcia_devmatch(p_dev, did)) | ||
716 | return 1; | ||
717 | did++; | ||
718 | } | ||
719 | |||
602 | return 0; | 720 | return 0; |
603 | } | 721 | } |
604 | 722 | ||
@@ -922,7 +1040,9 @@ static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info) | |||
922 | rescan: | 1040 | rescan: |
923 | p_dev->cardmgr = p_drv; | 1041 | p_dev->cardmgr = p_drv; |
924 | 1042 | ||
925 | pcmcia_device_query(p_dev); | 1043 | /* if a driver is already running, we can abort */ |
1044 | if (p_dev->dev.driver) | ||
1045 | goto err_put_module; | ||
926 | 1046 | ||
927 | /* | 1047 | /* |
928 | * Prevent this racing with a card insertion. | 1048 | * Prevent this racing with a card insertion. |
@@ -1595,6 +1715,7 @@ static int __devinit pcmcia_bus_add_socket(struct class_device *class_dev) | |||
1595 | 1715 | ||
1596 | init_waitqueue_head(&s->queue); | 1716 | init_waitqueue_head(&s->queue); |
1597 | INIT_LIST_HEAD(&s->devices_list); | 1717 | INIT_LIST_HEAD(&s->devices_list); |
1718 | INIT_WORK(&s->device_add, pcmcia_delayed_add_pseudo_device, s); | ||
1598 | 1719 | ||
1599 | /* Set up hotline to Card Services */ | 1720 | /* Set up hotline to Card Services */ |
1600 | s->callback.owner = THIS_MODULE; | 1721 | s->callback.owner = THIS_MODULE; |