aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pcmcia/ds.c
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 /drivers/pcmcia/ds.c
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>
Diffstat (limited to 'drivers/pcmcia/ds.c')
-rw-r--r--drivers/pcmcia/ds.c113
1 files changed, 112 insertions, 1 deletions
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 */