[RE-DIFFED PATCHES] Re: [2nd RFC] setting resources available to pcmcia

Dominik Brodowski linux at brodo.de
Sat Jun 21 00:49:01 BST 2003


Re-diffed and partly re-worked as the work ist gone.

Please consider applying on these patches on top of the other ones sent
earlier.

	Dominik
-------------- next part --------------
This patch adds a struct pcmcia_device, and fills it with content for
every function of every pcmcia card inserted into the socket as long
as there are resources to play with (see previous patch)

 drivers/pcmcia/ds.c |  174 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 include/pcmcia/ds.h |   25 +++++++
 2 files changed, 196 insertions(+), 3 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-06-20 23:29:09.000000000 +0200
+++ linux/drivers/pcmcia/ds.c	2003-06-20 23:28:54.000000000 +0200
@@ -35,7 +35,6 @@
 #include <linux/poll.h>
 #include <linux/pci.h>
 #include <linux/list.h>
-#include <linux/workqueue.h>
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -93,10 +92,16 @@
 	socket_bind_t		*bind;
 	struct device		*socket_dev;
 	struct pcmcia_socket	*parent;
+
+	/* 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_ADDING_PENDING	0x20
 
 /*====================================================================*/
 
@@ -126,6 +131,9 @@
 
 /*======================================================================*/
 
+extern int validate_cis(client_handle_t handle, cisinfo_t *info);
+extern 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);
 
@@ -182,6 +190,141 @@
 }
 #endif
 
+/*********************** device adding / removing ***************************/
+
+
+static int pcmcia_add_card(struct pcmcia_socket *s)
+{
+	struct pcmcia_device *p_dev;
+	int ret;
+	unsigned int has_cis = 0;
+	unsigned int func = 0, no_funcs = 1;
+
+	down(&s->pcmcia->devices_list_sem);
+
+ next_device:
+	DEBUG(0, "ds: pcmcia_add_card(%i):", func);
+
+	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->dev.dev;
+	p_dev->socket = s;
+
+	/* first case: no resources are available, so we can't decode
+	 * any device information */
+	if (!((dynamic_resources_available) || 
+	     (s->features & SS_CAP_HAS_RESOURCES))) {
+		DEBUG(0, "no resources available\n");
+		sprintf (p_dev->dev.bus_id, "pcmcia%d.?", 
+			 s->sock);
+		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->pcmcia->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->pcmcia->handle, &cisinfo);
+		if (ret || !cisinfo.Chains) {
+			DEBUG(0, "invalid CIS\n");
+		} else {
+			cistpl_longlink_mfc_t mfc;
+
+			DEBUG(0, "has CIS\n");
+			has_cis = 1;
+			if (!read_tuple(s->pcmcia->handle, CISTPL_LONGLINK_MFC, &mfc))
+				no_funcs = mfc.nfn;
+			else
+				no_funcs = 1;
+		}
+	}
+
+	p_dev->func = func;
+	p_dev->has_cis = has_cis;
+
+	sprintf (p_dev->dev.bus_id, "pcmcia%d.%d", s->sock, p_dev->func);
+
+	/* read out device name "vers_1" */
+	if (!has_cis || (read_tuple(s->pcmcia->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->pcmcia->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->pcmcia->handle, CISTPL_FUNCID, &p_dev->funcid)))
+		p_dev->has_funcid = 1;
+	else {
+		cistpl_device_geo_t devgeo;
+		if (has_cis && !(read_tuple(s->pcmcia->handle, CISTPL_DEVICE_GEO, &devgeo))) {
+			DEBUG(0, "(mem device geometry) leads to funcid_memory\n");
+			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->pcmcia->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->pcmcia->devices_list_sem);
+	return ret;
+}
+
+static void pcmcia_remove_card(struct pcmcia_socket *s)
+{
+	struct list_head *p1;
+	struct list_head *p2;
+
+	DEBUG(0, "ds: pcmcia_remove_card()\n");
+
+	down(&s->pcmcia->devices_list_sem);
+	list_for_each_safe(p1, p2, &s->pcmcia->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->pcmcia->devices_list_sem);
+}
+
+
 /*======================================================================
 
     These manage a ring buffer of events pending for one user process
@@ -253,11 +396,13 @@
 	
     case CS_EVENT_CARD_REMOVAL:
 	s->state &= ~SOCKET_PRESENT;
+	pcmcia_remove_card(s->parent);
 	handle_event(s, event);
 	break;
 	
     case CS_EVENT_CARD_INSERTION:
 	s->state |= SOCKET_PRESENT;
+	pcmcia_add_card(s->parent);
 	handle_event(s, event);
 	break;
 
@@ -634,6 +779,9 @@
 
 /*====================================================================*/
 
