lkml.org 
[lkml]   [2003]   [Mar]   [29]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH 2.5] pcmcia: add struct pcmcia_device
This patch adds a struct pcmcia_device, and fills it with content for
every function of every pcmcia card inserted into the socket.

Unfortunately, information about the card can only be detected when
"cardmgr"[*] has told the pcmcia rsrc_mgr on what IO-ports and what
IO-mem is available for usage by the pcmcia core or drivers. So the
cards need to be re-scanned on completion of the pcmcia
initialization.

Re-starting cardmgr will not re-enable the resources.

Dominik

drivers/pcmcia/cistpl.c | 3
drivers/pcmcia/ds.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++--
include/pcmcia/ds.h | 24 +++++
3 files changed, 227 insertions(+), 7 deletions(-)


diff -ruN linux-original/drivers/pcmcia/cistpl.c linux/drivers/pcmcia/cistpl.c
--- linux-original/drivers/pcmcia/cistpl.c 2003-03-29 15:16:03.000000000 +0100
+++ linux/drivers/pcmcia/cistpl.c 2003-03-29 15:37:35.000000000 +0100
@@ -1392,6 +1392,7 @@
ret = pcmcia_parse_tuple(handle, &tuple, parse);
return ret;
}
+EXPORT_SYMBOL(read_tuple);

/*======================================================================

@@ -1451,4 +1452,4 @@

return CS_SUCCESS;
}
-
+EXPORT_SYMBOL(pcmcia_validate_cis);
diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c 2003-03-29 15:22:14.000000000 +0100
+++ linux/drivers/pcmcia/ds.c 2003-03-29 15:37:35.000000000 +0100
@@ -108,11 +108,16 @@
struct device *socket_dev;
struct list_head socket_list;
unsigned int socket_no; /* deprecated */
+ /* the PCMCIA devices connected to this socket (normally one, more
+ * for multifunction devices: */
+ struct list_head devices_list;
+ struct semaphore devices_list_sem;
};

#define SOCKET_PRESENT 0x01
#define SOCKET_BUSY 0x02
#define SOCKET_REMOVAL_PENDING 0x10
+#define SOCKET_ADDING_PENDING 0x20

/*====================================================================*/

@@ -121,6 +126,12 @@

static int major_dev = -1;

+/* this is set to 1 when the iomem/ioport configuration of rsrc_mgr.c
+ * is completed -- only then it is possible to decode the name, manfid,
+ * prodid, etc. of the pcmcia card.
+ */
+static int resources_available = 0;
+
/* list of all sockets registered with the pcmcia bus driver */
static DECLARE_RWSEM(bus_socket_list_rwsem);
static LIST_HEAD(bus_socket_list);
@@ -138,6 +149,10 @@

/*======================================================================*/

+int validate_cis(client_handle_t handle, cisinfo_t *info);
+int read_tuple(client_handle_t handle, cisdata_t code, void *parse);
+
+
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);

@@ -250,6 +265,152 @@
}
#endif

