diff options
-rw-r--r-- | Documentation/pcmcia/driver.txt | 30 | ||||
-rw-r--r-- | drivers/pcmcia/ds.c | 113 | ||||
-rw-r--r-- | include/pcmcia/ds.h | 6 |
3 files changed, 148 insertions, 1 deletions
diff --git a/Documentation/pcmcia/driver.txt b/Documentation/pcmcia/driver.txt new file mode 100644 index 000000000000..0ac167920778 --- /dev/null +++ b/Documentation/pcmcia/driver.txt | |||
@@ -0,0 +1,30 @@ | |||
1 | PCMCIA Driver | ||
2 | ------------- | ||
3 | |||
4 | |||
5 | sysfs | ||
6 | ----- | ||
7 | |||
8 | New PCMCIA IDs may be added to a device driver pcmcia_device_id table at | ||
9 | runtime as shown below: | ||
10 | |||
11 | echo "match_flags manf_id card_id func_id function device_no \ | ||
12 | prod_id_hash[0] prod_id_hash[1] prod_id_hash[2] prod_id_hash[3]" > \ | ||
13 | /sys/bus/pcmcia/drivers/{driver}/new_id | ||
14 | |||
15 | All fields are passed in as hexadecimal values (no leading 0x). | ||
16 | The meaning is described in the PCMCIA specification, the match_flags is | ||
17 | a bitwise or-ed combination from PCMCIA_DEV_ID_MATCH_* constants | ||
18 | defined in include/linux/mod_devicetable.h. | ||
19 | |||
20 | Once added, the driver probe routine will be invoked for any unclaimed | ||
21 | PCMCIA device listed in its (newly updated) pcmcia_device_id list. | ||
22 | |||
23 | A common use-case is to add a new device according to the manufacturer ID | ||
24 | and the card ID (form the manf_id and card_id file in the device tree). | ||
25 | For this, just use: | ||
26 | |||
27 | echo "0x3 manf_id card_id 0 0 0 0 0 0 0" > \ | ||
28 | /sys/bus/pcmcia/drivers/{driver}/new_id | ||
29 | |||
30 | after loading the driver. | ||
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 18e111e12339..143c6efc478a 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c | |||
@@ -234,6 +234,89 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) | |||
234 | /*======================================================================*/ | 234 | /*======================================================================*/ |
235 | 235 | ||
236 | 236 | ||
237 | struct pcmcia_dynid { | ||
238 | struct list_head node; | ||
239 | struct pcmcia_device_id id; | ||
240 | }; | ||
241 | |||
242 | /** | ||
243 | * pcmcia_store_new_id - add a new PCMCIA device ID to this driver and re-probe devices | ||
244 | * @driver: target device driver | ||
245 | * @buf: buffer for scanning device ID data | ||
246 | * @count: input size | ||
247 | * | ||
248 | * Adds a new dynamic PCMCIA device ID to this driver, | ||
249 | * and causes the driver to probe for all devices again. | ||
250 | */ | ||
251 | static ssize_t | ||
252 | pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count) | ||
253 | { | ||
254 | struct pcmcia_dynid *dynid; | ||
255 | struct pcmcia_driver *pdrv = to_pcmcia_drv(driver); | ||
256 | __u16 match_flags, manf_id, card_id; | ||
257 | __u8 func_id, function, device_no; | ||
258 | __u32 prod_id_hash[4] = {0, 0, 0, 0}; | ||
259 | int fields=0; | ||
260 | int retval = 0; | ||
261 | |||
262 | fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x", | ||
263 | &match_flags, &manf_id, &card_id, &func_id, &function, &device_no, | ||
264 | &prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]); | ||
265 | if (fields < 6) | ||
266 | return -EINVAL; | ||
267 | |||
268 | dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL); | ||
269 | if (!dynid) | ||
270 | return -ENOMEM; | ||
271 | |||
272 | INIT_LIST_HEAD(&dynid->node); | ||
273 | dynid->id.match_flags = match_flags; | ||
274 | dynid->id.manf_id = manf_id; | ||
275 | dynid->id.card_id = card_id; | ||
276 | dynid->id.func_id = func_id; | ||
277 | dynid->id.function = function; | ||
278 | dynid->id.device_no = device_no; | ||
279 | memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4); | ||
280 | |||
281 | spin_lock(&pdrv->dynids.lock); | ||
282 | list_add_tail(&pdrv->dynids.list, &dynid->node); | ||
283 | spin_unlock(&pdrv->dynids.lock); | ||
284 | |||
285 | if (get_driver(&pdrv->drv)) { | ||
286 | retval = driver_attach(&pdrv->drv); | ||
287 | put_driver(&pdrv->drv); | ||
288 | } | ||
289 | |||
290 | if (retval) | ||
291 | return retval; | ||
292 | return count; | ||
293 | } | ||
294 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, pcmcia_store_new_id); | ||
295 | |||
296 | static void | ||
297 | pcmcia_free_dynids(struct pcmcia_driver *drv) | ||
298 | { | ||
299 | struct pcmcia_dynid *dynid, *n; | ||
300 | |||
301 | spin_lock(&drv->dynids.lock); | ||
302 | list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { | ||
303 | list_del(&dynid->node); | ||
304 | kfree(dynid); | ||
305 | } | ||
306 | spin_unlock(&drv->dynids.lock); | ||
307 | } | ||
308 | |||
309 | static int | ||
310 | pcmcia_create_newid_file(struct pcmcia_driver *drv) | ||
311 | { | ||
312 | int error = 0; | ||
313 | if (drv->probe != NULL) | ||
314 | error = sysfs_create_file(&drv->drv.kobj, | ||
315 | &driver_attr_new_id.attr); | ||
316 | return error; | ||
317 | } | ||
318 | |||
319 | |||
237 | /** | 320 | /** |
238 | * pcmcia_register_driver - register a PCMCIA driver with the bus core | 321 | * pcmcia_register_driver - register a PCMCIA driver with the bus core |
239 | * | 322 | * |
@@ -241,6 +324,8 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) | |||
241 | */ | 324 | */ |
242 | int pcmcia_register_driver(struct pcmcia_driver *driver) | 325 | int pcmcia_register_driver(struct pcmcia_driver *driver) |
243 | { | 326 | { |
327 | int error; | ||
328 | |||
244 | if (!driver) | 329 | if (!driver) |
245 | return -EINVAL; | 330 | return -EINVAL; |
246 | 331 | ||
@@ -249,10 +334,20 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) | |||
249 | /* initialize common fields */ | 334 | /* initialize common fields */ |
250 | driver->drv.bus = &pcmcia_bus_type; | 335 | driver->drv.bus = &pcmcia_bus_type; |
251 | driver->drv.owner = driver->owner; | 336 | driver->drv.owner = driver->owner; |
337 | spin_lock_init(&driver->dynids.lock); | ||
338 | INIT_LIST_HEAD(&driver->dynids.list); | ||
252 | 339 | ||
253 | ds_dbg(3, "registering driver %s\n", driver->drv.name); | 340 | ds_dbg(3, "registering driver %s\n", driver->drv.name); |
254 | 341 | ||
255 | return driver_register(&driver->drv); | 342 | error = driver_register(&driver->drv); |
343 | if (error < 0) | ||
344 | return error; | ||
345 | |||
346 | error = pcmcia_create_newid_file(driver); | ||
347 | if (error) | ||
348 | driver_unregister(&driver->drv); | ||
349 | |||
350 | return error; | ||
256 | } | 351 | } |
257 | EXPORT_SYMBOL(pcmcia_register_driver); | 352 | EXPORT_SYMBOL(pcmcia_register_driver); |
258 | 353 | ||
@@ -263,6 +358,7 @@ void pcmcia_unregister_driver(struct pcmcia_driver *driver) | |||
263 | { | 358 | { |
264 | ds_dbg(3, "unregistering driver %s\n", driver->drv.name); | 359 | ds_dbg(3, "unregistering driver %s\n", driver->drv.name); |
265 | driver_unregister(&driver->drv); | 360 | driver_unregister(&driver->drv); |
361 | pcmcia_free_dynids(driver); | ||
266 | } | 362 | } |
267 | EXPORT_SYMBOL(pcmcia_unregister_driver); | 363 | EXPORT_SYMBOL(pcmcia_unregister_driver); |
268 | 364 | ||
@@ -927,6 +1023,21 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { | |||
927 | struct pcmcia_device * p_dev = to_pcmcia_dev(dev); | 1023 | struct pcmcia_device * p_dev = to_pcmcia_dev(dev); |
928 | struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); | 1024 | struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); |
929 | struct pcmcia_device_id *did = p_drv->id_table; | 1025 | struct pcmcia_device_id *did = p_drv->id_table; |
1026 | struct pcmcia_dynid *dynid; | ||
1027 | |||
1028 | /* match dynamic devices first */ | ||
1029 | spin_lock(&p_drv->dynids.lock); | ||
1030 | list_for_each_entry(dynid, &p_drv->dynids.list, node) { | ||
1031 | ds_dbg(3, "trying to match %s to %s\n", dev->bus_id, | ||
1032 | drv->name); | ||
1033 | if (pcmcia_devmatch(p_dev, &dynid->id)) { | ||
1034 | ds_dbg(0, "matched %s to %s\n", dev->bus_id, | ||
1035 | drv->name); | ||
1036 | spin_unlock(&p_drv->dynids.lock); | ||
1037 | return 1; | ||
1038 | } | ||
1039 | } | ||
1040 | spin_unlock(&p_drv->dynids.lock); | ||
930 | 1041 | ||
931 | #ifdef CONFIG_PCMCIA_IOCTL | 1042 | #ifdef CONFIG_PCMCIA_IOCTL |
932 | /* matching by cardmgr */ | 1043 | /* matching by cardmgr */ |
diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 8c339f5678cf..90ef552c42dd 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h | |||
@@ -108,6 +108,11 @@ typedef struct dev_node_t { | |||
108 | struct pcmcia_socket; | 108 | struct pcmcia_socket; |
109 | struct config_t; | 109 | struct config_t; |
110 | 110 | ||
111 | struct pcmcia_dynids { | ||
112 | spinlock_t lock; | ||
113 | struct list_head list; | ||
114 | }; | ||
115 | |||
111 | struct pcmcia_driver { | 116 | struct pcmcia_driver { |
112 | int (*probe) (struct pcmcia_device *dev); | 117 | int (*probe) (struct pcmcia_device *dev); |
113 | void (*remove) (struct pcmcia_device *dev); | 118 | void (*remove) (struct pcmcia_device *dev); |
@@ -118,6 +123,7 @@ struct pcmcia_driver { | |||
118 | struct module *owner; | 123 | struct module *owner; |
119 | struct pcmcia_device_id *id_table; | 124 | struct pcmcia_device_id *id_table; |
120 | struct device_driver drv; | 125 | struct device_driver drv; |
126 | struct pcmcia_dynids dynids; | ||
121 | }; | 127 | }; |
122 | 128 | ||
123 | /* driver registration */ | 129 | /* driver registration */ |