+extern struct rw_semaphore pcmcia_socket_list_rwsem;
+extern struct list_head pcmcia_socket_list;
+
 static int ds_ioctl(struct inode * inode, struct file * file,
 		    u_int cmd, u_long arg)
 {
@@ -678,12 +826,31 @@
     if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size);
     
     if ((cmd != DS_ADJUST_RESOURCE_INFO) && (!dyn_res_status)) {
+	    struct pcmcia_socket *sock;
+
 	    dyn_res_status = 2;
 
 	    /* resources have just become available, and will stay available forever...
 	     * well, at least as long as the kernel is rebooted [pcmcia isn't modular
 	     * at the moment] */
 	    dynamic_resources_available = 1;
+
+	    /* rescan the sockets, if necessary */
+	    down_read(&pcmcia_socket_list_rwsem);
+
+	    list_for_each_entry(sock, &pcmcia_socket_list, socket_list) {
+		    if (!sock->pcmcia)
+			    continue;
+		    if (sock->features & SS_CAP_HAS_RESOURCES)
+			    continue;
+		    if (list_empty(&sock->pcmcia->devices_list))
+			    continue;
+		    pcmcia_remove_card(sock);
+		    pcmcia_add_card(sock);
+	    }
+
+	    up_read(&pcmcia_socket_list_rwsem);
+
     }
 
     switch (cmd) {
@@ -850,6 +1017,8 @@
 	/* initialize data */
 	s->socket_dev = socket->dev.dev;
 	s->parent = socket;
+	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;
@@ -892,8 +1061,7 @@
 	if (!socket || !socket->pcmcia)
 		return;
 
-	flush_scheduled_work();
-
+	pcmcia_remove_card(socket);
 	pcmcia_deregister_client(socket->pcmcia->handle);
 
 	kfree(socket->pcmcia);
diff -ruN linux-original/include/pcmcia/ds.h linux/include/pcmcia/ds.h
--- linux-original/include/pcmcia/ds.h	2003-06-20 08:48:47.000000000 +0200
+++ linux/include/pcmcia/ds.h	2003-06-20 23:28:31.000000000 +0200
@@ -159,5 +159,30 @@
 /* error reporting */
 void cs_error(client_handle_t handle, int func, int ret);
 
+
+struct pcmcia_socket;
+
+struct pcmcia_device {
+	struct pcmcia_socket		*socket;
+
+	struct list_head		socket_device_list;
+
+	unsigned int			func;
+	struct device			dev;
+
+	/* 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;
+};
+
+#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 */
-------------- next part --------------
Change the license of ds.c to GPL only.

 ds.c |   35 ++++++++++-------------------------
 1 files changed, 10 insertions(+), 25 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-06-20 23:14:59.000000000 +0200
+++ linux/drivers/pcmcia/ds.c	2003-06-20 23:19:23.000000000 +0200
@@ -1,34 +1,19 @@
 /*======================================================================
 
     PC Card Driver Services
-    
-    ds.c 1.112 2001/10/13 00:08:28
-    
-    The contents of this file are subject to the Mozilla Public
-    License Version 1.1 (the "License"); you may not use this file
-    except in compliance with the License. You may obtain a copy of
-    the License at http://www.mozilla.org/MPL/
-
-    Software distributed under the License is distributed on an "AS
-    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-    implied. See the License for the specific language governing
-    rights and limitations under the License.
+   
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License version 2 as
+    published by the Free Software Foundation.
 
     The initial developer of the original code is David A. Hinds
     <dahinds at users.sourceforge.net>.  Portions created by David A. Hinds
     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 
-    Alternatively, the contents of this file may be used under the
-    terms of the GNU General Public License version 2 (the "GPL"), in
-    which case the provisions of the GPL are applicable instead of the
-    above.  If you wish to allow the use of your version of this file
-    only under the terms of the GPL and not to allow others to use
-    your version of this file under the MPL, indicate your decision
-    by deleting the provisions above and replace them with the notice
-    and other provisions required by the GPL.  If you do not delete
-    the provisions above, a recipient may use your version of this
-    file under either the MPL or the GPL.
-    
+    Adaption to the Linux driver model, hotplug and module functions
+    of kernel 2.5 are Copyright (C) 2003 Dominik Brodowski. All Rights 
+    Reserved
+
 ======================================================================*/
 
 #include <linux/kernel.h>
@@ -64,9 +49,9 @@
 
 /* Module parameters */
 
-MODULE_AUTHOR("David Hinds <dahinds at users.sourceforge.net>");
+MODULE_AUTHOR("David Hinds <dahinds at users.sourceforge.net>, Dominik Brodowski <linux at brodo.de>");
 MODULE_DESCRIPTION("PCMCIA Driver Services " CS_RELEASE);
-MODULE_LICENSE("Dual MPL/GPL");
+MODULE_LICENSE("GPL");
 
 #define INT_MODULE_PARM(n, v) static int n = v; MODULE_PARM(n, "i")
 
-------------- next part --------------
To do anything proper in the kernel, it needs to be known whether
a) the socket driver only uses a fixed area of IOPORTS and IOMEM,
therefore can't clash with other e.g. ISA devices, or
b) the resource database (for all other sockets) has been filled with
enough information [by cardmgr]

 drivers/pcmcia/ds.c |   31 +++++++++++++++++++++++++++++--
 include/pcmcia/ss.h |    1 +
 2 files changed, 30 insertions(+), 2 deletions(-)

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c	2003-06-20 23:20:32.000000000 +0200
+++ linux/drivers/pcmcia/ds.c	2003-06-20 23:20:46.000000000 +0200
@@ -109,6 +109,14 @@
 
 /*====================================================================*/
 
