Commit 6179b5562d5d17c7c09b54cb11dd925ca308d7a9

Authored by Bernhard Walle
Committed by Linus Torvalds
1 parent 02c83595b8

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>

Showing 3 changed files with 148 additions and 1 deletions Side-by-side Diff

Documentation/pcmcia/driver.txt
  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.
... ... @@ -234,13 +234,98 @@
234 234 /*======================================================================*/
235 235  
236 236  
  237 +struct pcmcia_dynid {
  238 + struct list_head node;
  239 + struct pcmcia_device_id id;
  240 +};
  241 +
237 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 +
  320 +/**
238 321 * pcmcia_register_driver - register a PCMCIA driver with the bus core
239 322 *
240 323 * Registers a PCMCIA driver with the PCMCIA bus core.
241 324 */
242 325 int pcmcia_register_driver(struct pcmcia_driver *driver)
243 326 {
  327 + int error;
  328 +
244 329 if (!driver)
245 330 return -EINVAL;
246 331  
247 332  
... ... @@ -249,10 +334,20 @@
249 334 /* initialize common fields */
250 335 driver->drv.bus = &pcmcia_bus_type;
251 336 driver->drv.owner = driver->owner;
  337 + spin_lock_init(&driver->dynids.lock);
  338 + INIT_LIST_HEAD(&driver->dynids.list);
252 339  
253 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 352 EXPORT_SYMBOL(pcmcia_register_driver);
258 353  
... ... @@ -263,6 +358,7 @@
263 358 {
264 359 ds_dbg(3, "unregistering driver %s\n", driver->drv.name);
265 360 driver_unregister(&driver->drv);
  361 + pcmcia_free_dynids(driver);
266 362 }
267 363 EXPORT_SYMBOL(pcmcia_unregister_driver);
268 364  
... ... @@ -927,6 +1023,21 @@
927 1023 struct pcmcia_device * p_dev = to_pcmcia_dev(dev);
928 1024 struct pcmcia_driver * p_drv = to_pcmcia_drv(drv);
929 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 1042 #ifdef CONFIG_PCMCIA_IOCTL
932 1043 /* matching by cardmgr */
... ... @@ -108,6 +108,11 @@
108 108 struct pcmcia_socket;
109 109 struct config_t;
110 110  
  111 +struct pcmcia_dynids {
  112 + spinlock_t lock;
  113 + struct list_head list;
  114 +};
  115 +
111 116 struct pcmcia_driver {
112 117 int (*probe) (struct pcmcia_device *dev);
113 118 void (*remove) (struct pcmcia_device *dev);
... ... @@ -118,6 +123,7 @@
118 123 struct module *owner;
119 124 struct pcmcia_device_id *id_table;
120 125 struct device_driver drv;
  126 + struct pcmcia_dynids dynids;
121 127 };
122 128  
123 129 /* driver registration */