Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 54163
b: refs/heads/master
c: 6179b55
h: refs/heads/master
i:
  54161: a4cb183
  54159: bd13c37
v: v3
  • Loading branch information
Bernhard Walle authored and Linus Torvalds committed May 7, 2007
1 parent 4af6bf1 commit 23c7d2c
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 2 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 02c83595b86480ee4d61665beb13f76685d40239
refs/heads/master: 6179b5562d5d17c7c09b54cb11dd925ca308d7a9
30 changes: 30 additions & 0 deletions trunk/Documentation/pcmcia/driver.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
PCMCIA Driver
-------------


sysfs
-----

New PCMCIA IDs may be added to a device driver pcmcia_device_id table at
runtime as shown below:

echo "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]" > \
/sys/bus/pcmcia/drivers/{driver}/new_id

All fields are passed in as hexadecimal values (no leading 0x).
The meaning is described in the PCMCIA specification, the match_flags is
a bitwise or-ed combination from PCMCIA_DEV_ID_MATCH_* constants
defined in include/linux/mod_devicetable.h.

Once added, the driver probe routine will be invoked for any unclaimed
PCMCIA device listed in its (newly updated) pcmcia_device_id list.

A common use-case is to add a new device according to the manufacturer ID
and the card ID (form the manf_id and card_id file in the device tree).
For this, just use:

echo "0x3 manf_id card_id 0 0 0 0 0 0 0" > \
/sys/bus/pcmcia/drivers/{driver}/new_id

after loading the driver.
113 changes: 112 additions & 1 deletion trunk/drivers/pcmcia/ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,98 @@ 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
*
* Registers a PCMCIA driver with the PCMCIA bus core.
*/
int pcmcia_register_driver(struct pcmcia_driver *driver)
{
int error;

if (!driver)
return -EINVAL;

Expand All @@ -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);

Expand All @@ -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);

Expand Down Expand Up @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions trunk/include/pcmcia/ds.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ typedef struct dev_node_t {
struct pcmcia_socket;
struct config_t;

struct pcmcia_dynids {
spinlock_t lock;
struct list_head list;
};

struct pcmcia_driver {
int (*probe) (struct pcmcia_device *dev);
void (*remove) (struct pcmcia_device *dev);
Expand All @@ -118,6 +123,7 @@ struct pcmcia_driver {
struct module *owner;
struct pcmcia_device_id *id_table;
struct device_driver drv;
struct pcmcia_dynids dynids;
};

/* driver registration */
Expand Down

0 comments on commit 23c7d2c

Please sign in to comment.