+/*====================================================================*/
+
+static int pcmcia_add_card(struct pcmcia_bus_socket *s)
+{
+ struct pcmcia_device *p_dev;
+ int ret;
+ unsigned int has_cis = 0;
+ unsigned int func = 0, no_funcs = 1;
+
+ down(&s->devices_list_sem);
+
+ next_device:
+ p_dev = kmalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
+ if (!p_dev) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ memset(p_dev, 0, sizeof(struct pcmcia_device));
+ p_dev->dev.bus = &pcmcia_bus_type;
+ p_dev->dev.parent = s->socket_dev;
+ p_dev->socket_no = s->socket_no;
+ p_dev->bus_sock = s;
+
+ /* first case: no resources are available, so we can't decode
+ * any device information */
+ if (!resources_available) {
+ sprintf (p_dev->dev.bus_id, "pcmcia%d:%d.?",
+ s->socket_dev->class_num, s->socket_no);
+ sprintf (p_dev->dev.name, "unknown");
+ ret = device_register(&p_dev->dev);
+ if (ret) {
+ kfree(p_dev);
+ goto out;
+ }
+ list_add(&p_dev->socket_device_list, &s->devices_list);
+ goto out;
+ }
+
+ /* if we got here for the first time, we need to check whether
+ * the CIS is valid etc. */
+ if (!func) {
+ cisinfo_t cisinfo;
+ ret = pcmcia_validate_cis(s->handle, &cisinfo);
+ if (ret || !cisinfo.Chains) {
+ printk(KERN_INFO "pcmcia: inserted card has invalid CIS.\n");
+ } else {
+ cistpl_longlink_mfc_t mfc;
+
+ has_cis = 1;
+ if (!read_tuple(s->handle, CISTPL_LONGLINK_MFC, &mfc))
+ no_funcs = mfc.nfn;
+ else
+ no_funcs = 1;
+ }
+ }
+
+ p_dev->func = func;
+ p_dev->has_cis = has_cis;
+
+ /* don't rely on this yet -- the second value will change from socket_no to
+ * the number within the socket_dev */
+ sprintf (p_dev->dev.bus_id, "pcmcia%d:%d.%d",
+ s->socket_dev->class_num, s->socket_no, p_dev->func);
+
+ /* read out device name "vers_1" */
+ if (!has_cis || (read_tuple(s->handle, CISTPL_VERS_1, &p_dev->vers1)))
+ p_dev->vers1.ns = 0;
+
+ if (p_dev->vers1.ns) {
+ char name[255] = "\0";
+ int i = 0;
+ for (i=0; i < p_dev->vers1.ns; i++) {
+ strcat(name, (p_dev->vers1.str + p_dev->vers1.ofs[i]));
+ strcat(name, " ");
+ }
+ snprintf(p_dev->dev.name, DEVICE_NAME_SIZE, "%s", name);
+ } else
+ sprintf (p_dev->dev.name, "unknown");
+
+ /* read out manufacture ID */
+ if (has_cis && !(read_tuple(s->handle, CISTPL_MANFID, &p_dev->manfid)))
+ p_dev->has_manfid = 1;
+
+ /* read out function ID -- rule of thumb: cards with no FUNCID, but with
+ * common memory device geometry information, are probably memory cards */
+ if (has_cis && !(read_tuple(s->handle, CISTPL_FUNCID, &p_dev->funcid)))
+ p_dev->has_funcid = 1;
+ else {
+ cistpl_device_geo_t devgeo;
+ if (has_cis && !(read_tuple(s->handle, CISTPL_DEVICE_GEO, &devgeo))) {
+ p_dev->has_funcid = 1;
+ p_dev->funcid.func = CISTPL_FUNCID_MEMORY;
+ }
+ }
+
+ /* register it both with the driver model core and with the bus socket list */
+ ret = device_register(&p_dev->dev);
+ if (ret) {
+ kfree(p_dev);
+ goto out;
+ }
+ list_add(&p_dev->socket_device_list, &s->devices_list);
+
+ /* if we got a multifunction device, we need to register more devices,
+ * so go back up */
+ func++;
+ if (no_funcs > func)
+ goto next_device;
+
+ out:
+ up(&s->devices_list_sem);
+ return ret;
+}
+
+static void pcmcia_remove_card(struct pcmcia_bus_socket *s)
+{
+ struct list_head *p1;
+ struct list_head *p2;
+
+ down(&s->devices_list_sem);
+ list_for_each_safe(p1, p2, &s->devices_list) {
+ struct pcmcia_device *p_dev = container_of(p1, struct pcmcia_device, socket_device_list);
+ device_unregister(&p_dev->dev);
+ list_del(&p_dev->socket_device_list);
+ kfree(p_dev);
+ }
+ up(&s->devices_list_sem);
+}
+
+static void pcmcia_rescan_cards(void)
+{
+ struct pcmcia_bus_socket *bus_sock;
+
+ down_read(&bus_socket_list_rwsem);
+
+ list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
+ if (list_empty(&bus_sock->devices_list))
+ continue;
+ pcmcia_remove_card(bus_sock);
+ pcmcia_add_card(bus_sock);
+ }
+
+ up_read(&bus_socket_list_rwsem);
+}
+
+
/*======================================================================

These manage a ring buffer of events pending for one user process
@@ -303,10 +464,20 @@
static void handle_removal(u_long sn)
{
struct pcmcia_bus_socket *s = get_socket_info_by_nr(sn);
+ pcmcia_remove_card(s);
handle_event(s, CS_EVENT_CARD_REMOVAL);
s->state &= ~SOCKET_REMOVAL_PENDING;
}

+static void handle_adding(u_long sn)
+{
+ struct pcmcia_bus_socket *s = get_socket_info_by_nr(sn);
+ pcmcia_add_card(s);
+ s->state |= SOCKET_PRESENT;
+ handle_event(s, CS_EVENT_CARD_INSERTION);
+ s->state &= ~SOCKET_ADDING_PENDING;
+}
+
/*======================================================================

The card status event handler.
@@ -329,14 +500,20 @@
if (!(s->state & SOCKET_REMOVAL_PENDING)) {
s->state |= SOCKET_REMOVAL_PENDING;
init_timer(&s->removal);
+ s->removal.function = &handle_removal;
s->removal.expires = jiffies + HZ/10;
add_timer(&s->removal);
}
break;

case CS_EVENT_CARD_INSERTION:
- s->state |= SOCKET_PRESENT;
- handle_event(s, event);
+ if (!(s->state & SOCKET_ADDING_PENDING)) {
+ s->state |= SOCKET_ADDING_PENDING;
+ init_timer(&s->removal);
+ s->removal.function = &handle_adding;
+ s->removal.expires = jiffies + HZ/10;
+ add_timer(&s->removal);
+ }
break;

case CS_EVENT_EJECTION_REQUEST:
@@ -569,7 +746,7 @@
else
s->state |= SOCKET_BUSY;
}
-
+
user = kmalloc(sizeof(user_info_t), GFP_KERNEL);
if (!user) return -ENOMEM;
user->event_tail = user->event_head = 0;
@@ -716,6 +893,7 @@
u_int size;
int ret, err;
ds_ioctl_arg_t buf;
+ static int ioctl_resources_status = 0;

DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);

@@ -749,10 +927,25 @@

if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size);

+ if ((cmd != DS_ADJUST_RESOURCE_INFO) && (ioctl_resources_status)) {
+ if (ioctl_resources_status == 1) {
+ resources_available = 1;
+ pcmcia_rescan_cards();
+ }
+ ioctl_resources_status = 0;
+ }
+
switch (cmd) {
case DS_ADJUST_RESOURCE_INFO:
- ret = pcmcia_adjust_resource_info(s->handle, &buf.adjust);
- break;
+ if (resources_available) {
+ /* gotta go */
+ ioctl_resources_status = 2;
+ resources_available = 0;
+ pcmcia_rescan_cards();
+ } else if (!ioctl_resources_status)
+ ioctl_resources_status = 1;
+ ret = pcmcia_adjust_resource_info(s->handle, &buf.adjust);
+ break;
case DS_GET_CARD_SERVICES_INFO:
ret = pcmcia_get_card_services_info(&buf.servinfo);
break;
@@ -924,8 +1117,9 @@
/* initialize data */
s->socket_dev = dev;
s->removal.data = s->socket_no;
- s->removal.function = &handle_removal;
init_timer(&s->removal);
+ init_MUTEX(&s->devices_list_sem);
+ INIT_LIST_HEAD(&s->devices_list);