+/* As soon as the iomem/ioport/irq configuration is done by cardmgr
+ * (3.2.x), this is set to 1.
+ */
+static int dynamic_resources_available = 0;
+
+
+/*====================================================================*/
+
 void cs_error(client_handle_t handle, int func, int ret)
 {
 	error_info_t err = { func, ret };
@@ -634,6 +642,8 @@
     u_int size;
     int ret, err;
     ds_ioctl_arg_t buf;
+    static int dyn_res_status = 1;
+
 
     DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);
     
@@ -667,10 +677,27 @@
     
     if (cmd & IOC_IN) copy_from_user((char *)&buf, (char *)arg, size);
     
+    if ((cmd != DS_ADJUST_RESOURCE_INFO) && (!dyn_res_status)) {
+	    dyn_res_status = 2;
+
+	    /* resources have just become available, and will stay available forever...
+	     * well, at least as long as the kernel is rebooted [pcmcia isn't modular
+	     * at the moment] */
+	    dynamic_resources_available = 1;
+    }
+
     switch (cmd) {
     case DS_ADJUST_RESOURCE_INFO:
-	ret = pcmcia_adjust_resource_info(s->handle, &buf.adjust);
-	break;
+    {
+	    if (dyn_res_status == 2) {
+		    printk(KERN_INFO "pcmcia: warning: DS_ADJUST_RESOURCE_INFO calls don't do anything...\n");
+		    dyn_res_status++;
+		    break;
+	    }
+	    dyn_res_status = 0;
+	    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;
diff -ruN linux-original/include/pcmcia/ss.h linux/include/pcmcia/ss.h
--- linux-original/include/pcmcia/ss.h	2003-06-20 08:48:47.000000000 +0200
+++ linux/include/pcmcia/ss.h	2003-06-20 23:20:46.000000000 +0200
@@ -57,6 +57,7 @@
 #define SS_CAP_VIRTUAL_BUS	0x0002
 #define SS_CAP_MEM_ALIGN	0x0004
 #define SS_CAP_STATIC_MAP	0x0008
+#define SS_CAP_HAS_RESOURCES	0x0010
 #define SS_CAP_PCCARD		0x4000
 #define SS_CAP_CARDBUS		0x8000
 


More information about the linux-pcmcia mailing list