diff options
Diffstat (limited to 'drivers/pcmcia/ds.c')
| -rw-r--r-- | drivers/pcmcia/ds.c | 113 |
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 | ||
| 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 */ |
