public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2.5] pcmcia: add struct pcmcia_device
@ 2003-03-29 14:45 Dominik Brodowski
  2003-03-30  9:12 ` Russell King
  0 siblings, 1 reply; 2+ messages in thread
From: Dominik Brodowski @ 2003-03-29 14:45 UTC (permalink / raw)
  To: rmk; +Cc: linux-kernel

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 */

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH 2.5] pcmcia: add struct pcmcia_device
  2003-03-29 14:45 [PATCH 2.5] pcmcia: add struct pcmcia_device Dominik Brodowski
@ 2003-03-30  9:12 ` Russell King
  0 siblings, 0 replies; 2+ messages in thread
From: Russell King @ 2003-03-30  9:12 UTC (permalink / raw)
  To: Dominik Brodowski; +Cc: linux-kernel

Ok, some comments on this patch.

On Sat, Mar 29, 2003 at 03:45:47PM +0100, Dominik Brodowski wrote:
> 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;
> +

This isn't actually such a clear-cut situation.  Statically mapped
sockets like SA11x0-based stuff are able to access the card with no
resources loaded - in fact, there are no resources to be loaded.

One of my patches (which was the troublesome pcmcia-10 patch from akpm's
patch set) changes the way we deal with the CIS.  For windowed sockets,
when a card detected, we immediately try to claim a resource for the CIS
window.  If this fails, we whinge about "PCMCIA: insert failed" but
continue anyway.  We could fail at this point.

When resources are changed, I simulate a card removal + insertion request
so that the CIS gets re-setup.  The longer term solution, imho, is to
change the socket setup/reset/unreset code in cs.c to be more of a state
machine which could be re-started when resources become available.

> +	/* 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;
> +	}

I'm not completely sure we want to add a device in this case - if we don't
have any resources, there isn't much point in making the device available
to drivers.

> +	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");

I have a memory-type pcmcia card here, and I believe it doesn't have any
CIS what so ever, so this would fail.

-- 
Russell King (rmk@arm.linux.org.uk)                The developer of ARM Linux
             http://www.arm.linux.org.uk/personal/aboutme.html


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2003-03-30  9:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-03-29 14:45 [PATCH 2.5] pcmcia: add struct pcmcia_device Dominik Brodowski
2003-03-30  9:12 ` Russell King

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox