[PATCH] pcmcia driver model support [1/5]
Adam Belay
ambx1 at neo.rr.com
Thu Aug 5 18:20:26 EDT 2004
Hi,
This patch set is an updated version from the last and is against 2.6.8-rc3. It
includes suggestions from Dominik and an independent pcmcia bugfix. I would
appreciate any comments.
Thanks,
Adam
[PCMCIA] implement driver model support
This patch adds driver model support to the pcmcia bus driver.
--- a/drivers/pcmcia/cs_internal.h 2004-08-05 13:05:45.000000000 +0000
+++ b/drivers/pcmcia/cs_internal.h 2004-08-05 13:08:38.000000000 +0000
@@ -33,6 +33,7 @@
typedef struct client_t {
u_short client_magic;
struct pcmcia_socket *Socket;
+ struct device *device;
u_char Function;
dev_info_t dev_info;
u_int Attributes;
--- a/drivers/pcmcia/cs.c 2004-08-05 13:05:45.000000000 +0000
+++ b/drivers/pcmcia/cs.c 2004-08-05 13:08:38.000000000 +0000
@@ -1160,6 +1160,22 @@
#endif
+/*=====================================================================
+
+ Return the driver model device associated with a card..
+
+======================================================================*/
+
+struct device *pcmcia_lookup_device(client_handle_t handle)
+{
+ if (CHECK_HANDLE(handle))
+ return NULL;
+
+ return handle->device;
+}
+
+EXPORT_SYMBOL(pcmcia_lookup_device);
+
/*======================================================================
Get the current socket state bits. We don't support the latched
@@ -2154,6 +2170,7 @@
EXPORT_SYMBOL(pcmcia_suspend_card);
EXPORT_SYMBOL(pcmcia_validate_cis);
EXPORT_SYMBOL(pcmcia_write_memory);
+EXPORT_SYMBOL(read_tuple);
EXPORT_SYMBOL(dead_socket);
EXPORT_SYMBOL(MTDHelperEntry);
--- a/drivers/pcmcia/ds.c 2004-08-05 13:05:45.000000000 +0000
+++ b/drivers/pcmcia/ds.c 2004-08-05 13:12:03.000000000 +0000
@@ -119,6 +119,8 @@
struct work_struct removal;
socket_bind_t *bind;
struct pcmcia_socket *parent;
+ struct list_head devices;
+ struct semaphore device_mutex;
};
#define DS_SOCKET_PRESENT 0x01
@@ -164,6 +166,7 @@
client->client_magic = CLIENT_MAGIC;
strlcpy(client->dev_info, (char *)req->dev_info, DEV_NAME_LEN);
client->Socket = s;
+ client->device = &req->device->dev;
client->Function = req->Function;
client->state = CLIENT_UNBOUND;
client->erase_busy.next = &client->erase_busy;
@@ -354,6 +357,8 @@
/*======================================================================*/
+static struct pcmcia_device * get_pcmcia_device (struct pcmcia_bus_socket *s, int function);
+static void put_pcmcia_device(struct pcmcia_device *dev);
static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
static struct pcmcia_bus_socket * get_socket_info_by_nr(unsigned int nr);
@@ -430,6 +435,150 @@
/*======================================================================
+ sysfs support
+
+======================================================================*/
+
+static ssize_t
+pcmcia_show_product_string(struct pcmcia_device *dev, char *buf, int index)
+{
+ char *str = buf;
+
+ if (dev->id_mask & DEVICE_HAS_VERSION_INFO && index < dev->vers_1.ns)
+ str += sprintf(str,"%s\n",
+ dev->vers_1.str+dev->vers_1.ofs[index]);
+ else
+ str += sprintf(str,"\n");
+ return (str - buf);
+}
+
+#define pcmcia_prod_str_attr(field, index) \
+static ssize_t \
+show_##field (struct device *dmdev, char *buf) \
+{ \
+ struct pcmcia_device *dev; \
+ \
+ dev = to_pcmcia_device (dmdev); \
+ return pcmcia_show_product_string(dev,buf,index); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+pcmcia_prod_str_attr(prod_str0,0);
+pcmcia_prod_str_attr(prod_str1,1);
+pcmcia_prod_str_attr(prod_str2,2);
+pcmcia_prod_str_attr(prod_str3,3);
+
+static ssize_t
+pcmcia_show_manfid(struct device *dmdev, char *buf)
+{
+ struct pcmcia_device *dev = to_pcmcia_device(dmdev);
+ char *str = buf;
+
+ if (dev->id_mask & DEVICE_HAS_MANF_INFO)
+ str += sprintf(str,"0x%04x, 0x%04x\n",
+ dev->manfid.manf,
+ dev->manfid.card);
+ else
+ str += sprintf(str,"\n");
+ return (str - buf);
+}
+
+static DEVICE_ATTR(manfid,S_IRUGO,pcmcia_show_manfid,NULL);
+
+static void pcmcia_sysfs_attach(struct pcmcia_device *dev)
+{
+ device_create_file (&dev->dev, &dev_attr_prod_str0);
+ device_create_file (&dev->dev, &dev_attr_prod_str1);
+ device_create_file (&dev->dev, &dev_attr_prod_str2);
+ device_create_file (&dev->dev, &dev_attr_prod_str3);
+ device_create_file (&dev->dev, &dev_attr_manfid);
+}
+
+/*======================================================================
+
+ device addition, removal, and hotplug functions for the driver model
+
+======================================================================*/
+
+static void pcmcia_bus_release_device(struct device *pdev)
+{
+ struct pcmcia_device *dev = to_pcmcia_device(pdev);
+ kfree(dev);
+}
+
+static int pcmcia_bus_insert_card(struct pcmcia_bus_socket *s)
+{
+ struct pcmcia_device *dev;
+ int i, ret, function_count = 0, has_cis = 0;
+ cisinfo_t cisinfo;
+
+ if (!(s->state & DS_SOCKET_PRESENT))
+ return CS_NO_CARD;
+
+ down(&s->device_mutex);
+ if (!list_empty(&s->devices)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ret = pcmcia_validate_cis(s->handle, &cisinfo);
+ if (ret)
+ goto out;
+
+ if (cisinfo.Chains) {
+ cistpl_longlink_mfc_t mfc;
+
+ has_cis = 1;
+ if (!read_tuple(s->handle, CISTPL_LONGLINK_MFC, &mfc))
+ function_count = mfc.nfn;
+ }
+
+ for (i=0; i<=function_count; i++) {
+ dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
+ if (!dev)
+ continue;
+
+ memset(dev, 0, sizeof(struct pcmcia_device));
+ dev->socket = s;
+ dev->dev.parent = s->parent->dev.dev;
+ dev->dev.bus = &pcmcia_bus_type;
+ dev->dev.release = &pcmcia_bus_release_device;
+ snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%u:%u", s->parent->sock, i);
+
+ dev->function = i;
+ if (has_cis) {
+ if (!read_tuple(s->handle, CISTPL_VERS_1, &dev->vers_1))
+ dev->id_mask |= DEVICE_HAS_VERSION_INFO;
+ if (!read_tuple(s->handle, CISTPL_MANFID, &dev->manfid))
+ dev->id_mask |= DEVICE_HAS_MANF_INFO;
+ }
+
+ ret = device_register(&dev->dev);
+ if (!ret) {
+ list_add_tail(&dev->device_list, &s->devices);
+ pcmcia_sysfs_attach(dev);
+ } else
+ kfree(dev);
+ }
+
+out:
+ up(&s->device_mutex);
+ return ret;
+}
+
+static void pcmcia_bus_remove_card(struct pcmcia_bus_socket *s)
+{
+ struct pcmcia_device *dev, *tmp;
+ down(&s->device_mutex);
+ list_for_each_entry_safe(dev, tmp, &s->devices, device_list) {
+ list_del(&dev->device_list);
+ device_unregister(&dev->dev);
+ }
+ up(&s->device_mutex);
+}
+
+/*======================================================================
+
These manage a ring buffer of events pending for one user process
======================================================================*/
@@ -501,6 +650,7 @@
case CS_EVENT_CARD_REMOVAL:
s->state &= ~DS_SOCKET_PRESENT;
+ pcmcia_bus_remove_card(s);
if (!(s->state & DS_SOCKET_REMOVAL_PENDING)) {
s->state |= DS_SOCKET_REMOVAL_PENDING;
schedule_delayed_work(&s->removal, HZ/10);
@@ -562,6 +712,7 @@
static int bind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
{
struct pcmcia_driver *driver;
+ struct pcmcia_device *device;
socket_bind_t *b;
bind_req_t bind_req;
int ret;
@@ -587,7 +738,12 @@
if (!try_module_get(driver->owner))
return -EINVAL;
+ device = get_pcmcia_device(s, bind_info->function);
+ if (!device)
+ return -EINVAL;
+
bind_req.Socket = s->parent;
+ bind_req.device = device;
bind_req.Function = bind_info->function;
bind_req.dev_info = (dev_info_t *) driver->drv.name;
ret = pcmcia_bind_device(&bind_req);
@@ -614,16 +770,25 @@
b->next = s->bind;
s->bind = b;
+ down_write(&device->dev.bus->subsys.rwsem);
+ device->dev.driver = &driver->drv;
+
if (driver->attach) {
b->instance = driver->attach();
if (b->instance == NULL) {
printk(KERN_NOTICE "ds: unable to create instance "
"of '%s'!\n", (char *)bind_info->dev_info);
module_put(driver->owner);
+ device->dev.driver = NULL;
+ up_write(&device->dev.bus->subsys.rwsem);
return -ENODEV;
}
}
+ device_bind_driver(&device->dev);
+ up_write(&device->dev.bus->subsys.rwsem);
+ put_pcmcia_device(device);
+
return 0;
} /* bind_request */
@@ -699,6 +864,7 @@
static int unbind_request(struct pcmcia_bus_socket *s, bind_info_t *bind_info)
{
socket_bind_t **b, *c;
+ struct pcmcia_device *device;
ds_dbg(2, "unbind_request(%d, '%s')\n", s->parent->sock,
(char *)bind_info->dev_info);
@@ -710,6 +876,14 @@
if (*b == NULL)
return -ENODEV;
+ device = get_pcmcia_device(s, bind_info->function);
+ if (device) {
+ down_write(&device->dev.bus->subsys.rwsem);
+ device_release_driver(&device->dev);
+ up_write(&device->dev.bus->subsys.rwsem);
+ put_pcmcia_device(device);
+ }
+
c = *b;
c->driver->use_count--;
if (c->driver->detach) {
@@ -1005,6 +1179,7 @@
break;
case DS_BIND_REQUEST:
if (!capable(CAP_SYS_ADMIN)) return -EPERM;
+ pcmcia_bus_insert_card(s);
err = bind_request(s, &buf.bind_info);
break;
case DS_GET_DEVICE_INFO:
@@ -1087,6 +1262,8 @@
init_waitqueue_head(&s->request);
/* initialize data */
+ INIT_LIST_HEAD(&s->devices);
+ init_MUTEX(&s->device_mutex);
INIT_WORK(&s->removal, handle_removal, s);
s->parent = socket;
@@ -1135,6 +1312,8 @@
pcmcia_deregister_client(socket->pcmcia->handle);
+ pcmcia_bus_remove_card(socket->pcmcia);
+
socket->pcmcia->state |= DS_SOCKET_DEAD;
pcmcia_put_bus_socket(socket->pcmcia);
socket->pcmcia = NULL;
@@ -1154,6 +1333,7 @@
struct bus_type pcmcia_bus_type = {
.name = "pcmcia",
};
+
EXPORT_SYMBOL(pcmcia_bus_type);
@@ -1186,7 +1366,6 @@
static void __exit exit_pcmcia_bus(void)
{
- class_interface_unregister(&pcmcia_bus_interface);
#ifdef CONFIG_PROC_FS
if (proc_pccard) {
@@ -1197,6 +1376,7 @@
if (major_dev != -1)
unregister_chrdev(major_dev, "pcmcia");
+ class_interface_unregister(&pcmcia_bus_interface);
bus_unregister(&pcmcia_bus_type);
}
module_exit(exit_pcmcia_bus);
@@ -1244,3 +1424,24 @@
return cmp.drv;
return NULL;
}
+
+static struct pcmcia_device * get_pcmcia_device (struct pcmcia_bus_socket *s, int function)
+{
+ struct pcmcia_device *dev, *tmp;
+ down(&s->device_mutex);
+ list_for_each_entry_safe(dev, tmp, &s->devices, device_list) {
+ if (dev->function == function) {
+ if (!get_device(&dev->dev))
+ break;
+ up(&s->device_mutex);
+ return dev;
+ }
+ }
+ up(&s->device_mutex);
+ return NULL;
+}
+
+static void put_pcmcia_device(struct pcmcia_device *dev)
+{
+ put_device(&dev->dev);
+}
--- a/include/pcmcia/cs.h 2004-08-05 13:02:07.000000000 +0000
+++ b/include/pcmcia/cs.h 2004-08-05 13:08:38.000000000 +0000
@@ -318,6 +318,7 @@
/* Special stuff for binding drivers to sockets */
typedef struct bind_req_t {
struct pcmcia_socket *Socket;
+ struct pcmcia_device *device;
u_char Function;
dev_info_t *dev_info;
} bind_req_t;
@@ -452,6 +453,7 @@
int pcmcia_set_event_mask(client_handle_t handle, eventmask_t *mask);
int pcmcia_report_error(client_handle_t handle, error_info_t *err);
struct pci_bus *pcmcia_lookup_bus(client_handle_t handle);
+struct device *pcmcia_lookup_device(client_handle_t handle);
/* rsrc_mgr.c */
int pcmcia_adjust_resource_info(client_handle_t handle, adjust_t *adj);
--- a/include/pcmcia/ds.h 2004-08-05 13:02:07.000000000 +0000
+++ b/include/pcmcia/ds.h 2004-08-05 13:08:38.000000000 +0000
@@ -151,6 +151,21 @@
struct device_driver drv;
};
+struct pcmcia_device {
+ struct device dev;
+ struct pcmcia_bus_socket *socket;
+ struct list_head device_list;
+ int function;
+ int id_mask;
+ cistpl_vers_1_t vers_1;
+ cistpl_manfid_t manfid;
+};
+
+#define to_pcmcia_device(n) container_of(n, struct pcmcia_device, dev)
+
+#define DEVICE_HAS_VERSION_INFO 0x0001
+#define DEVICE_HAS_MANF_INFO 0x0002
+
/* driver registration */
int pcmcia_register_driver(struct pcmcia_driver *driver);
void pcmcia_unregister_driver(struct pcmcia_driver *driver);
More information about the linux-pcmcia
mailing list