aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernhard Walle <bwalle@suse.de>2007-05-06 17:48:44 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:50 -0400
commit6179b5562d5d17c7c09b54cb11dd925ca308d7a9 (patch)
tree2f740d0f653678557a5601c6dffed1287b9aa513
parent02c83595b86480ee4d61665beb13f76685d40239 (diff)
add new_id to PCMCIA drivers
PCI drivers have the new_id file in sysfs which allows new IDs to be added at runtime. The advantage is to avoid re-compilation of a driver that works for a new device, but it's ID table doesn't contain the new device. This mechanism is only meant for testing, after the driver has been tested successfully, the ID should be added in source code so that new revisions of the kernel automatically detect the device. The implementation follows the PCI implementation. The interface is documented in Documentation/pcmcia/driver.txt. Computations should be done in userspace, so the sysfs string contains the raw structure members for matching. Signed-off-by: Bernhard Walle <bwalle@suse.de> Cc: Dominik Brodowski <linux@dominikbrodowski.net> Cc: Greg KH <greg@kroah.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/pcmcia/driver.txt30
-rw-r--r--drivers/pcmcia/ds.c113
-rw-r--r--include/pcmcia/ds.h6
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 @@
1PCMCIA Driver
2-------------
3
4
5sysfs
6-----
7
8New PCMCIA IDs may be added to a device driver pcmcia_device_id table at
9runtime as shown below:
10
11echo "match_flags manf_id card_id func_id function device_no \
12prod_id_hash[0] prod_id_hash[1] prod_id_hash[2] prod_id_hash[3]" > \
13/sys/bus/pcmcia/drivers/{driver}/new_id
14
15All fields are passed in as hexadecimal values (no leading 0x).
16The meaning is described in the PCMCIA specification, the match_flags is
17a bitwise or-ed combination from PCMCIA_DEV_ID_MATCH_* constants
18defined in include/linux/mod_devicetable.h.
19
20Once added, the driver probe routine will be invoked for any unclaimed
21PCMCIA device listed in its (newly updated) pcmcia_device_id list.
22
23A common use-case is to add a new device according to the manufacturer ID
24and the card ID (form the manf_id and card_id file in the device tree).
25For this, just use:
26
27echo "0x3 manf_id card_id 0 0 0 0 0 0 0" > \
28 /sys/bus/pcmcia/drivers/{driver}/new_id
29
30after 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
237struct 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 */
251static ssize_t
252pcmcia_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}
294static DRIVER_ATTR(new_id, S_IWUSR, NULL, pcmcia_store_new_id);
295
296static void
297pcmcia_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
309static int
310pcmcia_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 */
242int pcmcia_register_driver(struct pcmcia_driver *driver) 325int 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}
257EXPORT_SYMBOL(pcmcia_register_driver); 352EXPORT_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}
267EXPORT_SYMBOL(pcmcia_unregister_driver); 363EXPORT_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 {
108struct pcmcia_socket; 108struct pcmcia_socket;
109struct config_t; 109struct config_t;
110 110
111struct pcmcia_dynids {
112 spinlock_t lock;
113 struct list_head list;
114};
115
111struct pcmcia_driver { 116struct 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 */