X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fpcmcia%2Fds.c;h=a99607142fc81ee029ee136c443f82d18c83ec1a;hb=dd6ccfe64dad4ea498392805b477308abef43a4c;hp=18e111e1233960dc85aaa0e71bc3550ceada4035;hpb=9468482bd4c3b89abe04a770848d5eaa1ea830b0;p=linux-2.6 diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 18e111e123..a99607142f 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) /*======================================================================*/ +struct pcmcia_dynid { + struct list_head node; + struct pcmcia_device_id id; +}; + +/** + * pcmcia_store_new_id - add a new PCMCIA device ID to this driver and re-probe devices + * @driver: target device driver + * @buf: buffer for scanning device ID data + * @count: input size + * + * Adds a new dynamic PCMCIA device ID to this driver, + * and causes the driver to probe for all devices again. + */ +static ssize_t +pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count) +{ + struct pcmcia_dynid *dynid; + struct pcmcia_driver *pdrv = to_pcmcia_drv(driver); + __u16 match_flags, manf_id, card_id; + __u8 func_id, function, device_no; + __u32 prod_id_hash[4] = {0, 0, 0, 0}; + int fields=0; + int retval = 0; + + fields = sscanf(buf, "%hx %hx %hx %hhx %hhx %hhx %x %x %x %x", + &match_flags, &manf_id, &card_id, &func_id, &function, &device_no, + &prod_id_hash[0], &prod_id_hash[1], &prod_id_hash[2], &prod_id_hash[3]); + if (fields < 6) + return -EINVAL; + + dynid = kzalloc(sizeof(struct pcmcia_dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + INIT_LIST_HEAD(&dynid->node); + dynid->id.match_flags = match_flags; + dynid->id.manf_id = manf_id; + dynid->id.card_id = card_id; + dynid->id.func_id = func_id; + dynid->id.function = function; + dynid->id.device_no = device_no; + memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4); + + spin_lock(&pdrv->dynids.lock); + list_add_tail(&pdrv->dynids.list, &dynid->node); + spin_unlock(&pdrv->dynids.lock); + + if (get_driver(&pdrv->drv)) { + retval = driver_attach(&pdrv->drv); + put_driver(&pdrv->drv); + } + + if (retval) + return retval; + return count; +} +static DRIVER_ATTR(new_id, S_IWUSR, NULL, pcmcia_store_new_id); + +static void +pcmcia_free_dynids(struct pcmcia_driver *drv) +{ + struct pcmcia_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + +static int +pcmcia_create_newid_file(struct pcmcia_driver *drv) +{ + int error = 0; + if (drv->probe != NULL) + error = sysfs_create_file(&drv->drv.kobj, + &driver_attr_new_id.attr); + return error; +} + + /** * pcmcia_register_driver - register a PCMCIA driver with the bus core * @@ -241,6 +324,8 @@ static void pcmcia_check_driver(struct pcmcia_driver *p_drv) */ int pcmcia_register_driver(struct pcmcia_driver *driver) { + int error; + if (!driver) return -EINVAL; @@ -249,10 +334,20 @@ int pcmcia_register_driver(struct pcmcia_driver *driver) /* initialize common fields */ driver->drv.bus = &pcmcia_bus_type; driver->drv.owner = driver->owner; + spin_lock_init(&driver->dynids.lock); + INIT_LIST_HEAD(&driver->dynids.list); ds_dbg(3, "registering driver %s\n", driver->drv.name); - return driver_register(&driver->drv); + error = driver_register(&driver->drv); + if (error < 0) + return error; + + error = pcmcia_create_newid_file(driver); + if (error) + driver_unregister(&driver->drv); + + return error; } EXPORT_SYMBOL(pcmcia_register_driver); @@ -263,6 +358,7 @@ void pcmcia_unregister_driver(struct pcmcia_driver *driver) { ds_dbg(3, "unregistering driver %s\n", driver->drv.name); driver_unregister(&driver->drv); + pcmcia_free_dynids(driver); } EXPORT_SYMBOL(pcmcia_unregister_driver); @@ -927,6 +1023,21 @@ static int pcmcia_bus_match(struct device * dev, struct device_driver * drv) { struct pcmcia_device * p_dev = to_pcmcia_dev(dev); struct pcmcia_driver * p_drv = to_pcmcia_drv(drv); struct pcmcia_device_id *did = p_drv->id_table; + struct pcmcia_dynid *dynid; + + /* match dynamic devices first */ + spin_lock(&p_drv->dynids.lock); + list_for_each_entry(dynid, &p_drv->dynids.list, node) { + ds_dbg(3, "trying to match %s to %s\n", dev->bus_id, + drv->name); + if (pcmcia_devmatch(p_dev, &dynid->id)) { + ds_dbg(0, "matched %s to %s\n", dev->bus_id, + drv->name); + spin_unlock(&p_drv->dynids.lock); + return 1; + } + } + spin_unlock(&p_drv->dynids.lock); #ifdef CONFIG_PCMCIA_IOCTL /* matching by cardmgr */ @@ -1016,6 +1127,34 @@ static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp, #endif +/************************ runtime PM support ***************************/ + +static int pcmcia_dev_suspend(struct device *dev, pm_message_t state); +static int pcmcia_dev_resume(struct device *dev); + +static int runtime_suspend(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_suspend(dev, PMSG_SUSPEND); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_SUSPEND; + return rc; +} + +static void runtime_resume(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_resume(dev); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_ON; +} + /************************ per-device sysfs output ***************************/ #define pcmcia_device_attr(field, test, format) \ @@ -1062,9 +1201,9 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute return -EINVAL; if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) - ret = dpm_runtime_suspend(dev, PMSG_SUSPEND); + ret = runtime_suspend(dev); else if (p_dev->suspended && !strncmp(buf, "on", 2)) - dpm_runtime_resume(dev); + runtime_resume(dev); return ret ? ret : count; } @@ -1201,10 +1340,10 @@ static int pcmcia_bus_suspend_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || p_dev->suspended) return 0; - return dpm_runtime_suspend(dev, PMSG_SUSPEND); + return runtime_suspend(dev); } static int pcmcia_bus_resume_callback(struct device *dev, void * _data) @@ -1212,10 +1351,10 @@ static int pcmcia_bus_resume_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || !p_dev->suspended) return 0; - dpm_runtime_resume(dev); + runtime_resume(dev); return 0; }