aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Brodowski <linux@dominikbrodowski.net>2005-06-27 19:28:06 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-06-27 21:03:06 -0400
commit1ad275e3e7d253d44f03868e85977c908e334fed (patch)
tree82f0a1fc070f32c015be14596c50c681d90d4c1a
parent3ee13937525f6044d769b1a84d5db5669ac1959e (diff)
[PATCH] pcmcia: device and driver matching
The actual matching of pcmcia drivers and pcmcia devices. The original version of this was written by David Woodhouse. Signed-off-by: Dominik Brodowski <linux@dominikbrodowski.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/pcmcia/ds.c123
-rw-r--r--include/linux/mod_devicetable.h33
-rw-r--r--include/pcmcia/device_id.h175
-rw-r--r--include/pcmcia/ds.h7
4 files changed, 336 insertions, 2 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};
105static spinlock_t pcmcia_dev_list_lock; 108static 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
603static 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
610static 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
620static 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
594static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { 705static 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)
922rescan: 1040rescan:
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;
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index d6eb7b2efc04..e9651cd8310c 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -175,4 +175,37 @@ struct serio_device_id {
175}; 175};
176 176
177 177
178/* PCMCIA */
179
180struct pcmcia_device_id {
181 __u16 match_flags;
182
183 __u16 manf_id;
184 __u16 card_id;
185
186 __u8 func_id;
187
188 /* for real multi-function devices */
189 __u8 function;
190
191 /* for pseude multi-function devices */
192 __u8 device_no;
193
194 const char * prod_id[4];
195 __u32 prod_id_hash[4];
196
197 /* not matched against */
198 kernel_ulong_t driver_info;
199};
200
201#define PCMCIA_DEV_ID_MATCH_MANF_ID 0x0001
202#define PCMCIA_DEV_ID_MATCH_CARD_ID 0x0002
203#define PCMCIA_DEV_ID_MATCH_FUNC_ID 0x0004
204#define PCMCIA_DEV_ID_MATCH_FUNCTION 0x0008
205#define PCMCIA_DEV_ID_MATCH_PROD_ID1 0x0010
206#define PCMCIA_DEV_ID_MATCH_PROD_ID2 0x0020
207#define PCMCIA_DEV_ID_MATCH_PROD_ID3 0x0040
208#define PCMCIA_DEV_ID_MATCH_PROD_ID4 0x0080
209#define PCMCIA_DEV_ID_MATCH_DEVICE_NO 0x0100
210
178#endif /* LINUX_MOD_DEVICETABLE_H */ 211#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/pcmcia/device_id.h b/include/pcmcia/device_id.h
new file mode 100644
index 000000000000..acf68656de3c
--- /dev/null
+++ b/include/pcmcia/device_id.h
@@ -0,0 +1,175 @@
1/*
2 * Copyright (2003-2004) Dominik Brodowski <linux@brodo.de>
3 * David Woodhouse
4 *
5 * License: GPL v2
6 */
7
8#define PCMCIA_DEVICE_MANF_CARD(manf, card) { \
9 .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
10 PCMCIA_DEV_ID_MATCH_CARD_ID, \
11 .manf_id = (manf), \
12 .card_id = (card), }
13
14#define PCMCIA_DEVICE_FUNC_ID(func) { \
15 .match_flags = PCMCIA_DEV_ID_MATCH_FUNC_ID, \
16 .func_id = (func), }
17
18#define PCMCIA_DEVICE_PROD_ID1(v1, vh1) { \
19 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1, \
20 .prod_id = { (v1), NULL, NULL, NULL }, \
21 .prod_id_hash = { (vh1), 0, 0, 0 }, }
22
23#define PCMCIA_DEVICE_PROD_ID2(v2, vh2) { \
24 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2, \
25 .prod_id = { NULL, (v2), NULL, NULL }, \
26 .prod_id_hash = { 0, (vh2), 0, 0 }, }
27
28#define PCMCIA_DEVICE_PROD_ID12(v1, v2, vh1, vh2) { \
29 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
30 PCMCIA_DEV_ID_MATCH_PROD_ID2, \
31 .prod_id = { (v1), (v2), NULL, NULL }, \
32 .prod_id_hash = { (vh1), (vh2), 0, 0 }, }
33
34#define PCMCIA_DEVICE_PROD_ID13(v1, v3, vh1, vh3) { \
35 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
36 PCMCIA_DEV_ID_MATCH_PROD_ID3, \
37 .prod_id = { (v1), NULL, (v3), NULL }, \
38 .prod_id_hash = { (vh1), 0, (vh3), 0 }, }
39
40#define PCMCIA_DEVICE_PROD_ID14(v1, v4, vh1, vh4) { \
41 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
42 PCMCIA_DEV_ID_MATCH_PROD_ID4, \
43 .prod_id = { (v1), NULL, NULL, (v4) }, \
44 .prod_id_hash = { (vh1), 0, 0, (vh4) }, }
45
46#define PCMCIA_DEVICE_PROD_ID123(v1, v2, v3, vh1, vh2, vh3) { \
47 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
48 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
49 PCMCIA_DEV_ID_MATCH_PROD_ID3, \
50 .prod_id = { (v1), (v2), (v3), NULL },\
51 .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, }
52
53#define PCMCIA_DEVICE_PROD_ID124(v1, v2, v4, vh1, vh2, vh4) { \
54 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
55 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
56 PCMCIA_DEV_ID_MATCH_PROD_ID4, \
57 .prod_id = { (v1), (v2), NULL, (v4) }, \
58 .prod_id_hash = { (vh1), (vh2), 0, (vh4) }, }
59
60#define PCMCIA_DEVICE_PROD_ID134(v1, v3, v4, vh1, vh3, vh4) { \
61 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
62 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
63 PCMCIA_DEV_ID_MATCH_PROD_ID4, \
64 .prod_id = { (v1), NULL, (v3), (v4) }, \
65 .prod_id_hash = { (vh1), 0, (vh3), (vh4) }, }
66
67#define PCMCIA_DEVICE_PROD_ID1234(v1, v2, v3, v4, vh1, vh2, vh3, vh4) { \
68 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
69 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
70 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
71 PCMCIA_DEV_ID_MATCH_PROD_ID4, \
72 .prod_id = { (v1), (v2), (v3), (v4) }, \
73 .prod_id_hash = { (vh1), (vh2), (vh3), (vh4) }, }
74
75
76/* multi-function devices */
77
78#define PCMCIA_MFC_DEVICE_MANF_CARD(mfc, manf, card) { \
79 .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
80 PCMCIA_DEV_ID_MATCH_CARD_ID| \
81 PCMCIA_DEV_ID_MATCH_FUNCTION, \
82 .manf_id = (manf), \
83 .card_id = (card), \
84 .function = (mfc), }
85
86#define PCMCIA_MFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
87 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
88 PCMCIA_DEV_ID_MATCH_FUNCTION, \
89 .prod_id = { (v1), NULL, NULL, NULL }, \
90 .prod_id_hash = { (vh1), 0, 0, 0 }, \
91 .function = (mfc), }
92
93#define PCMCIA_MFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
94 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
95 PCMCIA_DEV_ID_MATCH_FUNCTION, \
96 .prod_id = { NULL, (v2), NULL, NULL }, \
97 .prod_id_hash = { 0, (vh2), 0, 0 }, \
98 .function = (mfc), }
99
100#define PCMCIA_MFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
101 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
102 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
103 PCMCIA_DEV_ID_MATCH_FUNCTION, \
104 .prod_id = { (v1), (v2), NULL, NULL }, \
105 .prod_id_hash = { (vh1), (vh2), 0, 0 }, \
106 .function = (mfc), }
107
108#define PCMCIA_MFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
109 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
110 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
111 PCMCIA_DEV_ID_MATCH_FUNCTION, \
112 .prod_id = { (v1), NULL, (v3), NULL }, \
113 .prod_id_hash = { (vh1), 0, (vh3), 0 }, \
114 .function = (mfc), }
115
116#define PCMCIA_MFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
117 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
118 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
119 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
120 PCMCIA_DEV_ID_MATCH_FUNCTION, \
121 .prod_id = { (v1), (v2), (v3), NULL },\
122 .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
123 .function = (mfc), }
124
125/* pseudo multi-function devices */
126
127#define PCMCIA_PFC_DEVICE_MANF_CARD(mfc, manf, card) { \
128 .match_flags = PCMCIA_DEV_ID_MATCH_MANF_ID| \
129 PCMCIA_DEV_ID_MATCH_CARD_ID| \
130 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
131 .manf_id = (manf), \
132 .card_id = (card), \
133 .device_no = (mfc), }
134
135#define PCMCIA_PFC_DEVICE_PROD_ID1(mfc, v1, vh1) { \
136 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
137 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
138 .prod_id = { (v1), NULL, NULL, NULL }, \
139 .prod_id_hash = { (vh1), 0, 0, 0 }, \
140 .device_no = (mfc), }
141
142#define PCMCIA_PFC_DEVICE_PROD_ID2(mfc, v2, vh2) { \
143 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID2| \
144 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
145 .prod_id = { NULL, (v2), NULL, NULL }, \
146 .prod_id_hash = { 0, (vh2), 0, 0 }, \
147 .device_no = (mfc), }
148
149#define PCMCIA_PFC_DEVICE_PROD_ID12(mfc, v1, v2, vh1, vh2) { \
150 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
151 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
152 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
153 .prod_id = { (v1), (v2), NULL, NULL }, \
154 .prod_id_hash = { (vh1), (vh2), 0, 0 }, \
155 .device_no = (mfc), }
156
157#define PCMCIA_PFC_DEVICE_PROD_ID13(mfc, v1, v3, vh1, vh3) { \
158 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
159 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
160 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
161 .prod_id = { (v1), NULL, (v3), NULL }, \
162 .prod_id_hash = { (vh1), 0, (vh3), 0 }, \
163 .device_no = (mfc), }
164
165#define PCMCIA_PFC_DEVICE_PROD_ID123(mfc, v1, v2, v3, vh1, vh2, vh3) { \
166 .match_flags = PCMCIA_DEV_ID_MATCH_PROD_ID1| \
167 PCMCIA_DEV_ID_MATCH_PROD_ID2| \
168 PCMCIA_DEV_ID_MATCH_PROD_ID3| \
169 PCMCIA_DEV_ID_MATCH_DEVICE_NO, \
170 .prod_id = { (v1), (v2), (v3), NULL },\
171 .prod_id_hash = { (vh1), (vh2), (vh3), 0 }, \
172 .device_no = (mfc), }
173
174
175#define PCMCIA_DEVICE_NULL { .match_flags = 0, }
diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h
index 312fd958c901..c267edde9d0c 100644
--- a/include/pcmcia/ds.h
+++ b/include/pcmcia/ds.h
@@ -18,6 +18,8 @@
18 18
19#include <pcmcia/bulkmem.h> 19#include <pcmcia/bulkmem.h>
20#include <pcmcia/cs_types.h> 20#include <pcmcia/cs_types.h>
21#include <pcmcia/device_id.h>
22#include <linux/mod_devicetable.h>
21 23
22typedef struct tuple_parse_t { 24typedef struct tuple_parse_t {
23 tuple_t tuple; 25 tuple_t tuple;
@@ -135,6 +137,7 @@ struct pcmcia_driver {
135 dev_link_t *(*attach)(void); 137 dev_link_t *(*attach)(void);
136 void (*detach)(dev_link_t *); 138 void (*detach)(dev_link_t *);
137 struct module *owner; 139 struct module *owner;
140 struct pcmcia_device_id *id_table;
138 struct device_driver drv; 141 struct device_driver drv;
139}; 142};
140 143
@@ -173,7 +176,9 @@ struct pcmcia_device {
173 u8 has_manf_id:1; 176 u8 has_manf_id:1;
174 u8 has_card_id:1; 177 u8 has_card_id:1;
175 u8 has_func_id:1; 178 u8 has_func_id:1;
176 u8 reserved:5; 179
180 u8 allow_func_id_match:1;
181 u8 reserved:4;
177 182
178 u8 func_id; 183 u8 func_id;
179 u16 manf_id; 184 u16 manf_id;