/* Set up hotline to Card Services */
client_reg.dev_info = bind.dev_info = &dev_info;
@@ -991,6 +1185,7 @@
list_for_each_safe(list_loop, tmp_storage, &bus_socket_list) {
struct pcmcia_bus_socket *bus_sock = container_of(list_loop, struct pcmcia_bus_socket, socket_list);
if (bus_sock->socket_dev == dev) {
+ pcmcia_remove_card(bus_sock);
pcmcia_deregister_client(bus_sock->handle);
list_del(&bus_sock->socket_list);
kfree(bus_sock);
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h 2003-03-29 15:22:14.000000000 +0100
+++ linux/include/pcmcia/ds.h 2003-03-29 15:37:35.000000000 +0100
@@ -165,5 +165,29 @@
/* error reporting */
void cs_error(client_handle_t handle, int func, int ret);

+struct pcmcia_bus_socket;
+
+struct pcmcia_device {
+ unsigned int socket_no; /* deprecated */
+ struct pcmcia_bus_socket *bus_sock;
+ struct list_head socket_device_list;
+ unsigned int func;
+
+ /* cis information */
+ unsigned int has_cis;
+ cistpl_vers_1_t vers1;
+
+ unsigned int has_manfid;
+ cistpl_manfid_t manfid;
+
+ unsigned int has_funcid;
+ cistpl_funcid_t funcid;
+
+ struct device dev;
+};
+
+#define to_pcmcia_dev(n) container_of(n, struct pcmcia_device, dev)
+#define to_pcmcia_drv(n) container_of(n, struct pcmcia_driver, drv)
+
#endif /* __KERNEL__ */
#endif /* _LINUX_DS_H */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2005-03-22 13:34    [W:0.254 / U:0.344 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site