[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