Răsfoiți Sursa

Merge git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia-2.6: (49 commits)
  pcmcia: validate late-added resources
  pcmcia: allow for extension of resource interval
  pcmcia: remove useless msleep in ds.c
  pcmcia: use read_cis_mem return value
  pcmcia: handle error in serial_cs config calls
  pcmcia: add locking to pcmcia_{read,write}_cis_mem
  pcmcia: avoid prod_id memleak
  pcmcia: avoid sysfs-related lockup for cardbus
  pcmcia: use state machine for extended requery
  pcmcia: delay re-scanning and re-querying of PCMCIA bus
  pcmcia: use pccardd to handle eject, insert, suspend and resume requests
  pcmcia: use ops_mutex for rsrc_{mgr,nonstatic} locking
  pcmcia: use mutex for dynid lock
  pcmcia: assert locking to struct pcmcia_device
  pcmcia: add locking documentation
  pcmcia: simplify locking
  pcmcia: add locking to struct pcmcia_socket->pcmcia_state()
  pcmcia: protect s->device_count
  pcmcia: properly lock skt->irq, skt->irq_mask
  pcmcia: lock ops->set_socket
  ...
Linus Torvalds 15 ani în urmă
părinte
comite
8d37a371b6

+ 118 - 0
Documentation/pcmcia/locking.txt

@@ -0,0 +1,118 @@
+This file explains the locking and exclusion scheme used in the PCCARD
+and PCMCIA subsystems.
+
+
+A) Overview, Locking Hierarchy:
+===============================
+
+pcmcia_socket_list_rwsem	- protects only the list of sockets
+- skt_mutex			- serializes card insert / ejection
+  - ops_mutex			- serializes socket operation
+
+
+B) Exclusion
+============
+
+The following functions and callbacks to struct pcmcia_socket must
+be called with "skt_mutex" held:
+
+	socket_detect_change()
+	send_event()
+	socket_reset()
+	socket_shutdown()
+	socket_setup()
+	socket_remove()
+	socket_insert()
+	socket_early_resume()
+	socket_late_resume()
+	socket_resume()
+	socket_suspend()
+
+	struct pcmcia_callback	*callback
+
+The following functions and callbacks to struct pcmcia_socket must
+be called with "ops_mutex" held:
+
+	socket_reset()
+	socket_setup()
+
+	struct pccard_operations	*ops
+	struct pccard_resource_ops	*resource_ops;
+
+Note that send_event() and struct pcmcia_callback *callback must not be
+called with "ops_mutex" held.
+
+
+C) Protection
+=============
+
+1. Global Data:
+---------------
+struct list_head	pcmcia_socket_list;
+
+protected by pcmcia_socket_list_rwsem;
+
+
+2. Per-Socket Data:
+-------------------
+The resource_ops and their data are protected by ops_mutex.
+
+The "main" struct pcmcia_socket is protected as follows (read-only fields
+or single-use fields not mentioned):
+
+- by pcmcia_socket_list_rwsem:
+	struct list_head	socket_list;
+
+- by thread_lock:
+	unsigned int		thread_events;
+
+- by skt_mutex:
+	u_int			suspended_state;
+	void			(*tune_bridge);
+	struct pcmcia_callback	*callback;
+	int			resume_status;
+
+- by ops_mutex:
+	socket_state_t		socket;
+	u_int			state;
+	u_short			lock_count;
+	pccard_mem_map		cis_mem;
+	void __iomem 		*cis_virt;
+	struct { }		irq;
+	io_window_t		io[];
+	pccard_mem_map		win[];
+	struct list_head	cis_cache;
+	size_t			fake_cis_len;
+	u8			*fake_cis;
+	u_int			irq_mask;
+	void 			(*zoom_video);
+	int 			(*power_hook);
+	u8			resource...;
+	struct list_head	devices_list;
+	u8			device_count;
+	struct 			pcmcia_state;
+
+
+3. Per PCMCIA-device Data:
+--------------------------
+
+The "main" struct pcmcia_devie is protected as follows (read-only fields
+or single-use fields not mentioned):
+
+
+- by pcmcia_socket->ops_mutex:
+	struct list_head	socket_device_list;
+	struct config_t		*function_config;
+	u16			_irq:1;
+	u16			_io:1;
+	u16			_win:4;
+	u16			_locked:1;
+	u16			allow_func_id_match:1;
+	u16			suspended:1;
+	u16			_removed:1;
+
+- by the PCMCIA driver:
+	io_req_t		io;
+	irq_req_t		irq;
+	config_req_t		conf;
+	window_handle_t		win;

+ 1 - 1
drivers/char/pcmcia/cm4000_cs.c

@@ -1047,7 +1047,7 @@ release_io:
 static ssize_t cmm_write(struct file *filp, const char __user *buf,
 static ssize_t cmm_write(struct file *filp, const char __user *buf,
 			 size_t count, loff_t *ppos)
 			 size_t count, loff_t *ppos)
 {
 {
-	struct cm4000_dev *dev = (struct cm4000_dev *) filp->private_data;
+	struct cm4000_dev *dev = filp->private_data;
 	unsigned int iobase = dev->p_dev->io.BasePort1;
 	unsigned int iobase = dev->p_dev->io.BasePort1;
 	unsigned short s;
 	unsigned short s;
 	unsigned char tmp;
 	unsigned char tmp;

+ 2 - 4
drivers/net/pcmcia/smc91c92_cs.c

@@ -453,8 +453,7 @@ static int mhz_mfc_config(struct pcmcia_device *link)
 
 
     link->conf.Attributes |= CONF_ENABLE_SPKR;
     link->conf.Attributes |= CONF_ENABLE_SPKR;
     link->conf.Status = CCSR_AUDIO_ENA;
     link->conf.Status = CCSR_AUDIO_ENA;
-    link->irq.Attributes =
-	IRQ_TYPE_DYNAMIC_SHARING;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
     link->io.IOAddrLines = 16;
     link->io.IOAddrLines = 16;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->io.NumPorts2 = 8;
     link->io.NumPorts2 = 8;
@@ -652,8 +651,7 @@ static int osi_config(struct pcmcia_device *link)
 
 
     link->conf.Attributes |= CONF_ENABLE_SPKR;
     link->conf.Attributes |= CONF_ENABLE_SPKR;
     link->conf.Status = CCSR_AUDIO_ENA;
     link->conf.Status = CCSR_AUDIO_ENA;
-    link->irq.Attributes =
-	IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+    link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
     link->io.NumPorts1 = 64;
     link->io.NumPorts1 = 64;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
     link->io.NumPorts2 = 8;
     link->io.NumPorts2 = 8;

+ 3 - 6
drivers/pcmcia/Kconfig

@@ -84,7 +84,7 @@ config YENTA
 	tristate "CardBus yenta-compatible bridge support"
 	tristate "CardBus yenta-compatible bridge support"
 	depends on PCI
 	depends on PCI
 	select CARDBUS if !EMBEDDED
 	select CARDBUS if !EMBEDDED
-	select PCCARD_NONSTATIC
+	select PCCARD_NONSTATIC if PCMCIA != n
 	---help---
 	---help---
 	  This option enables support for CardBus host bridges.  Virtually
 	  This option enables support for CardBus host bridges.  Virtually
 	  all modern PCMCIA bridges are CardBus compatible.  A "bridge" is
 	  all modern PCMCIA bridges are CardBus compatible.  A "bridge" is
@@ -161,9 +161,8 @@ config TCIC
 
 
 config PCMCIA_M8XX
 config PCMCIA_M8XX
 	tristate "MPC8xx PCMCIA support"
 	tristate "MPC8xx PCMCIA support"
-	depends on PCMCIA && PPC && 8xx
-	select PCCARD_IODYN
-	select PCCARD_NONSTATIC
+	depends on PCCARD && PPC && 8xx
+	select PCCARD_IODYN if PCMCIA != n
 	help
 	help
 	  Say Y here to include support for PowerPC 8xx series PCMCIA
 	  Say Y here to include support for PowerPC 8xx series PCMCIA
 	  controller.
 	  controller.
@@ -238,14 +237,12 @@ config PCMCIA_PROBE
 config M32R_PCC
 config M32R_PCC
 	bool "M32R PCMCIA I/F"
 	bool "M32R PCMCIA I/F"
 	depends on M32R && CHIP_M32700 && PCMCIA
 	depends on M32R && CHIP_M32700 && PCMCIA
-	select PCCARD_NONSTATIC
 	help
 	help
 	  Say Y here to use the M32R PCMCIA controller.
 	  Say Y here to use the M32R PCMCIA controller.
 
 
 config M32R_CFC
 config M32R_CFC
 	bool "M32R CF I/F Controller"
 	bool "M32R CF I/F Controller"
 	depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT)
 	depends on M32R && (PLAT_USRV || PLAT_M32700UT || PLAT_MAPPI2 || PLAT_MAPPI3 || PLAT_OPSPUT)
-	select PCCARD_NONSTATIC
 	help
 	help
 	  Say Y here to use the M32R CompactFlash controller.
 	  Say Y here to use the M32R CompactFlash controller.
 
 

+ 2 - 2
drivers/pcmcia/Makefile

@@ -2,11 +2,11 @@
 # Makefile for the kernel pcmcia subsystem (c/o David Hinds)
 # Makefile for the kernel pcmcia subsystem (c/o David Hinds)
 #
 #
 
 
-pcmcia_core-y					+= cs.o cistpl.o rsrc_mgr.o socket_sysfs.o
+pcmcia_core-y					+= cs.o rsrc_mgr.o socket_sysfs.o
 pcmcia_core-$(CONFIG_CARDBUS)			+= cardbus.o
 pcmcia_core-$(CONFIG_CARDBUS)			+= cardbus.o
 obj-$(CONFIG_PCCARD)				+= pcmcia_core.o
 obj-$(CONFIG_PCCARD)				+= pcmcia_core.o
 
 
-pcmcia-y					+= ds.o pcmcia_resource.o
+pcmcia-y					+= ds.o pcmcia_resource.o cistpl.o
 pcmcia-$(CONFIG_PCMCIA_IOCTL)			+= pcmcia_ioctl.o
 pcmcia-$(CONFIG_PCMCIA_IOCTL)			+= pcmcia_ioctl.o
 obj-$(CONFIG_PCMCIA)				+= pcmcia.o
 obj-$(CONFIG_PCMCIA)				+= pcmcia.o
 
 

+ 0 - 2
drivers/pcmcia/at91_cf.c

@@ -52,8 +52,6 @@ struct at91_cf_socket {
 	unsigned long		phys_baseaddr;
 	unsigned long		phys_baseaddr;
 };
 };
 
 
-#define	SZ_2K			(2 * SZ_1K)
-
 static inline int at91_cf_present(struct at91_cf_socket *cf)
 static inline int at91_cf_present(struct at91_cf_socket *cf)
 {
 {
 	return !gpio_get_value(cf->board->det_pin);
 	return !gpio_get_value(cf->board->det_pin);

+ 1 - 1
drivers/pcmcia/bfin_cf_pcmcia.c

@@ -205,7 +205,7 @@ static int __devinit bfin_cf_probe(struct platform_device *pdev)
 	dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
 	dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
 
 
 	irq = platform_get_irq(pdev, 0);
 	irq = platform_get_irq(pdev, 0);
-	if (!irq)
+	if (irq <= 0)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	cd_pfx = platform_get_irq(pdev, 1);	/*Card Detect GPIO PIN */
 	cd_pfx = platform_get_irq(pdev, 1);	/*Card Detect GPIO PIN */

+ 14 - 161
drivers/pcmcia/cardbus.c

@@ -20,170 +20,12 @@
  */
  */
 
 
 
 
-#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/pci.h>
-#include <linux/ioport.h>
-#include <linux/io.h>
-#include <asm/irq.h>
 
 
-#include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
 #include <pcmcia/ss.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include "cs_internal.h"
-
-/*====================================================================*/
-
-/* Offsets in the Expansion ROM Image Header */
-#define ROM_SIGNATURE		0x0000	/* 2 bytes */
-#define ROM_DATA_PTR		0x0018	/* 2 bytes */
-
-/* Offsets in the CardBus PC Card Data Structure */
-#define PCDATA_SIGNATURE	0x0000	/* 4 bytes */
-#define PCDATA_VPD_PTR		0x0008	/* 2 bytes */
-#define PCDATA_LENGTH		0x000a	/* 2 bytes */
-#define PCDATA_REVISION		0x000c
-#define PCDATA_IMAGE_SZ		0x0010	/* 2 bytes */
-#define PCDATA_ROM_LEVEL	0x0012	/* 2 bytes */
-#define PCDATA_CODE_TYPE	0x0014
-#define PCDATA_INDICATOR	0x0015
-
-/*=====================================================================
-
-    Expansion ROM's have a special layout, and pointers specify an
-    image number and an offset within that image.  xlate_rom_addr()
-    converts an image/offset address to an absolute offset from the
-    ROM's base address.
-
-=====================================================================*/
-
-static u_int xlate_rom_addr(void __iomem *b, u_int addr)
-{
-	u_int img = 0, ofs = 0, sz;
-	u_short data;
-	while ((readb(b) == 0x55) && (readb(b + 1) == 0xaa)) {
-		if (img == (addr >> 28))
-			return (addr & 0x0fffffff) + ofs;
-		data = readb(b + ROM_DATA_PTR) + (readb(b + ROM_DATA_PTR + 1) << 8);
-		sz = 512 * (readb(b + data + PCDATA_IMAGE_SZ) +
-			    (readb(b + data + PCDATA_IMAGE_SZ + 1) << 8));
-		if ((sz == 0) || (readb(b + data + PCDATA_INDICATOR) & 0x80))
-			break;
-		b += sz;
-		ofs += sz;
-		img++;
-	}
-	return 0;
-}
-
-/*=====================================================================
-
-    These are similar to setup_cis_mem and release_cis_mem for 16-bit
-    cards.  The "result" that is used externally is the cb_cis_virt
-    pointer in the struct pcmcia_socket structure.
-
-=====================================================================*/
-
-static void cb_release_cis_mem(struct pcmcia_socket *s)
-{
-	if (s->cb_cis_virt) {
-		dev_dbg(&s->dev, "cb_release_cis_mem()\n");
-		iounmap(s->cb_cis_virt);
-		s->cb_cis_virt = NULL;
-		s->cb_cis_res = NULL;
-	}
-}
-
-static int cb_setup_cis_mem(struct pcmcia_socket *s, struct resource *res)
-{
-	unsigned int start, size;
-
-	if (res == s->cb_cis_res)
-		return 0;
-
-	if (s->cb_cis_res)
-		cb_release_cis_mem(s);
-
-	start = res->start;
-	size = res->end - start + 1;
-	s->cb_cis_virt = ioremap(start, size);
-
-	if (!s->cb_cis_virt)
-		return -1;
-
-	s->cb_cis_res = res;
-
-	return 0;
-}
-
-/*=====================================================================
-
-    This is used by the CIS processing code to read CIS information
-    from a CardBus device.
-
-=====================================================================*/
-
-int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
-		void *ptr)
-{
-	struct pci_dev *dev;
-	struct resource *res;
-
-	dev_dbg(&s->dev, "read_cb_mem(%d, %#x, %u)\n", space, addr, len);
 
 
-	dev = pci_get_slot(s->cb_dev->subordinate, 0);
-	if (!dev)
-		goto fail;
-
-	/* Config space? */
-	if (space == 0) {
-		if (addr + len > 0x100)
-			goto failput;
-		for (; len; addr++, ptr++, len--)
-			pci_read_config_byte(dev, addr, ptr);
-		return 0;
-	}
-
-	res = dev->resource + space - 1;
-
-	pci_dev_put(dev);
-
-	if (!res->flags)
-		goto fail;
-
-	if (cb_setup_cis_mem(s, res) != 0)
-		goto fail;
-
-	if (space == 7) {
-		addr = xlate_rom_addr(s->cb_cis_virt, addr);
-		if (addr == 0)
-			goto fail;
-	}
-
-	if (addr + len > res->end - res->start)
-		goto fail;
-
-	memcpy_fromio(ptr, s->cb_cis_virt + addr, len);
-	return 0;
-
-failput:
-	pci_dev_put(dev);
-fail:
-	memset(ptr, 0xff, len);
-	return -1;
-}
-
-/*=====================================================================
-
-    cb_alloc() and cb_free() allocate and free the kernel data
-    structures for a Cardbus device, and handle the lowest level PCI
-    device setup issues.
-
-=====================================================================*/
 
 
 static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
 static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
 {
 {
@@ -215,6 +57,13 @@ static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
 	}
 	}
 }
 }
 
 
+/**
+ * cb_alloc() - add CardBus device
+ * @s:		the pcmcia_socket where the CardBus device is located
+ *
+ * cb_alloc() allocates the kernel data structures for a Cardbus device
+ * and handles the lowest level PCI device setup issues.
+ */
 int __ref cb_alloc(struct pcmcia_socket *s)
 int __ref cb_alloc(struct pcmcia_socket *s)
 {
 {
 	struct pci_bus *bus = s->cb_dev->subordinate;
 	struct pci_bus *bus = s->cb_dev->subordinate;
@@ -249,12 +98,16 @@ int __ref cb_alloc(struct pcmcia_socket *s)
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * cb_free() - remove CardBus device
+ * @s:		the pcmcia_socket where the CardBus device was located
+ *
+ * cb_free() handles the lowest level PCI device cleanup.
+ */
 void cb_free(struct pcmcia_socket *s)
 void cb_free(struct pcmcia_socket *s)
 {
 {
 	struct pci_dev *bridge = s->cb_dev;
 	struct pci_dev *bridge = s->cb_dev;
 
 
-	cb_release_cis_mem(s);
-
 	if (bridge)
 	if (bridge)
 		pci_remove_behind_bridge(bridge);
 		pci_remove_behind_bridge(bridge);
 }
 }

+ 337 - 269
drivers/pcmcia/cistpl.c

@@ -64,6 +64,7 @@ module_param(cis_width, int, 0444);
 
 
 void release_cis_mem(struct pcmcia_socket *s)
 void release_cis_mem(struct pcmcia_socket *s)
 {
 {
+    mutex_lock(&s->ops_mutex);
     if (s->cis_mem.flags & MAP_ACTIVE) {
     if (s->cis_mem.flags & MAP_ACTIVE) {
 	s->cis_mem.flags &= ~MAP_ACTIVE;
 	s->cis_mem.flags &= ~MAP_ACTIVE;
 	s->ops->set_mem_map(s, &s->cis_mem);
 	s->ops->set_mem_map(s, &s->cis_mem);
@@ -75,13 +76,15 @@ void release_cis_mem(struct pcmcia_socket *s)
 	iounmap(s->cis_virt);
 	iounmap(s->cis_virt);
 	s->cis_virt = NULL;
 	s->cis_virt = NULL;
     }
     }
+    mutex_unlock(&s->ops_mutex);
 }
 }
-EXPORT_SYMBOL(release_cis_mem);
 
 
 /*
 /*
  * Map the card memory at "card_offset" into virtual space.
  * Map the card memory at "card_offset" into virtual space.
  * If flags & MAP_ATTRIB, map the attribute space, otherwise
  * If flags & MAP_ATTRIB, map the attribute space, otherwise
  * map the memory space.
  * map the memory space.
+ *
+ * Must be called with ops_mutex held.
  */
  */
 static void __iomem *
 static void __iomem *
 set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
 set_cis_map(struct pcmcia_socket *s, unsigned int card_offset, unsigned int flags)
@@ -140,6 +143,7 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 
 
     dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
     dev_dbg(&s->dev, "pcmcia_read_cis_mem(%d, %#x, %u)\n", attr, addr, len);
 
 
+    mutex_lock(&s->ops_mutex);
     if (attr & IS_INDIRECT) {
     if (attr & IS_INDIRECT) {
 	/* Indirect accesses use a bunch of special registers at fixed
 	/* Indirect accesses use a bunch of special registers at fixed
 	   locations in common memory */
 	   locations in common memory */
@@ -151,7 +155,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 
 
 	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
 	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
 	if (!sys) {
 	if (!sys) {
+	    dev_dbg(&s->dev, "could not map memory\n");
 	    memset(ptr, 0xff, len);
 	    memset(ptr, 0xff, len);
+	    mutex_unlock(&s->ops_mutex);
 	    return -1;
 	    return -1;
 	}
 	}
 
 
@@ -165,6 +171,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
     } else {
     } else {
 	u_int inc = 1, card_offset, flags;
 	u_int inc = 1, card_offset, flags;
 
 
+	if (addr > CISTPL_MAX_CIS_SIZE)
+		dev_dbg(&s->dev, "attempt to read CIS mem at addr %#x", addr);
+
 	flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
 	flags = MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0);
 	if (attr) {
 	if (attr) {
 	    flags |= MAP_ATTRIB;
 	    flags |= MAP_ATTRIB;
@@ -176,7 +185,9 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 	while (len) {
 	while (len) {
 	    sys = set_cis_map(s, card_offset, flags);
 	    sys = set_cis_map(s, card_offset, flags);
 	    if (!sys) {
 	    if (!sys) {
+		dev_dbg(&s->dev, "could not map memory\n");
 		memset(ptr, 0xff, len);
 		memset(ptr, 0xff, len);
+		mutex_unlock(&s->ops_mutex);
 		return -1;
 		return -1;
 	    }
 	    }
 	    end = sys + s->map_size;
 	    end = sys + s->map_size;
@@ -190,12 +201,12 @@ int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 	    addr = 0;
 	    addr = 0;
 	}
 	}
     }
     }
+    mutex_unlock(&s->ops_mutex);
     dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
     dev_dbg(&s->dev, "  %#2.2x %#2.2x %#2.2x %#2.2x ...\n",
 	  *(u_char *)(ptr+0), *(u_char *)(ptr+1),
 	  *(u_char *)(ptr+0), *(u_char *)(ptr+1),
 	  *(u_char *)(ptr+2), *(u_char *)(ptr+3));
 	  *(u_char *)(ptr+2), *(u_char *)(ptr+3));
     return 0;
     return 0;
 }
 }
-EXPORT_SYMBOL(pcmcia_read_cis_mem);
 
 
 
 
 void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
@@ -206,6 +217,7 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 
 
     dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
     dev_dbg(&s->dev, "pcmcia_write_cis_mem(%d, %#x, %u)\n", attr, addr, len);
 
 
+    mutex_lock(&s->ops_mutex);
     if (attr & IS_INDIRECT) {
     if (attr & IS_INDIRECT) {
 	/* Indirect accesses use a bunch of special registers at fixed
 	/* Indirect accesses use a bunch of special registers at fixed
 	   locations in common memory */
 	   locations in common memory */
@@ -216,8 +228,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 	}
 	}
 
 
 	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
 	sys = set_cis_map(s, 0, MAP_ACTIVE | ((cis_width) ? MAP_16BIT : 0));
-	if (!sys)
+	if (!sys) {
+		dev_dbg(&s->dev, "could not map memory\n");
+		mutex_unlock(&s->ops_mutex);
 		return; /* FIXME: Error */
 		return; /* FIXME: Error */
+	}
 
 
 	writeb(flags, sys+CISREG_ICTRL0);
 	writeb(flags, sys+CISREG_ICTRL0);
 	writeb(addr & 0xff, sys+CISREG_IADDR0);
 	writeb(addr & 0xff, sys+CISREG_IADDR0);
@@ -239,8 +254,11 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 	card_offset = addr & ~(s->map_size-1);
 	card_offset = addr & ~(s->map_size-1);
 	while (len) {
 	while (len) {
 	    sys = set_cis_map(s, card_offset, flags);
 	    sys = set_cis_map(s, card_offset, flags);
-	    if (!sys)
+	    if (!sys) {
+		dev_dbg(&s->dev, "could not map memory\n");
+		mutex_unlock(&s->ops_mutex);
 		return; /* FIXME: error */
 		return; /* FIXME: error */
+	    }
 
 
 	    end = sys + s->map_size;
 	    end = sys + s->map_size;
 	    sys = sys + (addr & (s->map_size-1));
 	    sys = sys + (addr & (s->map_size-1));
@@ -253,8 +271,8 @@ void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr, u_int addr,
 	    addr = 0;
 	    addr = 0;
 	}
 	}
     }
     }
+    mutex_unlock(&s->ops_mutex);
 }
 }
-EXPORT_SYMBOL(pcmcia_write_cis_mem);
 
 
 
 
 /*======================================================================
 /*======================================================================
@@ -265,32 +283,36 @@ EXPORT_SYMBOL(pcmcia_write_cis_mem);
 
 
 ======================================================================*/
 ======================================================================*/
 
 
-static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
-			   size_t len, void *ptr)
+static int read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
+			size_t len, void *ptr)
 {
 {
-    struct cis_cache_entry *cis;
-    int ret;
+	struct cis_cache_entry *cis;
+	int ret = 0;
 
 
-    if (s->fake_cis) {
-	if (s->fake_cis_len >= addr+len)
-	    memcpy(ptr, s->fake_cis+addr, len);
-	else
-	    memset(ptr, 0xff, len);
-	return;
-    }
+	if (s->state & SOCKET_CARDBUS)
+		return -EINVAL;
 
 
-    list_for_each_entry(cis, &s->cis_cache, node) {
-	if (cis->addr == addr && cis->len == len && cis->attr == attr) {
-	    memcpy(ptr, cis->cache, len);
-	    return;
+	mutex_lock(&s->ops_mutex);
+	if (s->fake_cis) {
+		if (s->fake_cis_len >= addr+len)
+			memcpy(ptr, s->fake_cis+addr, len);
+		else {
+			memset(ptr, 0xff, len);
+			ret = -EINVAL;
+		}
+		mutex_unlock(&s->ops_mutex);
+		return ret;
 	}
 	}
-    }
 
 
-#ifdef CONFIG_CARDBUS
-    if (s->state & SOCKET_CARDBUS)
-	ret = read_cb_mem(s, attr, addr, len, ptr);
-    else
-#endif
+	list_for_each_entry(cis, &s->cis_cache, node) {
+		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
+			memcpy(ptr, cis->cache, len);
+			mutex_unlock(&s->ops_mutex);
+			return 0;
+		}
+	}
+	mutex_unlock(&s->ops_mutex);
+
 	ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
 	ret = pcmcia_read_cis_mem(s, attr, addr, len, ptr);
 
 
 	if (ret == 0) {
 	if (ret == 0) {
@@ -301,9 +323,12 @@ static void read_cis_cache(struct pcmcia_socket *s, int attr, u_int addr,
 			cis->len = len;
 			cis->len = len;
 			cis->attr = attr;
 			cis->attr = attr;
 			memcpy(cis->cache, ptr, len);
 			memcpy(cis->cache, ptr, len);
+			mutex_lock(&s->ops_mutex);
 			list_add(&cis->node, &s->cis_cache);
 			list_add(&cis->node, &s->cis_cache);
+			mutex_unlock(&s->ops_mutex);
 		}
 		}
 	}
 	}
+	return ret;
 }
 }
 
 
 static void
 static void
@@ -311,32 +336,35 @@ remove_cis_cache(struct pcmcia_socket *s, int attr, u_int addr, u_int len)
 {
 {
 	struct cis_cache_entry *cis;
 	struct cis_cache_entry *cis;
 
 
+	mutex_lock(&s->ops_mutex);
 	list_for_each_entry(cis, &s->cis_cache, node)
 	list_for_each_entry(cis, &s->cis_cache, node)
 		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
 		if (cis->addr == addr && cis->len == len && cis->attr == attr) {
 			list_del(&cis->node);
 			list_del(&cis->node);
 			kfree(cis);
 			kfree(cis);
 			break;
 			break;
 		}
 		}
+	mutex_unlock(&s->ops_mutex);
 }
 }
 
 
+/**
+ * destroy_cis_cache() - destroy the CIS cache
+ * @s:		pcmcia_socket for which CIS cache shall be destroyed
+ *
+ * This destroys the CIS cache but keeps any fake CIS alive. Must be
+ * called with ops_mutex held.
+ */
+
 void destroy_cis_cache(struct pcmcia_socket *s)
 void destroy_cis_cache(struct pcmcia_socket *s)
 {
 {
 	struct list_head *l, *n;
 	struct list_head *l, *n;
+	struct cis_cache_entry *cis;
 
 
 	list_for_each_safe(l, n, &s->cis_cache) {
 	list_for_each_safe(l, n, &s->cis_cache) {
-		struct cis_cache_entry *cis = list_entry(l, struct cis_cache_entry, node);
-
+		cis = list_entry(l, struct cis_cache_entry, node);
 		list_del(&cis->node);
 		list_del(&cis->node);
 		kfree(cis);
 		kfree(cis);
 	}
 	}
-
-	/*
-	 * If there was a fake CIS, destroy that as well.
-	 */
-	kfree(s->fake_cis);
-	s->fake_cis = NULL;
 }
 }
-EXPORT_SYMBOL(destroy_cis_cache);
 
 
 /*======================================================================
 /*======================================================================
 
 
@@ -349,6 +377,10 @@ int verify_cis_cache(struct pcmcia_socket *s)
 {
 {
 	struct cis_cache_entry *cis;
 	struct cis_cache_entry *cis;
 	char *buf;
 	char *buf;
+	int ret;
+
+	if (s->state & SOCKET_CARDBUS)
+		return -EINVAL;
 
 
 	buf = kmalloc(256, GFP_KERNEL);
 	buf = kmalloc(256, GFP_KERNEL);
 	if (buf == NULL) {
 	if (buf == NULL) {
@@ -361,14 +393,9 @@ int verify_cis_cache(struct pcmcia_socket *s)
 
 
 		if (len > 256)
 		if (len > 256)
 			len = 256;
 			len = 256;
-#ifdef CONFIG_CARDBUS
-		if (s->state & SOCKET_CARDBUS)
-			read_cb_mem(s, cis->attr, cis->addr, len, buf);
-		else
-#endif
-			pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
 
 
-		if (memcmp(buf, cis->cache, len) != 0) {
+		ret = pcmcia_read_cis_mem(s, cis->attr, cis->addr, len, buf);
+		if (ret || memcmp(buf, cis->cache, len) != 0) {
 			kfree(buf);
 			kfree(buf);
 			return -1;
 			return -1;
 		}
 		}
@@ -391,17 +418,20 @@ int pcmcia_replace_cis(struct pcmcia_socket *s,
 		dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
 		dev_printk(KERN_WARNING, &s->dev, "replacement CIS too big\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+	mutex_lock(&s->ops_mutex);
 	kfree(s->fake_cis);
 	kfree(s->fake_cis);
 	s->fake_cis = kmalloc(len, GFP_KERNEL);
 	s->fake_cis = kmalloc(len, GFP_KERNEL);
 	if (s->fake_cis == NULL) {
 	if (s->fake_cis == NULL) {
 		dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
 		dev_printk(KERN_WARNING, &s->dev, "no memory to replace CIS\n");
+		mutex_unlock(&s->ops_mutex);
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 	s->fake_cis_len = len;
 	s->fake_cis_len = len;
 	memcpy(s->fake_cis, data, len);
 	memcpy(s->fake_cis, data, len);
+	dev_info(&s->dev, "Using replacement CIS\n");
+	mutex_unlock(&s->ops_mutex);
 	return 0;
 	return 0;
 }
 }
-EXPORT_SYMBOL(pcmcia_replace_cis);
 
 
 /*======================================================================
 /*======================================================================
 
 
@@ -425,25 +455,16 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
 {
 {
     if (!s)
     if (!s)
 	return -EINVAL;
 	return -EINVAL;
-    if (!(s->state & SOCKET_PRESENT))
+
+    if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
 	return -ENODEV;
 	return -ENODEV;
     tuple->TupleLink = tuple->Flags = 0;
     tuple->TupleLink = tuple->Flags = 0;
-#ifdef CONFIG_CARDBUS
-    if (s->state & SOCKET_CARDBUS) {
-	struct pci_dev *dev = s->cb_dev;
-	u_int ptr;
-	pci_bus_read_config_dword(dev->subordinate, 0, PCI_CARDBUS_CIS, &ptr);
-	tuple->CISOffset = ptr & ~7;
-	SPACE(tuple->Flags) = (ptr & 7);
-    } else
-#endif
-    {
-	/* Assume presence of a LONGLINK_C to address 0 */
-	tuple->CISOffset = tuple->LinkOffset = 0;
-	SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
-    }
-    if (!(s->state & SOCKET_CARDBUS) && (s->functions > 1) &&
-	!(tuple->Attributes & TUPLE_RETURN_COMMON)) {
+
+    /* Assume presence of a LONGLINK_C to address 0 */
+    tuple->CISOffset = tuple->LinkOffset = 0;
+    SPACE(tuple->Flags) = HAS_LINK(tuple->Flags) = 1;
+
+    if ((s->functions > 1) && !(tuple->Attributes & TUPLE_RETURN_COMMON)) {
 	cisdata_t req = tuple->DesiredTuple;
 	cisdata_t req = tuple->DesiredTuple;
 	tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
 	tuple->DesiredTuple = CISTPL_LONGLINK_MFC;
 	if (pccard_get_next_tuple(s, function, tuple) == 0) {
 	if (pccard_get_next_tuple(s, function, tuple) == 0) {
@@ -456,17 +477,19 @@ int pccard_get_first_tuple(struct pcmcia_socket *s, unsigned int function, tuple
     }
     }
     return pccard_get_next_tuple(s, function, tuple);
     return pccard_get_next_tuple(s, function, tuple);
 }
 }
-EXPORT_SYMBOL(pccard_get_first_tuple);
 
 
 static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
 static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
 {
 {
     u_char link[5];
     u_char link[5];
     u_int ofs;
     u_int ofs;
+    int ret;
 
 
     if (MFC_FN(tuple->Flags)) {
     if (MFC_FN(tuple->Flags)) {
 	/* Get indirect link from the MFC tuple */
 	/* Get indirect link from the MFC tuple */
-	read_cis_cache(s, LINK_SPACE(tuple->Flags),
+	ret = read_cis_cache(s, LINK_SPACE(tuple->Flags),
 		       tuple->LinkOffset, 5, link);
 		       tuple->LinkOffset, 5, link);
+	if (ret)
+		return -1;
 	ofs = get_unaligned_le32(link + 1);
 	ofs = get_unaligned_le32(link + 1);
 	SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
 	SPACE(tuple->Flags) = (link[0] == CISTPL_MFC_ATTR);
 	/* Move to the next indirect link */
 	/* Move to the next indirect link */
@@ -479,10 +502,12 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
     } else {
     } else {
 	return -1;
 	return -1;
     }
     }
-    if (!(s->state & SOCKET_CARDBUS) && SPACE(tuple->Flags)) {
+    if (SPACE(tuple->Flags)) {
 	/* This is ugly, but a common CIS error is to code the long
 	/* This is ugly, but a common CIS error is to code the long
 	   link offset incorrectly, so we check the right spot... */
 	   link offset incorrectly, so we check the right spot... */
-	read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+	ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+	if (ret)
+		return -1;
 	if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
 	if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
 	    (strncmp(link+2, "CIS", 3) == 0))
 	    (strncmp(link+2, "CIS", 3) == 0))
 	    return ofs;
 	    return ofs;
@@ -490,7 +515,9 @@ static int follow_link(struct pcmcia_socket *s, tuple_t *tuple)
 	/* Then, we try the wrong spot... */
 	/* Then, we try the wrong spot... */
 	ofs = ofs >> 1;
 	ofs = ofs >> 1;
     }
     }
-    read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+    ret = read_cis_cache(s, SPACE(tuple->Flags), ofs, 5, link);
+    if (ret)
+	    return -1;
     if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
     if ((link[0] == CISTPL_LINKTARGET) && (link[1] >= 3) &&
 	(strncmp(link+2, "CIS", 3) == 0))
 	(strncmp(link+2, "CIS", 3) == 0))
 	return ofs;
 	return ofs;
@@ -502,10 +529,11 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
 {
 {
     u_char link[2], tmp;
     u_char link[2], tmp;
     int ofs, i, attr;
     int ofs, i, attr;
+    int ret;
 
 
     if (!s)
     if (!s)
 	return -EINVAL;
 	return -EINVAL;
-    if (!(s->state & SOCKET_PRESENT))
+    if (!(s->state & SOCKET_PRESENT) || (s->state & SOCKET_CARDBUS))
 	return -ENODEV;
 	return -ENODEV;
 
 
     link[1] = tuple->TupleLink;
     link[1] = tuple->TupleLink;
@@ -516,7 +544,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
 	if (link[1] == 0xff) {
 	if (link[1] == 0xff) {
 	    link[0] = CISTPL_END;
 	    link[0] = CISTPL_END;
 	} else {
 	} else {
-	    read_cis_cache(s, attr, ofs, 2, link);
+	    ret = read_cis_cache(s, attr, ofs, 2, link);
+	    if (ret)
+		    return -1;
 	    if (link[0] == CISTPL_NULL) {
 	    if (link[0] == CISTPL_NULL) {
 		ofs++; continue;
 		ofs++; continue;
 	    }
 	    }
@@ -528,7 +558,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
 	    if (ofs < 0)
 	    if (ofs < 0)
 		return -ENOSPC;
 		return -ENOSPC;
 	    attr = SPACE(tuple->Flags);
 	    attr = SPACE(tuple->Flags);
-	    read_cis_cache(s, attr, ofs, 2, link);
+	    ret = read_cis_cache(s, attr, ofs, 2, link);
+	    if (ret)
+		    return -1;
 	}
 	}
 
 
 	/* Is this a link tuple?  Make a note of it */
 	/* Is this a link tuple?  Make a note of it */
@@ -542,12 +574,16 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
 	    case CISTPL_LONGLINK_A:
 	    case CISTPL_LONGLINK_A:
 		HAS_LINK(tuple->Flags) = 1;
 		HAS_LINK(tuple->Flags) = 1;
 		LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
 		LINK_SPACE(tuple->Flags) = attr | IS_ATTR;
-		read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		if (ret)
+			return -1;
 		break;
 		break;
 	    case CISTPL_LONGLINK_C:
 	    case CISTPL_LONGLINK_C:
 		HAS_LINK(tuple->Flags) = 1;
 		HAS_LINK(tuple->Flags) = 1;
 		LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
 		LINK_SPACE(tuple->Flags) = attr & ~IS_ATTR;
-		read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		ret = read_cis_cache(s, attr, ofs+2, 4, &tuple->LinkOffset);
+		if (ret)
+			return -1;
 		break;
 		break;
 	    case CISTPL_INDIRECT:
 	    case CISTPL_INDIRECT:
 		HAS_LINK(tuple->Flags) = 1;
 		HAS_LINK(tuple->Flags) = 1;
@@ -559,7 +595,9 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
 		LINK_SPACE(tuple->Flags) = attr;
 		LINK_SPACE(tuple->Flags) = attr;
 		if (function == BIND_FN_ALL) {
 		if (function == BIND_FN_ALL) {
 		    /* Follow all the MFC links */
 		    /* Follow all the MFC links */
-		    read_cis_cache(s, attr, ofs+2, 1, &tmp);
+		    ret = read_cis_cache(s, attr, ofs+2, 1, &tmp);
+		    if (ret)
+			    return -1;
 		    MFC_FN(tuple->Flags) = tmp;
 		    MFC_FN(tuple->Flags) = tmp;
 		} else {
 		} else {
 		    /* Follow exactly one of the links */
 		    /* Follow exactly one of the links */
@@ -592,7 +630,6 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function, tuple_
     tuple->CISOffset = ofs + 2;
     tuple->CISOffset = ofs + 2;
     return 0;
     return 0;
 }
 }
-EXPORT_SYMBOL(pccard_get_next_tuple);
 
 
 /*====================================================================*/
 /*====================================================================*/
 
 
@@ -601,6 +638,7 @@ EXPORT_SYMBOL(pccard_get_next_tuple);
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
 {
 {
     u_int len;
     u_int len;
+    int ret;
 
 
     if (!s)
     if (!s)
 	return -EINVAL;
 	return -EINVAL;
@@ -611,12 +649,13 @@ int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple)
     tuple->TupleDataLen = tuple->TupleLink;
     tuple->TupleDataLen = tuple->TupleLink;
     if (len == 0)
     if (len == 0)
 	return 0;
 	return 0;
-    read_cis_cache(s, SPACE(tuple->Flags),
+    ret = read_cis_cache(s, SPACE(tuple->Flags),
 		   tuple->CISOffset + tuple->TupleOffset,
 		   tuple->CISOffset + tuple->TupleOffset,
 		   _MIN(len, tuple->TupleDataMax), tuple->TupleData);
 		   _MIN(len, tuple->TupleDataMax), tuple->TupleData);
+    if (ret)
+	    return -1;
     return 0;
     return 0;
 }
 }
-EXPORT_SYMBOL(pccard_get_tuple_data);
 
 
 
 
 /*======================================================================
 /*======================================================================
@@ -1190,119 +1229,6 @@ static int parse_cftable_entry(tuple_t *tuple,
 
 
 /*====================================================================*/
 /*====================================================================*/
 
 
-#ifdef CONFIG_CARDBUS
-
-static int parse_bar(tuple_t *tuple, cistpl_bar_t *bar)
-{
-    u_char *p;
-    if (tuple->TupleDataLen < 6)
-	return -EINVAL;
-    p = (u_char *)tuple->TupleData;
-    bar->attr = *p;
-    p += 2;
-    bar->size = get_unaligned_le32(p);
-    return 0;
-}
-
-static int parse_config_cb(tuple_t *tuple, cistpl_config_t *config)
-{
-    u_char *p;
-
-    p = (u_char *)tuple->TupleData;
-    if ((*p != 3) || (tuple->TupleDataLen < 6))
-	return -EINVAL;
-    config->last_idx = *(++p);
-    p++;
-    config->base = get_unaligned_le32(p);
-    config->subtuples = tuple->TupleDataLen - 6;
-    return 0;
-}
-
-static int parse_cftable_entry_cb(tuple_t *tuple,
-				  cistpl_cftable_entry_cb_t *entry)
-{
-    u_char *p, *q, features;
-
-    p = tuple->TupleData;
-    q = p + tuple->TupleDataLen;
-    entry->index = *p & 0x3f;
-    entry->flags = 0;
-    if (*p & 0x40)
-	entry->flags |= CISTPL_CFTABLE_DEFAULT;
-
-    /* Process optional features */
-    if (++p == q)
-	    return -EINVAL;
-    features = *p; p++;
-
-    /* Power options */
-    if ((features & 3) > 0) {
-	p = parse_power(p, q, &entry->vcc);
-	if (p == NULL)
-		return -EINVAL;
-    } else
-	entry->vcc.present = 0;
-    if ((features & 3) > 1) {
-	p = parse_power(p, q, &entry->vpp1);
-	if (p == NULL)
-		return -EINVAL;
-    } else
-	entry->vpp1.present = 0;
-    if ((features & 3) > 2) {
-	p = parse_power(p, q, &entry->vpp2);
-	if (p == NULL)
-		return -EINVAL;
-    } else
-	entry->vpp2.present = 0;
-
-    /* I/O window options */
-    if (features & 0x08) {
-	if (p == q)
-		return -EINVAL;
-	entry->io = *p; p++;
-    } else
-	entry->io = 0;
-
-    /* Interrupt options */
-    if (features & 0x10) {
-	p = parse_irq(p, q, &entry->irq);
-	if (p == NULL)
-		return -EINVAL;
-    } else
-	entry->irq.IRQInfo1 = 0;
-
-    if (features & 0x20) {
-	if (p == q)
-		return -EINVAL;
-	entry->mem = *p; p++;
-    } else
-	entry->mem = 0;
-
-    /* Misc features */
-    if (features & 0x80) {
-	if (p == q)
-		return -EINVAL;
-	entry->flags |= (*p << 8);
-	if (*p & 0x80) {
-	    if (++p == q)
-		    return -EINVAL;
-	    entry->flags |= (*p << 16);
-	}
-	while (*p & 0x80)
-	    if (++p == q)
-		    return -EINVAL;
-	p++;
-    }
-
-    entry->subtuples = q-p;
-
-    return 0;
-}
-
-#endif
-
-/*====================================================================*/
-
 static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
 static int parse_device_geo(tuple_t *tuple, cistpl_device_geo_t *geo)
 {
 {
     u_char *p, *q;
     u_char *p, *q;
@@ -1404,17 +1330,6 @@ int pcmcia_parse_tuple(tuple_t *tuple, cisparse_t *parse)
     case CISTPL_DEVICE_A:
     case CISTPL_DEVICE_A:
 	ret = parse_device(tuple, &parse->device);
 	ret = parse_device(tuple, &parse->device);
 	break;
 	break;
-#ifdef CONFIG_CARDBUS
-    case CISTPL_BAR:
-	ret = parse_bar(tuple, &parse->bar);
-	break;
-    case CISTPL_CONFIG_CB:
-	ret = parse_config_cb(tuple, &parse->config);
-	break;
-    case CISTPL_CFTABLE_ENTRY_CB:
-	ret = parse_cftable_entry_cb(tuple, &parse->cftable_entry_cb);
-	break;
-#endif
     case CISTPL_CHECKSUM:
     case CISTPL_CHECKSUM:
 	ret = parse_checksum(tuple, &parse->checksum);
 	ret = parse_checksum(tuple, &parse->checksum);
 	break;
 	break;
@@ -1513,7 +1428,6 @@ done:
     kfree(buf);
     kfree(buf);
     return ret;
     return ret;
 }
 }
-EXPORT_SYMBOL(pccard_read_tuple);
 
 
 
 
 /**
 /**
@@ -1573,84 +1487,238 @@ next_entry:
 	kfree(buf);
 	kfree(buf);
 	return ret;
 	return ret;
 }
 }
-EXPORT_SYMBOL(pccard_loop_tuple);
 
 
 
 
-/*======================================================================
-
-    This tries to determine if a card has a sensible CIS.  It returns
-    the number of tuples in the CIS, or 0 if the CIS looks bad.  The
-    checks include making sure several critical tuples are present and
-    valid; seeing if the total number of tuples is reasonable; and
-    looking for tuples that use reserved codes.
-
-======================================================================*/
-
+/**
+ * pccard_validate_cis() - check whether card has a sensible CIS
+ * @s:		the struct pcmcia_socket we are to check
+ * @info:	returns the number of tuples in the (valid) CIS, or 0
+ *
+ * This tries to determine if a card has a sensible CIS.  In @info, it
+ * returns the number of tuples in the CIS, or 0 if the CIS looks bad. The
+ * checks include making sure several critical tuples are present and
+ * valid; seeing if the total number of tuples is reasonable; and
+ * looking for tuples that use reserved codes.
+ *
+ * The function returns 0 on success.
+ */
 int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
 int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *info)
 {
 {
-    tuple_t *tuple;
-    cisparse_t *p;
-    unsigned int count = 0;
-    int ret, reserved, dev_ok = 0, ident_ok = 0;
+	tuple_t *tuple;
+	cisparse_t *p;
+	unsigned int count = 0;
+	int ret, reserved, dev_ok = 0, ident_ok = 0;
 
 
-    if (!s)
-	return -EINVAL;
+	if (!s)
+		return -EINVAL;
 
 
-    tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
-    if (tuple == NULL) {
-	    dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
-	    return -ENOMEM;
-    }
-    p = kmalloc(sizeof(*p), GFP_KERNEL);
-    if (p == NULL) {
-	    kfree(tuple);
-	    dev_printk(KERN_WARNING, &s->dev, "no memory to validate CIS\n");
-	    return -ENOMEM;
-    }
+	/* We do not want to validate the CIS cache... */
+	mutex_lock(&s->ops_mutex);
+	destroy_cis_cache(s);
+	mutex_unlock(&s->ops_mutex);
 
 
-    count = reserved = 0;
-    tuple->DesiredTuple = RETURN_FIRST_TUPLE;
-    tuple->Attributes = TUPLE_RETURN_COMMON;
-    ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
-    if (ret != 0)
-	goto done;
-
-    /* First tuple should be DEVICE; we should really have either that
-       or a CFTABLE_ENTRY of some sort */
-    if ((tuple->TupleCode == CISTPL_DEVICE) ||
-	(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p) == 0) ||
-	(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p) == 0))
-	dev_ok++;
-
-    /* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
-       tuple, for card identification.  Certain old D-Link and Linksys
-       cards have only a broken VERS_2 tuple; hence the bogus test. */
-    if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
-	(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
-	(pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
-	ident_ok++;
-
-    if (!dev_ok && !ident_ok)
-	goto done;
-
-    for (count = 1; count < MAX_TUPLES; count++) {
-	ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
+	tuple = kmalloc(sizeof(*tuple), GFP_KERNEL);
+	if (tuple == NULL) {
+		dev_warn(&s->dev, "no memory to validate CIS\n");
+		return -ENOMEM;
+	}
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (p == NULL) {
+		kfree(tuple);
+		dev_warn(&s->dev, "no memory to validate CIS\n");
+		return -ENOMEM;
+	}
+
+	count = reserved = 0;
+	tuple->DesiredTuple = RETURN_FIRST_TUPLE;
+	tuple->Attributes = TUPLE_RETURN_COMMON;
+	ret = pccard_get_first_tuple(s, BIND_FN_ALL, tuple);
 	if (ret != 0)
 	if (ret != 0)
-		break;
-	if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
-	    ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
-	    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
-	    reserved++;
-    }
-    if ((count == MAX_TUPLES) || (reserved > 5) ||
-	((!dev_ok || !ident_ok) && (count > 10)))
-	count = 0;
+		goto done;
+
+	/* First tuple should be DEVICE; we should really have either that
+	   or a CFTABLE_ENTRY of some sort */
+	if ((tuple->TupleCode == CISTPL_DEVICE) ||
+	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY, p)) ||
+	    (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_CFTABLE_ENTRY_CB, p)))
+		dev_ok++;
+
+	/* All cards should have a MANFID tuple, and/or a VERS_1 or VERS_2
+	   tuple, for card identification.  Certain old D-Link and Linksys
+	   cards have only a broken VERS_2 tuple; hence the bogus test. */
+	if ((pccard_read_tuple(s, BIND_FN_ALL, CISTPL_MANFID, p) == 0) ||
+	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_1, p) == 0) ||
+	    (pccard_read_tuple(s, BIND_FN_ALL, CISTPL_VERS_2, p) != -ENOSPC))
+		ident_ok++;
+
+	if (!dev_ok && !ident_ok)
+		goto done;
+
+	for (count = 1; count < MAX_TUPLES; count++) {
+		ret = pccard_get_next_tuple(s, BIND_FN_ALL, tuple);
+		if (ret != 0)
+			break;
+		if (((tuple->TupleCode > 0x23) && (tuple->TupleCode < 0x40)) ||
+		    ((tuple->TupleCode > 0x47) && (tuple->TupleCode < 0x80)) ||
+		    ((tuple->TupleCode > 0x90) && (tuple->TupleCode < 0xff)))
+			reserved++;
+	}
+	if ((count == MAX_TUPLES) || (reserved > 5) ||
+		((!dev_ok || !ident_ok) && (count > 10)))
+		count = 0;
+
+	ret = 0;
 
 
 done:
 done:
-    if (info)
-	    *info = count;
-    kfree(tuple);
-    kfree(p);
-    return 0;
+	/* invalidate CIS cache on failure */
+	if (!dev_ok || !ident_ok || !count) {
+		mutex_lock(&s->ops_mutex);
+		destroy_cis_cache(s);
+		mutex_unlock(&s->ops_mutex);
+		ret = -EIO;
+	}
+
+	if (info)
+		*info = count;
+	kfree(tuple);
+	kfree(p);
+	return ret;
 }
 }
-EXPORT_SYMBOL(pccard_validate_cis);
+
+
+#define to_socket(_dev) container_of(_dev, struct pcmcia_socket, dev)
+
+static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf,
+				  loff_t off, size_t count)
+{
+	tuple_t tuple;
+	int status, i;
+	loff_t pointer = 0;
+	ssize_t ret = 0;
+	u_char *tuplebuffer;
+	u_char *tempbuffer;
+
+	tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
+	if (!tuplebuffer)
+		return -ENOMEM;
+
+	tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
+	if (!tempbuffer) {
+		ret = -ENOMEM;
+		goto free_tuple;
+	}
+
+	memset(&tuple, 0, sizeof(tuple_t));
+
+	tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
+	tuple.DesiredTuple = RETURN_FIRST_TUPLE;
+	tuple.TupleOffset = 0;
+
+	status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
+	while (!status) {
+		tuple.TupleData = tuplebuffer;
+		tuple.TupleDataMax = 255;
+		memset(tuplebuffer, 0, sizeof(u_char) * 255);
+
+		status = pccard_get_tuple_data(s, &tuple);
+		if (status)
+			break;
+
+		if (off < (pointer + 2 + tuple.TupleDataLen)) {
+			tempbuffer[0] = tuple.TupleCode & 0xff;
+			tempbuffer[1] = tuple.TupleLink & 0xff;
+			for (i = 0; i < tuple.TupleDataLen; i++)
+				tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
+
+			for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
+				if (((i + pointer) >= off) &&
+				    (i + pointer) < (off + count)) {
+					buf[ret] = tempbuffer[i];
+					ret++;
+				}
+			}
+		}
+
+		pointer += 2 + tuple.TupleDataLen;
+
+		if (pointer >= (off + count))
+			break;
+
+		if (tuple.TupleCode == CISTPL_END)
+			break;
+		status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
+	}
+
+	kfree(tempbuffer);
+ free_tuple:
+	kfree(tuplebuffer);
+
+	return ret;
+}
+
+
+static ssize_t pccard_show_cis(struct kobject *kobj,
+			       struct bin_attribute *bin_attr,
+			       char *buf, loff_t off, size_t count)
+{
+	unsigned int size = 0x200;
+
+	if (off >= size)
+		count = 0;
+	else {
+		struct pcmcia_socket *s;
+		unsigned int chains;
+
+		if (off + count > size)
+			count = size - off;
+
+		s = to_socket(container_of(kobj, struct device, kobj));
+
+		if (!(s->state & SOCKET_PRESENT))
+			return -ENODEV;
+		if (pccard_validate_cis(s, &chains))
+			return -EIO;
+		if (!chains)
+			return -ENODATA;
+
+		count = pccard_extract_cis(s, buf, off, count);
+	}
+
+	return count;
+}
+
+
+static ssize_t pccard_store_cis(struct kobject *kobj,
+				struct bin_attribute *bin_attr,
+				char *buf, loff_t off, size_t count)
+{
+	struct pcmcia_socket *s;
+	int error;
+
+	s = to_socket(container_of(kobj, struct device, kobj));
+
+	if (off)
+		return -EINVAL;
+
+	if (count >= CISTPL_MAX_CIS_SIZE)
+		return -EINVAL;
+
+	if (!(s->state & SOCKET_PRESENT))
+		return -ENODEV;
+
+	error = pcmcia_replace_cis(s, buf, count);
+	if (error)
+		return -EIO;
+
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
+
+	return count;
+}
+
+
+struct bin_attribute pccard_cis_attr = {
+	.attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
+	.size = 0x200,
+	.read = pccard_show_cis,
+	.write = pccard_store_cis,
+};

+ 142 - 170
drivers/pcmcia/cs.c

@@ -140,19 +140,13 @@ struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt)
 	struct device *dev = get_device(&skt->dev);
 	struct device *dev = get_device(&skt->dev);
 	if (!dev)
 	if (!dev)
 		return NULL;
 		return NULL;
-	skt = dev_get_drvdata(dev);
-	if (!try_module_get(skt->owner)) {
-		put_device(&skt->dev);
-		return NULL;
-	}
-	return skt;
+	return dev_get_drvdata(dev);
 }
 }
 EXPORT_SYMBOL(pcmcia_get_socket);
 EXPORT_SYMBOL(pcmcia_get_socket);
 
 
 
 
 void pcmcia_put_socket(struct pcmcia_socket *skt)
 void pcmcia_put_socket(struct pcmcia_socket *skt)
 {
 {
-	module_put(skt->owner);
 	put_device(&skt->dev);
 	put_device(&skt->dev);
 }
 }
 EXPORT_SYMBOL(pcmcia_put_socket);
 EXPORT_SYMBOL(pcmcia_put_socket);
@@ -181,8 +175,6 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
 
 
 	dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
 	dev_dbg(&socket->dev, "pcmcia_register_socket(0x%p)\n", socket->ops);
 
 
-	spin_lock_init(&socket->lock);
-
 	/* try to obtain a socket number [yes, it gets ugly if we
 	/* try to obtain a socket number [yes, it gets ugly if we
 	 * register more than 2^sizeof(unsigned int) pcmcia
 	 * register more than 2^sizeof(unsigned int) pcmcia
 	 * sockets... but the socket number is deprecated
 	 * sockets... but the socket number is deprecated
@@ -228,10 +220,13 @@ int pcmcia_register_socket(struct pcmcia_socket *socket)
 	init_completion(&socket->socket_released);
 	init_completion(&socket->socket_released);
 	init_completion(&socket->thread_done);
 	init_completion(&socket->thread_done);
 	mutex_init(&socket->skt_mutex);
 	mutex_init(&socket->skt_mutex);
+	mutex_init(&socket->ops_mutex);
 	spin_lock_init(&socket->thread_lock);
 	spin_lock_init(&socket->thread_lock);
 
 
 	if (socket->resource_ops->init) {
 	if (socket->resource_ops->init) {
+		mutex_lock(&socket->ops_mutex);
 		ret = socket->resource_ops->init(socket);
 		ret = socket->resource_ops->init(socket);
+		mutex_unlock(&socket->ops_mutex);
 		if (ret)
 		if (ret)
 			goto err;
 			goto err;
 	}
 	}
@@ -283,15 +278,17 @@ void pcmcia_unregister_socket(struct pcmcia_socket *socket)
 	if (socket->thread)
 	if (socket->thread)
 		kthread_stop(socket->thread);
 		kthread_stop(socket->thread);
 
 
-	release_cis_mem(socket);
-
 	/* remove from our own list */
 	/* remove from our own list */
 	down_write(&pcmcia_socket_list_rwsem);
 	down_write(&pcmcia_socket_list_rwsem);
 	list_del(&socket->socket_list);
 	list_del(&socket->socket_list);
 	up_write(&pcmcia_socket_list_rwsem);
 	up_write(&pcmcia_socket_list_rwsem);
 
 
 	/* wait for sysfs to drop all references */
 	/* wait for sysfs to drop all references */
-	release_resource_db(socket);
+	if (socket->resource_ops->exit) {
+		mutex_lock(&socket->ops_mutex);
+		socket->resource_ops->exit(socket);
+		mutex_unlock(&socket->ops_mutex);
+	}
 	wait_for_completion(&socket->socket_released);
 	wait_for_completion(&socket->socket_released);
 } /* pcmcia_unregister_socket */
 } /* pcmcia_unregister_socket */
 EXPORT_SYMBOL(pcmcia_unregister_socket);
 EXPORT_SYMBOL(pcmcia_unregister_socket);
@@ -328,7 +325,7 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
 {
 {
 	int ret;
 	int ret;
 
 
-	if (s->state & SOCKET_CARDBUS)
+	if ((s->state & SOCKET_CARDBUS) && (event != CS_EVENT_CARD_REMOVAL))
 		return 0;
 		return 0;
 
 
 	dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
 	dev_dbg(&s->dev, "send_event(event %d, pri %d, callback 0x%p)\n",
@@ -346,13 +343,6 @@ static int send_event(struct pcmcia_socket *s, event_t event, int priority)
 	return ret;
 	return ret;
 }
 }
 
 
-static void socket_remove_drivers(struct pcmcia_socket *skt)
-{
-	dev_dbg(&skt->dev, "remove_drivers\n");
-
-	send_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
-}
-
 static int socket_reset(struct pcmcia_socket *skt)
 static int socket_reset(struct pcmcia_socket *skt)
 {
 {
 	int status, i;
 	int status, i;
@@ -395,7 +385,9 @@ static void socket_shutdown(struct pcmcia_socket *s)
 
 
 	dev_dbg(&s->dev, "shutdown\n");
 	dev_dbg(&s->dev, "shutdown\n");
 
 
-	socket_remove_drivers(s);
+	send_event(s, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+
+	mutex_lock(&s->ops_mutex);
 	s->state &= SOCKET_INUSE | SOCKET_PRESENT;
 	s->state &= SOCKET_INUSE | SOCKET_PRESENT;
 	msleep(shutdown_delay * 10);
 	msleep(shutdown_delay * 10);
 	s->state &= SOCKET_INUSE;
 	s->state &= SOCKET_INUSE;
@@ -406,11 +398,21 @@ static void socket_shutdown(struct pcmcia_socket *s)
 	s->ops->set_socket(s, &s->socket);
 	s->ops->set_socket(s, &s->socket);
 	s->irq.AssignedIRQ = s->irq.Config = 0;
 	s->irq.AssignedIRQ = s->irq.Config = 0;
 	s->lock_count = 0;
 	s->lock_count = 0;
-	destroy_cis_cache(s);
+	kfree(s->fake_cis);
+	s->fake_cis = NULL;
+	s->functions = 0;
+
+	/* From here on we can be sure that only we (that is, the
+	 * pccardd thread) accesses this socket, and all (16-bit)
+	 * PCMCIA interactions are gone. Therefore, release
+	 * ops_mutex so that we don't get a sysfs-related lockdep
+	 * warning.
+	 */
+	mutex_unlock(&s->ops_mutex);
+
 #ifdef CONFIG_CARDBUS
 #ifdef CONFIG_CARDBUS
 	cb_free(s);
 	cb_free(s);
 #endif
 #endif
-	s->functions = 0;
 
 
 	/* give socket some time to power down */
 	/* give socket some time to power down */
 	msleep(100);
 	msleep(100);
@@ -421,7 +423,7 @@ static void socket_shutdown(struct pcmcia_socket *s)
 			   "*** DANGER *** unable to remove socket power\n");
 			   "*** DANGER *** unable to remove socket power\n");
 	}
 	}
 
 
-	cs_socket_put(s);
+	s->state &= ~SOCKET_INUSE;
 }
 }
 
 
 static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
@@ -460,7 +462,8 @@ static int socket_setup(struct pcmcia_socket *skt, int initial_delay)
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 		skt->state |= SOCKET_CARDBUS;
 		skt->state |= SOCKET_CARDBUS;
-	}
+	} else
+		skt->state &= ~SOCKET_CARDBUS;
 
 
 	/*
 	/*
 	 * Decode the card voltage requirements, and apply power to the card.
 	 * Decode the card voltage requirements, and apply power to the card.
@@ -509,8 +512,12 @@ static int socket_insert(struct pcmcia_socket *skt)
 
 
 	dev_dbg(&skt->dev, "insert\n");
 	dev_dbg(&skt->dev, "insert\n");
 
 
-	if (!cs_socket_get(skt))
-		return -ENODEV;
+	mutex_lock(&skt->ops_mutex);
+	if (skt->state & SOCKET_INUSE) {
+		mutex_unlock(&skt->ops_mutex);
+		return -EINVAL;
+	}
+	skt->state |= SOCKET_INUSE;
 
 
 	ret = socket_setup(skt, setup_delay);
 	ret = socket_setup(skt, setup_delay);
 	if (ret == 0) {
 	if (ret == 0) {
@@ -528,9 +535,11 @@ static int socket_insert(struct pcmcia_socket *skt)
 		}
 		}
 #endif
 #endif
 		dev_dbg(&skt->dev, "insert done\n");
 		dev_dbg(&skt->dev, "insert done\n");
+		mutex_unlock(&skt->ops_mutex);
 
 
 		send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
 		send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
 	} else {
 	} else {
+		mutex_unlock(&skt->ops_mutex);
 		socket_shutdown(skt);
 		socket_shutdown(skt);
 	}
 	}
 
 
@@ -542,58 +551,66 @@ static int socket_suspend(struct pcmcia_socket *skt)
 	if (skt->state & SOCKET_SUSPEND)
 	if (skt->state & SOCKET_SUSPEND)
 		return -EBUSY;
 		return -EBUSY;
 
 
+	mutex_lock(&skt->ops_mutex);
+	skt->suspended_state = skt->state;
+
 	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
 	send_event(skt, CS_EVENT_PM_SUSPEND, CS_EVENT_PRI_LOW);
 	skt->socket = dead_socket;
 	skt->socket = dead_socket;
 	skt->ops->set_socket(skt, &skt->socket);
 	skt->ops->set_socket(skt, &skt->socket);
 	if (skt->ops->suspend)
 	if (skt->ops->suspend)
 		skt->ops->suspend(skt);
 		skt->ops->suspend(skt);
 	skt->state |= SOCKET_SUSPEND;
 	skt->state |= SOCKET_SUSPEND;
-
+	mutex_unlock(&skt->ops_mutex);
 	return 0;
 	return 0;
 }
 }
 
 
 static int socket_early_resume(struct pcmcia_socket *skt)
 static int socket_early_resume(struct pcmcia_socket *skt)
 {
 {
+	mutex_lock(&skt->ops_mutex);
 	skt->socket = dead_socket;
 	skt->socket = dead_socket;
 	skt->ops->init(skt);
 	skt->ops->init(skt);
 	skt->ops->set_socket(skt, &skt->socket);
 	skt->ops->set_socket(skt, &skt->socket);
 	if (skt->state & SOCKET_PRESENT)
 	if (skt->state & SOCKET_PRESENT)
 		skt->resume_status = socket_setup(skt, resume_delay);
 		skt->resume_status = socket_setup(skt, resume_delay);
+	mutex_unlock(&skt->ops_mutex);
 	return 0;
 	return 0;
 }
 }
 
 
 static int socket_late_resume(struct pcmcia_socket *skt)
 static int socket_late_resume(struct pcmcia_socket *skt)
 {
 {
-	if (!(skt->state & SOCKET_PRESENT)) {
-		skt->state &= ~SOCKET_SUSPEND;
+	mutex_lock(&skt->ops_mutex);
+	skt->state &= ~SOCKET_SUSPEND;
+	mutex_unlock(&skt->ops_mutex);
+
+	if (!(skt->state & SOCKET_PRESENT))
 		return socket_insert(skt);
 		return socket_insert(skt);
+
+	if (skt->resume_status) {
+		socket_shutdown(skt);
+		return 0;
 	}
 	}
 
 
-	if (skt->resume_status == 0) {
-		/*
-		 * FIXME: need a better check here for cardbus cards.
-		 */
-		if (verify_cis_cache(skt) != 0) {
-			dev_dbg(&skt->dev, "cis mismatch - different card\n");
-			socket_remove_drivers(skt);
-			destroy_cis_cache(skt);
-			/*
-			 * Workaround: give DS time to schedule removal.
-			 * Remove me once the 100ms delay is eliminated
-			 * in ds.c
-			 */
-			msleep(200);
-			send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW);
-		} else {
-			dev_dbg(&skt->dev, "cis matches cache\n");
-			send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
-		}
-	} else {
+	if (skt->suspended_state != skt->state) {
+		dev_dbg(&skt->dev,
+			"suspend state 0x%x != resume state 0x%x\n",
+			skt->suspended_state, skt->state);
+
 		socket_shutdown(skt);
 		socket_shutdown(skt);
+		return socket_insert(skt);
 	}
 	}
 
 
-	skt->state &= ~SOCKET_SUSPEND;
+#ifdef CONFIG_CARDBUS
+	if (skt->state & SOCKET_CARDBUS) {
+		/* We can't be sure the CardBus card is the same
+		 * as the one previously inserted. Therefore, remove
+		 * and re-add... */
+		cb_free(skt);
+		cb_alloc(skt);
+		return 0;
+	}
+#endif
 
 
+	send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -672,20 +689,26 @@ static int pccardd(void *__skt)
 
 
 	complete(&skt->thread_done);
 	complete(&skt->thread_done);
 
 
+	/* wait for userspace to catch up */
+	msleep(250);
+
 	set_freezable();
 	set_freezable();
 	for (;;) {
 	for (;;) {
 		unsigned long flags;
 		unsigned long flags;
 		unsigned int events;
 		unsigned int events;
+		unsigned int sysfs_events;
 
 
 		set_current_state(TASK_INTERRUPTIBLE);
 		set_current_state(TASK_INTERRUPTIBLE);
 
 
 		spin_lock_irqsave(&skt->thread_lock, flags);
 		spin_lock_irqsave(&skt->thread_lock, flags);
 		events = skt->thread_events;
 		events = skt->thread_events;
 		skt->thread_events = 0;
 		skt->thread_events = 0;
+		sysfs_events = skt->sysfs_events;
+		skt->sysfs_events = 0;
 		spin_unlock_irqrestore(&skt->thread_lock, flags);
 		spin_unlock_irqrestore(&skt->thread_lock, flags);
 
 
+		mutex_lock(&skt->skt_mutex);
 		if (events) {
 		if (events) {
-			mutex_lock(&skt->skt_mutex);
 			if (events & SS_DETECT)
 			if (events & SS_DETECT)
 				socket_detect_change(skt);
 				socket_detect_change(skt);
 			if (events & SS_BATDEAD)
 			if (events & SS_BATDEAD)
@@ -694,10 +717,39 @@ static int pccardd(void *__skt)
 				send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
 				send_event(skt, CS_EVENT_BATTERY_LOW, CS_EVENT_PRI_LOW);
 			if (events & SS_READY)
 			if (events & SS_READY)
 				send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
 				send_event(skt, CS_EVENT_READY_CHANGE, CS_EVENT_PRI_LOW);
-			mutex_unlock(&skt->skt_mutex);
-			continue;
 		}
 		}
 
 
+		if (sysfs_events) {
+			if (sysfs_events & PCMCIA_UEVENT_EJECT)
+				socket_remove(skt);
+			if (sysfs_events & PCMCIA_UEVENT_INSERT)
+				socket_insert(skt);
+			if ((sysfs_events & PCMCIA_UEVENT_RESUME) &&
+				!(skt->state & SOCKET_CARDBUS)) {
+				ret = socket_resume(skt);
+				if (!ret && skt->callback)
+					skt->callback->resume(skt);
+			}
+			if ((sysfs_events & PCMCIA_UEVENT_SUSPEND) &&
+				!(skt->state & SOCKET_CARDBUS)) {
+				if (skt->callback)
+					ret = skt->callback->suspend(skt);
+				else
+					ret = 0;
+				if (!ret)
+					socket_suspend(skt);
+			}
+			if ((sysfs_events & PCMCIA_UEVENT_REQUERY) &&
+				!(skt->state & SOCKET_CARDBUS)) {
+				if (!ret && skt->callback)
+					skt->callback->requery(skt);
+			}
+		}
+		mutex_unlock(&skt->skt_mutex);
+
+		if (events || sysfs_events)
+			continue;
+
 		if (kthread_should_stop())
 		if (kthread_should_stop())
 			break;
 			break;
 
 
@@ -707,6 +759,13 @@ static int pccardd(void *__skt)
 	/* make sure we are running before we exit */
 	/* make sure we are running before we exit */
 	set_current_state(TASK_RUNNING);
 	set_current_state(TASK_RUNNING);
 
 
+	/* shut down socket, if a device is still present */
+	if (skt->state & SOCKET_PRESENT) {
+		mutex_lock(&skt->skt_mutex);
+		socket_remove(skt);
+		mutex_unlock(&skt->skt_mutex);
+	}
+
 	/* remove from the device core */
 	/* remove from the device core */
 	pccard_sysfs_remove_socket(&skt->dev);
 	pccard_sysfs_remove_socket(&skt->dev);
 	device_unregister(&skt->dev);
 	device_unregister(&skt->dev);
@@ -732,6 +791,31 @@ void pcmcia_parse_events(struct pcmcia_socket *s, u_int events)
 } /* pcmcia_parse_events */
 } /* pcmcia_parse_events */
 EXPORT_SYMBOL(pcmcia_parse_events);
 EXPORT_SYMBOL(pcmcia_parse_events);
 
 
+/**
+ * pcmcia_parse_uevents() - tell pccardd to issue manual commands
+ * @s:		the PCMCIA socket we wan't to command
+ * @events:	events to pass to pccardd
+ *
+ * userspace-issued insert, eject, suspend and resume commands must be
+ * handled by pccardd to avoid any sysfs-related deadlocks. Valid events
+ * are PCMCIA_UEVENT_EJECT (for eject), PCMCIA_UEVENT__INSERT (for insert),
+ * PCMCIA_UEVENT_RESUME (for resume), PCMCIA_UEVENT_SUSPEND (for suspend)
+ * and PCMCIA_UEVENT_REQUERY (for re-querying the PCMCIA card).
+ */
+void pcmcia_parse_uevents(struct pcmcia_socket *s, u_int events)
+{
+	unsigned long flags;
+	dev_dbg(&s->dev, "parse_uevents: events %08x\n", events);
+	if (s->thread) {
+		spin_lock_irqsave(&s->thread_lock, flags);
+		s->sysfs_events |= events;
+		spin_unlock_irqrestore(&s->thread_lock, flags);
+
+		wake_up_process(s->thread);
+	}
+}
+EXPORT_SYMBOL(pcmcia_parse_uevents);
+
 
 
 /* register pcmcia_callback */
 /* register pcmcia_callback */
 int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
 int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c)
@@ -796,7 +880,10 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
 			send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
 			send_event(skt, CS_EVENT_RESET_PHYSICAL, CS_EVENT_PRI_LOW);
 			if (skt->callback)
 			if (skt->callback)
 				skt->callback->suspend(skt);
 				skt->callback->suspend(skt);
-			if (socket_reset(skt) == 0) {
+			mutex_lock(&skt->ops_mutex);
+			ret = socket_reset(skt);
+			mutex_unlock(&skt->ops_mutex);
+			if (ret == 0) {
 				send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
 				send_event(skt, CS_EVENT_CARD_RESET, CS_EVENT_PRI_LOW);
 				if (skt->callback)
 				if (skt->callback)
 					skt->callback->resume(skt);
 					skt->callback->resume(skt);
@@ -812,121 +899,6 @@ int pcmcia_reset_card(struct pcmcia_socket *skt)
 EXPORT_SYMBOL(pcmcia_reset_card);
 EXPORT_SYMBOL(pcmcia_reset_card);
 
 
 
 
-/* These shut down or wake up a socket.  They are sort of user
- * initiated versions of the APM suspend and resume actions.
- */
-int pcmcia_suspend_card(struct pcmcia_socket *skt)
-{
-	int ret;
-
-	dev_dbg(&skt->dev, "suspending socket\n");
-
-	mutex_lock(&skt->skt_mutex);
-	do {
-		if (!(skt->state & SOCKET_PRESENT)) {
-			ret = -ENODEV;
-			break;
-		}
-		if (skt->state & SOCKET_CARDBUS) {
-			ret = -EPERM;
-			break;
-		}
-		if (skt->callback) {
-			ret = skt->callback->suspend(skt);
-			if (ret)
-				break;
-		}
-		ret = socket_suspend(skt);
-	} while (0);
-	mutex_unlock(&skt->skt_mutex);
-
-	return ret;
-} /* suspend_card */
-EXPORT_SYMBOL(pcmcia_suspend_card);
-
-
-int pcmcia_resume_card(struct pcmcia_socket *skt)
-{
-	int ret;
-
-	dev_dbg(&skt->dev, "waking up socket\n");
-
-	mutex_lock(&skt->skt_mutex);
-	do {
-		if (!(skt->state & SOCKET_PRESENT)) {
-			ret = -ENODEV;
-			break;
-		}
-		if (skt->state & SOCKET_CARDBUS) {
-			ret = -EPERM;
-			break;
-		}
-		ret = socket_resume(skt);
-		if (!ret && skt->callback)
-			skt->callback->resume(skt);
-	} while (0);
-	mutex_unlock(&skt->skt_mutex);
-
-	return ret;
-} /* resume_card */
-EXPORT_SYMBOL(pcmcia_resume_card);
-
-
-/* These handle user requests to eject or insert a card. */
-int pcmcia_eject_card(struct pcmcia_socket *skt)
-{
-	int ret;
-
-	dev_dbg(&skt->dev, "user eject request\n");
-
-	mutex_lock(&skt->skt_mutex);
-	do {
-		if (!(skt->state & SOCKET_PRESENT)) {
-			ret = -ENODEV;
-			break;
-		}
-
-		ret = send_event(skt, CS_EVENT_EJECTION_REQUEST, CS_EVENT_PRI_LOW);
-		if (ret != 0) {
-			ret = -EINVAL;
-			break;
-		}
-
-		socket_remove(skt);
-		ret = 0;
-	} while (0);
-	mutex_unlock(&skt->skt_mutex);
-
-	return ret;
-} /* eject_card */
-EXPORT_SYMBOL(pcmcia_eject_card);
-
-
-int pcmcia_insert_card(struct pcmcia_socket *skt)
-{
-	int ret;
-
-	dev_dbg(&skt->dev, "user insert request\n");
-
-	mutex_lock(&skt->skt_mutex);
-	do {
-		if (skt->state & SOCKET_PRESENT) {
-			ret = -EBUSY;
-			break;
-		}
-		if (socket_insert(skt) == -ENODEV) {
-			ret = -ENODEV;
-			break;
-		}
-		ret = 0;
-	} while (0);
-	mutex_unlock(&skt->skt_mutex);
-
-	return ret;
-} /* insert_card */
-EXPORT_SYMBOL(pcmcia_insert_card);
-
-
 static int pcmcia_socket_uevent(struct device *dev,
 static int pcmcia_socket_uevent(struct device *dev,
 				struct kobj_uevent_env *env)
 				struct kobj_uevent_env *env)
 {
 {

+ 27 - 62
drivers/pcmcia/cs_internal.h

@@ -87,37 +87,11 @@ struct pccard_resource_ops {
 #define SOCKET_CARDBUS		0x8000
 #define SOCKET_CARDBUS		0x8000
 #define SOCKET_CARDBUS_CONFIG	0x10000
 #define SOCKET_CARDBUS_CONFIG	0x10000
 
 
-static inline int cs_socket_get(struct pcmcia_socket *skt)
-{
-	int ret;
-
-	WARN_ON(skt->state & SOCKET_INUSE);
-
-	ret = try_module_get(skt->owner);
-	if (ret)
-		skt->state |= SOCKET_INUSE;
-	return ret;
-}
-
-static inline void cs_socket_put(struct pcmcia_socket *skt)
-{
-	if (skt->state & SOCKET_INUSE) {
-		skt->state &= ~SOCKET_INUSE;
-		module_put(skt->owner);
-	}
-}
-
 
 
 /*
 /*
  * Stuff internal to module "pcmcia_core":
  * Stuff internal to module "pcmcia_core":
  */
  */
 
 
-/* cistpl.c */
-int verify_cis_cache(struct pcmcia_socket *s);
-
-/* rsrc_mgr.c */
-void release_resource_db(struct pcmcia_socket *s);
-
 /* socket_sysfs.c */
 /* socket_sysfs.c */
 extern int pccard_sysfs_add_socket(struct device *dev);
 extern int pccard_sysfs_add_socket(struct device *dev);
 extern void pccard_sysfs_remove_socket(struct device *dev);
 extern void pccard_sysfs_remove_socket(struct device *dev);
@@ -125,8 +99,6 @@ extern void pccard_sysfs_remove_socket(struct device *dev);
 /* cardbus.c */
 /* cardbus.c */
 int cb_alloc(struct pcmcia_socket *s);
 int cb_alloc(struct pcmcia_socket *s);
 void cb_free(struct pcmcia_socket *s);
 void cb_free(struct pcmcia_socket *s);
-int read_cb_mem(struct pcmcia_socket *s, int space, u_int addr, u_int len,
-		void *ptr);
 
 
 
 
 
 
@@ -138,7 +110,8 @@ struct pcmcia_callback{
 	struct module	*owner;
 	struct module	*owner;
 	int		(*event) (struct pcmcia_socket *s,
 	int		(*event) (struct pcmcia_socket *s,
 				  event_t event, int priority);
 				  event_t event, int priority);
-	void		(*requery) (struct pcmcia_socket *s, int new_cis);
+	void		(*requery) (struct pcmcia_socket *s);
+	int		(*validate) (struct pcmcia_socket *s, unsigned int *i);
 	int		(*suspend) (struct pcmcia_socket *s);
 	int		(*suspend) (struct pcmcia_socket *s);
 	int		(*resume) (struct pcmcia_socket *s);
 	int		(*resume) (struct pcmcia_socket *s);
 };
 };
@@ -151,16 +124,35 @@ extern struct class pcmcia_socket_class;
 int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
 int pccard_register_pcmcia(struct pcmcia_socket *s, struct pcmcia_callback *c);
 struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
 struct pcmcia_socket *pcmcia_get_socket_by_nr(unsigned int nr);
 
 
-int pcmcia_suspend_card(struct pcmcia_socket *skt);
-int pcmcia_resume_card(struct pcmcia_socket *skt);
-
-int pcmcia_eject_card(struct pcmcia_socket *skt);
-int pcmcia_insert_card(struct pcmcia_socket *skt);
+void pcmcia_parse_uevents(struct pcmcia_socket *socket, unsigned int events);
+#define PCMCIA_UEVENT_EJECT	0x0001
+#define PCMCIA_UEVENT_INSERT	0x0002
+#define PCMCIA_UEVENT_SUSPEND	0x0004
+#define PCMCIA_UEVENT_RESUME	0x0008
+#define PCMCIA_UEVENT_REQUERY	0x0010
 
 
 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
 struct pcmcia_socket *pcmcia_get_socket(struct pcmcia_socket *skt);
 void pcmcia_put_socket(struct pcmcia_socket *skt);
 void pcmcia_put_socket(struct pcmcia_socket *skt);
 
 
+/*
+ * Stuff internal to module "pcmcia".
+ */
+/* ds.c */
+extern struct bus_type pcmcia_bus_type;
+
+/* pcmcia_resource.c */
+extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
+extern int pcmcia_validate_mem(struct pcmcia_socket *s);
+extern struct resource *pcmcia_find_mem_region(u_long base,
+					       u_long num,
+					       u_long align,
+					       int low,
+					       struct pcmcia_socket *s);
+
+
 /* cistpl.c */
 /* cistpl.c */
+extern struct bin_attribute pccard_cis_attr;
+
 int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
 int pcmcia_read_cis_mem(struct pcmcia_socket *s, int attr,
 			u_int addr, u_int len, void *ptr);
 			u_int addr, u_int len, void *ptr);
 void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
 void pcmcia_write_cis_mem(struct pcmcia_socket *s, int attr,
@@ -172,8 +164,8 @@ int pccard_read_tuple(struct pcmcia_socket *s, unsigned int function,
 int pcmcia_replace_cis(struct pcmcia_socket *s,
 int pcmcia_replace_cis(struct pcmcia_socket *s,
 		       const u8 *data, const size_t len);
 		       const u8 *data, const size_t len);
 int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count);
 int pccard_validate_cis(struct pcmcia_socket *s, unsigned int *count);
+int verify_cis_cache(struct pcmcia_socket *s);
 
 
-/* loop over CIS entries */
 int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
 int pccard_loop_tuple(struct pcmcia_socket *s, unsigned int function,
 		      cisdata_t code, cisparse_t *parse, void *priv_data,
 		      cisdata_t code, cisparse_t *parse, void *priv_data,
 		      int (*loop_tuple) (tuple_t *tuple,
 		      int (*loop_tuple) (tuple_t *tuple,
@@ -189,35 +181,8 @@ int pccard_get_next_tuple(struct pcmcia_socket *s, unsigned int function,
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
 int pccard_get_tuple_data(struct pcmcia_socket *s, tuple_t *tuple);
 
 
 
 
-/* rsrc_mgr.c */
-int pcmcia_validate_mem(struct pcmcia_socket *s);
-struct resource *pcmcia_find_io_region(unsigned long base,
-				       int num,
-				       unsigned long align,
-				       struct pcmcia_socket *s);
-int pcmcia_adjust_io_region(struct resource *res,
-			    unsigned long r_start,
-			    unsigned long r_end,
-			    struct pcmcia_socket *s);
-struct resource *pcmcia_find_mem_region(u_long base,
-					u_long num,
-					u_long align,
-					int low,
-					struct pcmcia_socket *s);
-
-/*
- * Stuff internal to module "pcmcia".
- */
-/* ds.c */
-extern struct bus_type pcmcia_bus_type;
-
-/* pcmcia_resource.c */
-extern int pcmcia_release_configuration(struct pcmcia_device *p_dev);
-
 #ifdef CONFIG_PCMCIA_IOCTL
 #ifdef CONFIG_PCMCIA_IOCTL
 /* ds.c */
 /* ds.c */
-extern spinlock_t pcmcia_dev_list_lock;
-
 extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev);
 extern struct pcmcia_device *pcmcia_get_dev(struct pcmcia_device *p_dev);
 extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
 extern void pcmcia_put_dev(struct pcmcia_device *p_dev);
 
 

+ 190 - 143
drivers/pcmcia/ds.c

@@ -42,8 +42,6 @@ MODULE_DESCRIPTION("PCMCIA Driver Services");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
 
 
 
 
-spinlock_t pcmcia_dev_list_lock;
-
 /*====================================================================*/
 /*====================================================================*/
 
 
 static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
 static void pcmcia_check_driver(struct pcmcia_driver *p_drv)
@@ -126,9 +124,9 @@ pcmcia_store_new_id(struct device_driver *driver, const char *buf, size_t count)
 	dynid->id.device_no = device_no;
 	dynid->id.device_no = device_no;
 	memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
 	memcpy(dynid->id.prod_id_hash, prod_id_hash, sizeof(__u32) * 4);
 
 
-	spin_lock(&pdrv->dynids.lock);
+	mutex_lock(&pdrv->dynids.lock);
 	list_add_tail(&dynid->node, &pdrv->dynids.list);
 	list_add_tail(&dynid->node, &pdrv->dynids.list);
-	spin_unlock(&pdrv->dynids.lock);
+	mutex_unlock(&pdrv->dynids.lock);
 
 
 	if (get_driver(&pdrv->drv)) {
 	if (get_driver(&pdrv->drv)) {
 		retval = driver_attach(&pdrv->drv);
 		retval = driver_attach(&pdrv->drv);
@@ -146,12 +144,12 @@ pcmcia_free_dynids(struct pcmcia_driver *drv)
 {
 {
 	struct pcmcia_dynid *dynid, *n;
 	struct pcmcia_dynid *dynid, *n;
 
 
-	spin_lock(&drv->dynids.lock);
+	mutex_lock(&drv->dynids.lock);
 	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
 	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
 		list_del(&dynid->node);
 		list_del(&dynid->node);
 		kfree(dynid);
 		kfree(dynid);
 	}
 	}
-	spin_unlock(&drv->dynids.lock);
+	mutex_unlock(&drv->dynids.lock);
 }
 }
 
 
 static int
 static int
@@ -182,7 +180,7 @@ int pcmcia_register_driver(struct pcmcia_driver *driver)
 	/* initialize common fields */
 	/* initialize common fields */
 	driver->drv.bus = &pcmcia_bus_type;
 	driver->drv.bus = &pcmcia_bus_type;
 	driver->drv.owner = driver->owner;
 	driver->drv.owner = driver->owner;
-	spin_lock_init(&driver->dynids.lock);
+	mutex_init(&driver->dynids.lock);
 	INIT_LIST_HEAD(&driver->dynids.list);
 	INIT_LIST_HEAD(&driver->dynids.list);
 
 
 	pr_debug("registering driver %s\n", driver->drv.name);
 	pr_debug("registering driver %s\n", driver->drv.name);
@@ -239,30 +237,21 @@ static void pcmcia_release_function(struct kref *ref)
 static void pcmcia_release_dev(struct device *dev)
 static void pcmcia_release_dev(struct device *dev)
 {
 {
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
+	int i;
 	dev_dbg(dev, "releasing device\n");
 	dev_dbg(dev, "releasing device\n");
 	pcmcia_put_socket(p_dev->socket);
 	pcmcia_put_socket(p_dev->socket);
+	for (i = 0; i < 4; i++)
+		kfree(p_dev->prod_id[i]);
 	kfree(p_dev->devname);
 	kfree(p_dev->devname);
 	kref_put(&p_dev->function_config->ref, pcmcia_release_function);
 	kref_put(&p_dev->function_config->ref, pcmcia_release_function);
 	kfree(p_dev);
 	kfree(p_dev);
 }
 }
 
 
-static void pcmcia_add_device_later(struct pcmcia_socket *s, int mfc)
-{
-	if (!s->pcmcia_state.device_add_pending) {
-		dev_dbg(&s->dev, "scheduling to add %s secondary"
-		       " device to %d\n", mfc ? "mfc" : "pfc", s->sock);
-		s->pcmcia_state.device_add_pending = 1;
-		s->pcmcia_state.mfc_pfc = mfc;
-		schedule_work(&s->device_add);
-	}
-	return;
-}
 
 
 static int pcmcia_device_probe(struct device *dev)
 static int pcmcia_device_probe(struct device *dev)
 {
 {
 	struct pcmcia_device *p_dev;
 	struct pcmcia_device *p_dev;
 	struct pcmcia_driver *p_drv;
 	struct pcmcia_driver *p_drv;
-	struct pcmcia_device_id *did;
 	struct pcmcia_socket *s;
 	struct pcmcia_socket *s;
 	cistpl_config_t cis_config;
 	cistpl_config_t cis_config;
 	int ret = 0;
 	int ret = 0;
@@ -275,18 +264,6 @@ static int pcmcia_device_probe(struct device *dev)
 	p_drv = to_pcmcia_drv(dev->driver);
 	p_drv = to_pcmcia_drv(dev->driver);
 	s = p_dev->socket;
 	s = p_dev->socket;
 
 
-	/* The PCMCIA code passes the match data in via dev_set_drvdata(dev)
-	 * which is an ugly hack. Once the driver probe is called it may
-	 * and often will overwrite the match data so we must save it first
-	 *
-	 * handle pseudo multifunction devices:
-	 * there are at most two pseudo multifunction devices.
-	 * if we're matching against the first, schedule a
-	 * call which will then check whether there are two
-	 * pseudo devices, and if not, add the second one.
-	 */
-	did = dev_get_drvdata(&p_dev->dev);
-
 	dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name);
 	dev_dbg(dev, "trying to bind to %s\n", p_drv->drv.name);
 
 
 	if ((!p_drv->probe) || (!p_dev->function_config) ||
 	if ((!p_drv->probe) || (!p_dev->function_config) ||
@@ -315,9 +292,11 @@ static int pcmcia_device_probe(struct device *dev)
 		goto put_module;
 		goto put_module;
 	}
 	}
 
 
-	if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
+	mutex_lock(&s->ops_mutex);
+	if ((s->pcmcia_state.has_pfc) &&
 	    (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
 	    (p_dev->socket->device_count == 1) && (p_dev->device_no == 0))
-		pcmcia_add_device_later(p_dev->socket, 0);
+		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
+	mutex_unlock(&s->ops_mutex);
 
 
 put_module:
 put_module:
 	if (ret)
 	if (ret)
@@ -336,26 +315,27 @@ static void pcmcia_card_remove(struct pcmcia_socket *s, struct pcmcia_device *le
 {
 {
 	struct pcmcia_device	*p_dev;
 	struct pcmcia_device	*p_dev;
 	struct pcmcia_device	*tmp;
 	struct pcmcia_device	*tmp;
-	unsigned long		flags;
 
 
 	dev_dbg(leftover ? &leftover->dev : &s->dev,
 	dev_dbg(leftover ? &leftover->dev : &s->dev,
 		   "pcmcia_card_remove(%d) %s\n", s->sock,
 		   "pcmcia_card_remove(%d) %s\n", s->sock,
 		   leftover ? leftover->devname : "");
 		   leftover ? leftover->devname : "");
 
 
+	mutex_lock(&s->ops_mutex);
 	if (!leftover)
 	if (!leftover)
 		s->device_count = 0;
 		s->device_count = 0;
 	else
 	else
 		s->device_count = 1;
 		s->device_count = 1;
+	mutex_unlock(&s->ops_mutex);
 
 
 	/* unregister all pcmcia_devices registered with this socket, except leftover */
 	/* unregister all pcmcia_devices registered with this socket, except leftover */
 	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
 	list_for_each_entry_safe(p_dev, tmp, &s->devices_list, socket_device_list) {
 		if (p_dev == leftover)
 		if (p_dev == leftover)
 			continue;
 			continue;
 
 
-		spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+		mutex_lock(&s->ops_mutex);
 		list_del(&p_dev->socket_device_list);
 		list_del(&p_dev->socket_device_list);
 		p_dev->_removed = 1;
 		p_dev->_removed = 1;
-		spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+		mutex_unlock(&s->ops_mutex);
 
 
 		dev_dbg(&p_dev->dev, "unregistering device\n");
 		dev_dbg(&p_dev->dev, "unregistering device\n");
 		device_unregister(&p_dev->dev);
 		device_unregister(&p_dev->dev);
@@ -368,7 +348,6 @@ static int pcmcia_device_remove(struct device *dev)
 {
 {
 	struct pcmcia_device *p_dev;
 	struct pcmcia_device *p_dev;
 	struct pcmcia_driver *p_drv;
 	struct pcmcia_driver *p_drv;
-	struct pcmcia_device_id *did;
 	int i;
 	int i;
 
 
 	p_dev = to_pcmcia_dev(dev);
 	p_dev = to_pcmcia_dev(dev);
@@ -380,9 +359,8 @@ static int pcmcia_device_remove(struct device *dev)
 	 * pseudo multi-function card, we need to unbind
 	 * pseudo multi-function card, we need to unbind
 	 * all devices
 	 * all devices
 	 */
 	 */
-	did = dev_get_drvdata(&p_dev->dev);
-	if (did && (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) &&
-	    (p_dev->socket->device_count != 0) &&
+	if ((p_dev->socket->pcmcia_state.has_pfc) &&
+	    (p_dev->socket->device_count > 0) &&
 	    (p_dev->device_no == 0))
 	    (p_dev->device_no == 0))
 		pcmcia_card_remove(p_dev->socket, p_dev);
 		pcmcia_card_remove(p_dev->socket, p_dev);
 
 
@@ -431,16 +409,20 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
 
 
 	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
 	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL,
 			       CISTPL_MANFID, &manf_id)) {
 			       CISTPL_MANFID, &manf_id)) {
+		mutex_lock(&p_dev->socket->ops_mutex);
 		p_dev->manf_id = manf_id.manf;
 		p_dev->manf_id = manf_id.manf;
 		p_dev->card_id = manf_id.card;
 		p_dev->card_id = manf_id.card;
 		p_dev->has_manf_id = 1;
 		p_dev->has_manf_id = 1;
 		p_dev->has_card_id = 1;
 		p_dev->has_card_id = 1;
+		mutex_unlock(&p_dev->socket->ops_mutex);
 	}
 	}
 
 
 	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
 	if (!pccard_read_tuple(p_dev->socket, p_dev->func,
 			       CISTPL_FUNCID, &func_id)) {
 			       CISTPL_FUNCID, &func_id)) {
+		mutex_lock(&p_dev->socket->ops_mutex);
 		p_dev->func_id = func_id.func;
 		p_dev->func_id = func_id.func;
 		p_dev->has_func_id = 1;
 		p_dev->has_func_id = 1;
+		mutex_unlock(&p_dev->socket->ops_mutex);
 	} else {
 	} else {
 		/* rule of thumb: cards with no FUNCID, but with
 		/* rule of thumb: cards with no FUNCID, but with
 		 * common memory device geometry information, are
 		 * common memory device geometry information, are
@@ -457,17 +439,21 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
 			dev_dbg(&p_dev->dev,
 			dev_dbg(&p_dev->dev,
 				   "mem device geometry probably means "
 				   "mem device geometry probably means "
 				   "FUNCID_MEMORY\n");
 				   "FUNCID_MEMORY\n");
+			mutex_lock(&p_dev->socket->ops_mutex);
 			p_dev->func_id = CISTPL_FUNCID_MEMORY;
 			p_dev->func_id = CISTPL_FUNCID_MEMORY;
 			p_dev->has_func_id = 1;
 			p_dev->has_func_id = 1;
+			mutex_unlock(&p_dev->socket->ops_mutex);
 		}
 		}
 		kfree(devgeo);
 		kfree(devgeo);
 	}
 	}
 
 
 	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
 	if (!pccard_read_tuple(p_dev->socket, BIND_FN_ALL, CISTPL_VERS_1,
 			       vers1)) {
 			       vers1)) {
+		mutex_lock(&p_dev->socket->ops_mutex);
 		for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
 		for (i = 0; i < min_t(unsigned int, 4, vers1->ns); i++) {
 			char *tmp;
 			char *tmp;
 			unsigned int length;
 			unsigned int length;
+			char *new;
 
 
 			tmp = vers1->str + vers1->ofs[i];
 			tmp = vers1->str + vers1->ofs[i];
 
 
@@ -475,14 +461,17 @@ static int pcmcia_device_query(struct pcmcia_device *p_dev)
 			if ((length < 2) || (length > 255))
 			if ((length < 2) || (length > 255))
 				continue;
 				continue;
 
 
-			p_dev->prod_id[i] = kmalloc(sizeof(char) * length,
-						    GFP_KERNEL);
-			if (!p_dev->prod_id[i])
+			new = kmalloc(sizeof(char) * length, GFP_KERNEL);
+			if (!new)
 				continue;
 				continue;
 
 
-			p_dev->prod_id[i] = strncpy(p_dev->prod_id[i],
-						    tmp, length);
+			new = strncpy(new, tmp, length);
+
+			tmp = p_dev->prod_id[i];
+			p_dev->prod_id[i] = new;
+			kfree(tmp);
 		}
 		}
+		mutex_unlock(&p_dev->socket->ops_mutex);
 	}
 	}
 
 
 	kfree(vers1);
 	kfree(vers1);
@@ -502,7 +491,7 @@ static DEFINE_MUTEX(device_add_lock);
 struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
 struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int function)
 {
 {
 	struct pcmcia_device *p_dev, *tmp_dev;
 	struct pcmcia_device *p_dev, *tmp_dev;
-	unsigned long flags;
+	int i;
 
 
 	s = pcmcia_get_socket(s);
 	s = pcmcia_get_socket(s);
 	if (!s)
 	if (!s)
@@ -512,16 +501,19 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
 
 
 	pr_debug("adding device to %d, function %d\n", s->sock, function);
 	pr_debug("adding device to %d, function %d\n", s->sock, function);
 
 
-	/* max of 4 devices per card */
-	if (s->device_count == 4)
-		goto err_put;
-
 	p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
 	p_dev = kzalloc(sizeof(struct pcmcia_device), GFP_KERNEL);
 	if (!p_dev)
 	if (!p_dev)
 		goto err_put;
 		goto err_put;
 
 
-	p_dev->socket = s;
+	mutex_lock(&s->ops_mutex);
 	p_dev->device_no = (s->device_count++);
 	p_dev->device_no = (s->device_count++);
+	mutex_unlock(&s->ops_mutex);
+
+	/* max of 2 devices per card */
+	if (p_dev->device_no >= 2)
+		goto err_free;
+
+	p_dev->socket = s;
 	p_dev->func   = function;
 	p_dev->func   = function;
 
 
 	p_dev->dev.bus = &pcmcia_bus_type;
 	p_dev->dev.bus = &pcmcia_bus_type;
@@ -538,7 +530,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
 		goto err_free;
 		goto err_free;
 	dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
 	dev_dbg(&p_dev->dev, "devname is %s\n", p_dev->devname);
 
 
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
 
 
 	/*
 	/*
 	 * p_dev->function_config must be the same for all card functions.
 	 * p_dev->function_config must be the same for all card functions.
@@ -556,7 +548,7 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
 	/* Add to the list in pcmcia_bus_socket */
 	/* Add to the list in pcmcia_bus_socket */
 	list_add(&p_dev->socket_device_list, &s->devices_list);
 	list_add(&p_dev->socket_device_list, &s->devices_list);
 
 
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 
 
 	if (!p_dev->function_config) {
 	if (!p_dev->function_config) {
 		dev_dbg(&p_dev->dev, "creating config_t\n");
 		dev_dbg(&p_dev->dev, "creating config_t\n");
@@ -581,14 +573,19 @@ struct pcmcia_device *pcmcia_device_add(struct pcmcia_socket *s, unsigned int fu
 	return p_dev;
 	return p_dev;
 
 
  err_unreg:
  err_unreg:
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
 	list_del(&p_dev->socket_device_list);
 	list_del(&p_dev->socket_device_list);
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 
 
  err_free:
  err_free:
+	mutex_lock(&s->ops_mutex);
+	s->device_count--;
+	mutex_unlock(&s->ops_mutex);
+
+	for (i = 0; i < 4; i++)
+		kfree(p_dev->prod_id[i]);
 	kfree(p_dev->devname);
 	kfree(p_dev->devname);
 	kfree(p_dev);
 	kfree(p_dev);
-	s->device_count--;
  err_put:
  err_put:
 	mutex_unlock(&device_add_lock);
 	mutex_unlock(&device_add_lock);
 	pcmcia_put_socket(s);
 	pcmcia_put_socket(s);
@@ -601,19 +598,23 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
 {
 {
 	cistpl_longlink_mfc_t mfc;
 	cistpl_longlink_mfc_t mfc;
 	unsigned int no_funcs, i, no_chains;
 	unsigned int no_funcs, i, no_chains;
-	int ret = 0;
+	int ret = -EAGAIN;
 
 
+	mutex_lock(&s->ops_mutex);
 	if (!(s->resource_setup_done)) {
 	if (!(s->resource_setup_done)) {
 		dev_dbg(&s->dev,
 		dev_dbg(&s->dev,
 			   "no resources available, delaying card_add\n");
 			   "no resources available, delaying card_add\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EAGAIN; /* try again, but later... */
 		return -EAGAIN; /* try again, but later... */
 	}
 	}
 
 
 	if (pcmcia_validate_mem(s)) {
 	if (pcmcia_validate_mem(s)) {
 		dev_dbg(&s->dev, "validating mem resources failed, "
 		dev_dbg(&s->dev, "validating mem resources failed, "
 		       "delaying card_add\n");
 		       "delaying card_add\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EAGAIN; /* try again, but later... */
 		return -EAGAIN; /* try again, but later... */
 	}
 	}
+	mutex_unlock(&s->ops_mutex);
 
 
 	ret = pccard_validate_cis(s, &no_chains);
 	ret = pccard_validate_cis(s, &no_chains);
 	if (ret || !no_chains) {
 	if (ret || !no_chains) {
@@ -634,17 +635,7 @@ static int pcmcia_card_add(struct pcmcia_socket *s)
 }
 }
 
 
 
 
-static void pcmcia_delayed_add_device(struct work_struct *work)
-{
-	struct pcmcia_socket *s =
-		container_of(work, struct pcmcia_socket, device_add);
-	dev_dbg(&s->dev, "adding additional device to %d\n", s->sock);
-	pcmcia_device_add(s, s->pcmcia_state.mfc_pfc);
-	s->pcmcia_state.device_add_pending = 0;
-	s->pcmcia_state.mfc_pfc = 0;
-}
-
-static int pcmcia_requery(struct device *dev, void * _data)
+static int pcmcia_requery_callback(struct device *dev, void * _data)
 {
 {
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 	if (!p_dev->dev.driver) {
 	if (!p_dev->dev.driver) {
@@ -655,45 +646,67 @@ static int pcmcia_requery(struct device *dev, void * _data)
 	return 0;
 	return 0;
 }
 }
 
 
-static void pcmcia_bus_rescan(struct pcmcia_socket *skt, int new_cis)
-{
-	int no_devices = 0;
-	int ret = 0;
-	unsigned long flags;
 
 
-	/* must be called with skt_mutex held */
-	dev_dbg(&skt->dev, "re-scanning socket %d\n", skt->sock);
+static void pcmcia_requery(struct pcmcia_socket *s)
+{
+	int present, has_pfc;
 
 
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
-	if (list_empty(&skt->devices_list))
-		no_devices = 1;
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
+	present = s->pcmcia_state.present;
+	mutex_unlock(&s->ops_mutex);
 
 
-	/* If this is because of a CIS override, start over */
-	if (new_cis && !no_devices)
-		pcmcia_card_remove(skt, NULL);
+	if (!present)
+		return;
 
 
-	/* if no devices were added for this socket yet because of
-	 * missing resource information or other trouble, we need to
-	 * do this now. */
-	if (no_devices || new_cis) {
-		ret = pcmcia_card_add(skt);
-		if (ret)
-			return;
+	if (s->functions == 0) {
+		pcmcia_card_add(s);
+		return;
 	}
 	}
 
 
 	/* some device information might have changed because of a CIS
 	/* some device information might have changed because of a CIS
 	 * update or because we can finally read it correctly... so
 	 * update or because we can finally read it correctly... so
 	 * determine it again, overwriting old values if necessary. */
 	 * determine it again, overwriting old values if necessary. */
-	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery);
+	bus_for_each_dev(&pcmcia_bus_type, NULL, NULL, pcmcia_requery_callback);
+
+	/* if the CIS changed, we need to check whether the number of
+	 * functions changed. */
+	if (s->fake_cis) {
+		int old_funcs, new_funcs;
+		cistpl_longlink_mfc_t mfc;
+
+		/* does this cis override add or remove functions? */
+		old_funcs = s->functions;
+
+		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC,
+					&mfc))
+			new_funcs = mfc.nfn;
+		else
+			new_funcs = 1;
+		if (old_funcs > new_funcs) {
+			pcmcia_card_remove(s, NULL);
+			pcmcia_card_add(s);
+		} else if (new_funcs > old_funcs) {
+			s->functions = new_funcs;
+			pcmcia_device_add(s, 1);
+		}
+	}
+
+	/* If the PCMCIA device consists of two pseudo devices,
+	 * call pcmcia_device_add() -- which will fail if both
+	 * devices are already registered. */
+	mutex_lock(&s->ops_mutex);
+	has_pfc = s->pcmcia_state.has_pfc;
+	mutex_unlock(&s->ops_mutex);
+	if (has_pfc)
+		pcmcia_device_add(s, 0);
 
 
 	/* we re-scan all devices, not just the ones connected to this
 	/* we re-scan all devices, not just the ones connected to this
 	 * socket. This does not matter, though. */
 	 * socket. This does not matter, though. */
-	ret = bus_rescan_devices(&pcmcia_bus_type);
-	if (ret)
-		printk(KERN_INFO "pcmcia: bus_rescan_devices failed\n");
+	if (bus_rescan_devices(&pcmcia_bus_type))
+		dev_warn(&s->dev, "rescanning the bus failed\n");
 }
 }
 
 
+
 #ifdef CONFIG_PCMCIA_LOAD_CIS
 #ifdef CONFIG_PCMCIA_LOAD_CIS
 
 
 /**
 /**
@@ -710,9 +723,6 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
 	struct pcmcia_socket *s = dev->socket;
 	struct pcmcia_socket *s = dev->socket;
 	const struct firmware *fw;
 	const struct firmware *fw;
 	int ret = -ENOMEM;
 	int ret = -ENOMEM;
-	int no_funcs;
-	int old_funcs;
-	cistpl_longlink_mfc_t mfc;
 
 
 	if (!filename)
 	if (!filename)
 		return -EINVAL;
 		return -EINVAL;
@@ -739,19 +749,8 @@ static int pcmcia_load_firmware(struct pcmcia_device *dev, char * filename)
 		/* update information */
 		/* update information */
 		pcmcia_device_query(dev);
 		pcmcia_device_query(dev);
 
 
-		/* does this cis override add or remove functions? */
-		old_funcs = s->functions;
-
-		if (!pccard_read_tuple(s, BIND_FN_ALL, CISTPL_LONGLINK_MFC, &mfc))
-			no_funcs = mfc.nfn;
-		else
-			no_funcs = 1;
-		s->functions = no_funcs;
-
-		if (old_funcs > no_funcs)
-			pcmcia_card_remove(s, dev);
-		else if (no_funcs > old_funcs)
-			pcmcia_add_device_later(s, 1);
+		/* requery (as number of functions might have changed) */
+		pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
 	}
 	}
  release:
  release:
 	release_firmware(fw);
 	release_firmware(fw);
@@ -818,9 +817,14 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_DEVICE_NO) {
 		if (dev->device_no != did->device_no)
 		if (dev->device_no != did->device_no)
 			return 0;
 			return 0;
+		mutex_lock(&dev->socket->ops_mutex);
+		dev->socket->pcmcia_state.has_pfc = 1;
+		mutex_unlock(&dev->socket->ops_mutex);
 	}
 	}
 
 
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FUNC_ID) {
+		int ret;
+
 		if ((!dev->has_func_id) || (dev->func_id != did->func_id))
 		if ((!dev->has_func_id) || (dev->func_id != did->func_id))
 			return 0;
 			return 0;
 
 
@@ -835,10 +839,15 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
 		 * after it has re-checked that there is no possible module
 		 * after it has re-checked that there is no possible module
 		 * with a prod_id/manf_id/card_id match.
 		 * with a prod_id/manf_id/card_id match.
 		 */
 		 */
-		dev_dbg(&dev->dev,
-			"skipping FUNC_ID match until userspace interaction\n");
-		if (!dev->allow_func_id_match)
+		mutex_lock(&dev->socket->ops_mutex);
+		ret = dev->allow_func_id_match;
+		mutex_unlock(&dev->socket->ops_mutex);
+
+		if (!ret) {
+			dev_dbg(&dev->dev,
+				"skipping FUNC_ID match until userspace ACK\n");
 			return 0;
 			return 0;
+		}
 	}
 	}
 
 
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
 	if (did->match_flags & PCMCIA_DEV_ID_MATCH_FAKE_CIS) {
@@ -859,8 +868,6 @@ static inline int pcmcia_devmatch(struct pcmcia_device *dev,
 			return 0;
 			return 0;
 	}
 	}
 
 
-	dev_set_drvdata(&dev->dev, did);
-
 	return 1;
 	return 1;
 }
 }
 
 
@@ -873,16 +880,16 @@ static int pcmcia_bus_match(struct device *dev, struct device_driver *drv)
 	struct pcmcia_dynid *dynid;
 	struct pcmcia_dynid *dynid;
 
 
 	/* match dynamic devices first */
 	/* match dynamic devices first */
-	spin_lock(&p_drv->dynids.lock);
+	mutex_lock(&p_drv->dynids.lock);
 	list_for_each_entry(dynid, &p_drv->dynids.list, node) {
 	list_for_each_entry(dynid, &p_drv->dynids.list, node) {
 		dev_dbg(dev, "trying to match to %s\n", drv->name);
 		dev_dbg(dev, "trying to match to %s\n", drv->name);
 		if (pcmcia_devmatch(p_dev, &dynid->id)) {
 		if (pcmcia_devmatch(p_dev, &dynid->id)) {
 			dev_dbg(dev, "matched to %s\n", drv->name);
 			dev_dbg(dev, "matched to %s\n", drv->name);
-			spin_unlock(&p_drv->dynids.lock);
+			mutex_unlock(&p_drv->dynids.lock);
 			return 1;
 			return 1;
 		}
 		}
 	}
 	}
-	spin_unlock(&p_drv->dynids.lock);
+	mutex_unlock(&p_drv->dynids.lock);
 
 
 #ifdef CONFIG_PCMCIA_IOCTL
 #ifdef CONFIG_PCMCIA_IOCTL
 	/* matching by cardmgr */
 	/* matching by cardmgr */
@@ -970,13 +977,14 @@ static int runtime_suspend(struct device *dev)
 	return rc;
 	return rc;
 }
 }
 
 
-static void runtime_resume(struct device *dev)
+static int runtime_resume(struct device *dev)
 {
 {
 	int rc;
 	int rc;
 
 
 	down(&dev->sem);
 	down(&dev->sem);
 	rc = pcmcia_dev_resume(dev);
 	rc = pcmcia_dev_resume(dev);
 	up(&dev->sem);
 	up(&dev->sem);
+	return rc;
 }
 }
 
 
 /************************ per-device sysfs output ***************************/
 /************************ per-device sysfs output ***************************/
@@ -1027,7 +1035,7 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute
 	if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
 	if ((!p_dev->suspended) && !strncmp(buf, "off", 3))
 		ret = runtime_suspend(dev);
 		ret = runtime_suspend(dev);
 	else if (p_dev->suspended && !strncmp(buf, "on", 2))
 	else if (p_dev->suspended && !strncmp(buf, "on", 2))
-		runtime_resume(dev);
+		ret = runtime_resume(dev);
 
 
 	return ret ? ret : count;
 	return ret ? ret : count;
 }
 }
@@ -1059,19 +1067,14 @@ static ssize_t pcmcia_store_allow_func_id_match(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t count)
 		struct device_attribute *attr, const char *buf, size_t count)
 {
 {
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
 	struct pcmcia_device *p_dev = to_pcmcia_dev(dev);
-	int ret;
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	mutex_lock(&p_dev->socket->skt_mutex);
+	mutex_lock(&p_dev->socket->ops_mutex);
 	p_dev->allow_func_id_match = 1;
 	p_dev->allow_func_id_match = 1;
-	mutex_unlock(&p_dev->socket->skt_mutex);
-
-	ret = bus_rescan_devices(&pcmcia_bus_type);
-	if (ret)
-		printk(KERN_INFO "pcmcia: bus_rescan_devices failed after "
-		       "allowing func_id matches\n");
+	mutex_unlock(&p_dev->socket->ops_mutex);
+	pcmcia_parse_uevents(p_dev->socket, PCMCIA_UEVENT_REQUERY);
 
 
 	return count;
 	return count;
 }
 }
@@ -1099,8 +1102,13 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
 	struct pcmcia_driver *p_drv = NULL;
 	struct pcmcia_driver *p_drv = NULL;
 	int ret = 0;
 	int ret = 0;
 
 
-	if (p_dev->suspended)
+	mutex_lock(&p_dev->socket->ops_mutex);
+	if (p_dev->suspended) {
+		mutex_unlock(&p_dev->socket->ops_mutex);
 		return 0;
 		return 0;
+	}
+	p_dev->suspended = 1;
+	mutex_unlock(&p_dev->socket->ops_mutex);
 
 
 	dev_dbg(dev, "suspending\n");
 	dev_dbg(dev, "suspending\n");
 
 
@@ -1117,6 +1125,9 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
 				   "pcmcia: device %s (driver %s) did "
 				   "pcmcia: device %s (driver %s) did "
 				   "not want to go to sleep (%d)\n",
 				   "not want to go to sleep (%d)\n",
 				   p_dev->devname, p_drv->drv.name, ret);
 				   p_dev->devname, p_drv->drv.name, ret);
+			mutex_lock(&p_dev->socket->ops_mutex);
+			p_dev->suspended = 0;
+			mutex_unlock(&p_dev->socket->ops_mutex);
 			goto out;
 			goto out;
 		}
 		}
 	}
 	}
@@ -1127,8 +1138,6 @@ static int pcmcia_dev_suspend(struct device *dev, pm_message_t state)
 	}
 	}
 
 
  out:
  out:
-	if (!ret)
-		p_dev->suspended = 1;
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1139,8 +1148,13 @@ static int pcmcia_dev_resume(struct device *dev)
 	struct pcmcia_driver *p_drv = NULL;
 	struct pcmcia_driver *p_drv = NULL;
 	int ret = 0;
 	int ret = 0;
 
 
-	if (!p_dev->suspended)
+	mutex_lock(&p_dev->socket->ops_mutex);
+	if (!p_dev->suspended) {
+		mutex_unlock(&p_dev->socket->ops_mutex);
 		return 0;
 		return 0;
+	}
+	p_dev->suspended = 0;
+	mutex_unlock(&p_dev->socket->ops_mutex);
 
 
 	dev_dbg(dev, "resuming\n");
 	dev_dbg(dev, "resuming\n");
 
 
@@ -1161,8 +1175,6 @@ static int pcmcia_dev_resume(struct device *dev)
 		ret = p_drv->resume(p_dev);
 		ret = p_drv->resume(p_dev);
 
 
  out:
  out:
-	if (!ret)
-		p_dev->suspended = 0;
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1237,13 +1249,22 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
 
 
 	switch (event) {
 	switch (event) {
 	case CS_EVENT_CARD_REMOVAL:
 	case CS_EVENT_CARD_REMOVAL:
+		mutex_lock(&s->ops_mutex);
 		s->pcmcia_state.present = 0;
 		s->pcmcia_state.present = 0;
+		mutex_unlock(&s->ops_mutex);
 		pcmcia_card_remove(skt, NULL);
 		pcmcia_card_remove(skt, NULL);
 		handle_event(skt, event);
 		handle_event(skt, event);
+		mutex_lock(&s->ops_mutex);
+		destroy_cis_cache(s);
+		mutex_unlock(&s->ops_mutex);
 		break;
 		break;
 
 
 	case CS_EVENT_CARD_INSERTION:
 	case CS_EVENT_CARD_INSERTION:
+		mutex_lock(&s->ops_mutex);
+		s->pcmcia_state.has_pfc = 0;
 		s->pcmcia_state.present = 1;
 		s->pcmcia_state.present = 1;
+		destroy_cis_cache(s); /* to be on the safe side... */
+		mutex_unlock(&s->ops_mutex);
 		pcmcia_card_add(skt);
 		pcmcia_card_add(skt);
 		handle_event(skt, event);
 		handle_event(skt, event);
 		break;
 		break;
@@ -1251,8 +1272,24 @@ static int ds_event(struct pcmcia_socket *skt, event_t event, int priority)
 	case CS_EVENT_EJECTION_REQUEST:
 	case CS_EVENT_EJECTION_REQUEST:
 		break;
 		break;
 
 
-	case CS_EVENT_PM_SUSPEND:
 	case CS_EVENT_PM_RESUME:
 	case CS_EVENT_PM_RESUME:
+		if (verify_cis_cache(skt) != 0) {
+			dev_dbg(&skt->dev, "cis mismatch - different card\n");
+			/* first, remove the card */
+			ds_event(skt, CS_EVENT_CARD_REMOVAL, CS_EVENT_PRI_HIGH);
+			mutex_lock(&s->ops_mutex);
+			destroy_cis_cache(skt);
+			kfree(skt->fake_cis);
+			skt->fake_cis = NULL;
+			mutex_unlock(&s->ops_mutex);
+			/* now, add the new card */
+			ds_event(skt, CS_EVENT_CARD_INSERTION,
+				 CS_EVENT_PRI_LOW);
+		}
+		handle_event(skt, event);
+		break;
+
+	case CS_EVENT_PM_SUSPEND:
 	case CS_EVENT_RESET_PHYSICAL:
 	case CS_EVENT_RESET_PHYSICAL:
 	case CS_EVENT_CARD_RESET:
 	case CS_EVENT_CARD_RESET:
 	default:
 	default:
@@ -1275,9 +1312,13 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
 	if (!p_dev)
 	if (!p_dev)
 		return NULL;
 		return NULL;
 
 
+	mutex_lock(&p_dev->socket->ops_mutex);
 	if (!p_dev->socket->pcmcia_state.present)
 	if (!p_dev->socket->pcmcia_state.present)
 		goto out;
 		goto out;
 
 
+	if (p_dev->socket->pcmcia_state.dead)
+		goto out;
+
 	if (p_dev->_removed)
 	if (p_dev->_removed)
 		goto out;
 		goto out;
 
 
@@ -1286,6 +1327,7 @@ struct pcmcia_device *pcmcia_dev_present(struct pcmcia_device *_p_dev)
 
 
 	ret = p_dev;
 	ret = p_dev;
  out:
  out:
+	mutex_unlock(&p_dev->socket->ops_mutex);
 	pcmcia_put_dev(p_dev);
 	pcmcia_put_dev(p_dev);
 	return ret;
 	return ret;
 }
 }
@@ -1295,7 +1337,8 @@ EXPORT_SYMBOL(pcmcia_dev_present);
 static struct pcmcia_callback pcmcia_bus_callback = {
 static struct pcmcia_callback pcmcia_bus_callback = {
 	.owner = THIS_MODULE,
 	.owner = THIS_MODULE,
 	.event = ds_event,
 	.event = ds_event,
-	.requery = pcmcia_bus_rescan,
+	.requery = pcmcia_requery,
+	.validate = pccard_validate_cis,
 	.suspend = pcmcia_bus_suspend,
 	.suspend = pcmcia_bus_suspend,
 	.resume = pcmcia_bus_resume,
 	.resume = pcmcia_bus_resume,
 };
 };
@@ -1313,17 +1356,17 @@ static int __devinit pcmcia_bus_add_socket(struct device *dev,
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	/*
-	 * Ugly. But we want to wait for the socket threads to have started up.
-	 * We really should let the drivers themselves drive some of this..
-	 */
-	msleep(250);
+	ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
+	if (ret) {
+		dev_printk(KERN_ERR, dev, "PCMCIA registration failed\n");
+		pcmcia_put_socket(socket);
+		return ret;
+	}
 
 
 #ifdef CONFIG_PCMCIA_IOCTL
 #ifdef CONFIG_PCMCIA_IOCTL
 	init_waitqueue_head(&socket->queue);
 	init_waitqueue_head(&socket->queue);
 #endif
 #endif
 	INIT_LIST_HEAD(&socket->devices_list);
 	INIT_LIST_HEAD(&socket->devices_list);
-	INIT_WORK(&socket->device_add, pcmcia_delayed_add_device);
 	memset(&socket->pcmcia_state, 0, sizeof(u8));
 	memset(&socket->pcmcia_state, 0, sizeof(u8));
 	socket->device_count = 0;
 	socket->device_count = 0;
 
 
@@ -1345,14 +1388,20 @@ static void pcmcia_bus_remove_socket(struct device *dev,
 	if (!socket)
 	if (!socket)
 		return;
 		return;
 
 
+	mutex_lock(&socket->ops_mutex);
 	socket->pcmcia_state.dead = 1;
 	socket->pcmcia_state.dead = 1;
+	mutex_unlock(&socket->ops_mutex);
+
 	pccard_register_pcmcia(socket, NULL);
 	pccard_register_pcmcia(socket, NULL);
 
 
 	/* unregister any unbound devices */
 	/* unregister any unbound devices */
 	mutex_lock(&socket->skt_mutex);
 	mutex_lock(&socket->skt_mutex);
 	pcmcia_card_remove(socket, NULL);
 	pcmcia_card_remove(socket, NULL);
+	release_cis_mem(socket);
 	mutex_unlock(&socket->skt_mutex);
 	mutex_unlock(&socket->skt_mutex);
 
 
+	sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
+
 	pcmcia_put_socket(socket);
 	pcmcia_put_socket(socket);
 
 
 	return;
 	return;
@@ -1383,8 +1432,6 @@ static int __init init_pcmcia_bus(void)
 {
 {
 	int ret;
 	int ret;
 
 
-	spin_lock_init(&pcmcia_dev_list_lock);
-
 	ret = bus_register(&pcmcia_bus_type);
 	ret = bus_register(&pcmcia_bus_type);
 	if (ret < 0) {
 	if (ret < 0) {
 		printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);
 		printk(KERN_WARNING "pcmcia: bus_register error: %d\n", ret);

+ 1 - 1
drivers/pcmcia/electra_cf.c

@@ -347,7 +347,7 @@ static int __devexit electra_cf_remove(struct of_device *ofdev)
 	return 0;
 	return 0;
 }
 }
 
 
-static struct of_device_id electra_cf_match[] = {
+static const struct of_device_id electra_cf_match[] = {
 	{
 	{
 		.compatible   = "electra-cf",
 		.compatible   = "electra-cf",
 	},
 	},

+ 2 - 2
drivers/pcmcia/i82365.h

@@ -77,8 +77,8 @@
 #define I365_VPP2_5V	0x04	/* Vpp2 = 5.0v */
 #define I365_VPP2_5V	0x04	/* Vpp2 = 5.0v */
 #define I365_VPP2_12V	0x08	/* Vpp2 = 12.0v */
 #define I365_VPP2_12V	0x08	/* Vpp2 = 12.0v */
 #define I365_VPP1_MASK	0x03	/* Mask for turning off Vpp1 */
 #define I365_VPP1_MASK	0x03	/* Mask for turning off Vpp1 */
-#define I365_VPP1_5V	0x01	/* Vpp2 = 5.0v */
-#define I365_VPP1_12V	0x02	/* Vpp2 = 12.0v */
+#define I365_VPP1_5V	0x01	/* Vpp1 = 5.0v */
+#define I365_VPP1_12V	0x02	/* Vpp1 = 12.0v */
 
 
 /* Flags for I365_INTCTL */
 /* Flags for I365_INTCTL */
 #define I365_RING_ENA	0x80
 #define I365_RING_ENA	0x80

+ 1 - 1
drivers/pcmcia/m32r_cfc.c

@@ -764,7 +764,7 @@ static int __init init_m32r_pcc(void)
 	for (i = 0 ; i < pcc_sockets ; i++) {
 	for (i = 0 ; i < pcc_sockets ; i++) {
 		socket[i].socket.dev.parent = &pcc_device.dev;
 		socket[i].socket.dev.parent = &pcc_device.dev;
 		socket[i].socket.ops = &pcc_operations;
 		socket[i].socket.ops = &pcc_operations;
-		socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+		socket[i].socket.resource_ops = &pccard_static_ops;
 		socket[i].socket.owner = THIS_MODULE;
 		socket[i].socket.owner = THIS_MODULE;
 		socket[i].number = i;
 		socket[i].number = i;
 		ret = pcmcia_register_socket(&socket[i].socket);
 		ret = pcmcia_register_socket(&socket[i].socket);

+ 2 - 2
drivers/pcmcia/m8xx_pcmcia.c

@@ -1233,7 +1233,7 @@ static int __init m8xx_probe(struct of_device *ofdev,
 		socket[i].socket.io_offset = 0;
 		socket[i].socket.io_offset = 0;
 		socket[i].socket.pci_irq = pcmcia_schlvl;
 		socket[i].socket.pci_irq = pcmcia_schlvl;
 		socket[i].socket.ops = &m8xx_services;
 		socket[i].socket.ops = &m8xx_services;
-		socket[i].socket.resource_ops = &pccard_nonstatic_ops;
+		socket[i].socket.resource_ops = &pccard_iodyn_ops;
 		socket[i].socket.cb_dev = NULL;
 		socket[i].socket.cb_dev = NULL;
 		socket[i].socket.dev.parent = &ofdev->dev;
 		socket[i].socket.dev.parent = &ofdev->dev;
 		socket[i].pcmcia = pcmcia;
 		socket[i].pcmcia = pcmcia;
@@ -1303,7 +1303,7 @@ static int m8xx_resume(struct platform_device *pdev)
 #define m8xx_resume NULL
 #define m8xx_resume NULL
 #endif
 #endif
 
 
-static struct of_device_id m8xx_pcmcia_match[] = {
+static const struct of_device_id m8xx_pcmcia_match[] = {
 	{
 	{
 	 .type = "pcmcia",
 	 .type = "pcmcia",
 	 .compatible = "fsl,pq-pcmcia",
 	 .compatible = "fsl,pq-pcmcia",

+ 29 - 16
drivers/pcmcia/o2micro.h

@@ -116,13 +116,12 @@ static int o2micro_override(struct yenta_socket *socket)
 	 * from Eric Still, 02Micro.
 	 * from Eric Still, 02Micro.
 	 */
 	 */
 	u8 a, b;
 	u8 a, b;
+	bool use_speedup;
 
 
 	if (PCI_FUNC(socket->dev->devfn) == 0) {
 	if (PCI_FUNC(socket->dev->devfn) == 0) {
 		a = config_readb(socket, O2_RESERVED1);
 		a = config_readb(socket, O2_RESERVED1);
 		b = config_readb(socket, O2_RESERVED2);
 		b = config_readb(socket, O2_RESERVED2);
-
-		dev_printk(KERN_INFO, &socket->dev->dev,
-			   "O2: res at 0x94/0xD4: %02x/%02x\n", a, b);
+		dev_dbg(&socket->dev->dev, "O2: 0x94/0xD4: %02x/%02x\n", a, b);
 
 
 		switch (socket->dev->device) {
 		switch (socket->dev->device) {
 		/*
 		/*
@@ -135,23 +134,37 @@ static int o2micro_override(struct yenta_socket *socket)
 		case PCI_DEVICE_ID_O2_6812:
 		case PCI_DEVICE_ID_O2_6812:
 		case PCI_DEVICE_ID_O2_6832:
 		case PCI_DEVICE_ID_O2_6832:
 		case PCI_DEVICE_ID_O2_6836:
 		case PCI_DEVICE_ID_O2_6836:
- 		case PCI_DEVICE_ID_O2_6933:
-			dev_printk(KERN_INFO, &socket->dev->dev,
-				   "Yenta O2: old bridge, disabling read "
-				   "prefetch/write burst\n");
-			config_writeb(socket, O2_RESERVED1,
-			              a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
-			config_writeb(socket, O2_RESERVED2,
-			              b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
+		case PCI_DEVICE_ID_O2_6933:
+			use_speedup = false;
 			break;
 			break;
-
 		default:
 		default:
-			dev_printk(KERN_INFO , &socket->dev->dev,
-				   "O2: enabling read prefetch/write burst\n");
+			use_speedup = true;
+			break;
+		}
+
+		/* the user may override our decision */
+		if (strcasecmp(o2_speedup, "on") == 0)
+			use_speedup = true;
+		else if (strcasecmp(o2_speedup, "off") == 0)
+			use_speedup = false;
+		else if (strcasecmp(o2_speedup, "default") != 0)
+			dev_warn(&socket->dev->dev,
+				"O2: Unknown parameter, using 'default'");
+
+		if (use_speedup) {
+			dev_info(&socket->dev->dev,
+				"O2: enabling read prefetch/write burst\n");
+			config_writeb(socket, O2_RESERVED1,
+				      a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+			config_writeb(socket, O2_RESERVED2,
+				      b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+		} else {
+			dev_info(&socket->dev->dev,
+				"O2: disabling read prefetch/write burst\n");
 			config_writeb(socket, O2_RESERVED1,
 			config_writeb(socket, O2_RESERVED1,
-			              a | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+				      a & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
 			config_writeb(socket, O2_RESERVED2,
 			config_writeb(socket, O2_RESERVED2,
-			              b | O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST);
+				      b & ~(O2_RES_READ_PREFETCH | O2_RES_WRITE_BURST));
 		}
 		}
 	}
 	}
 
 

+ 0 - 2
drivers/pcmcia/omap_cf.c

@@ -71,8 +71,6 @@ struct omap_cf_socket {
 
 
 #define	POLL_INTERVAL		(2 * HZ)
 #define	POLL_INTERVAL		(2 * HZ)
 
 
-#define	SZ_2K			(2 * SZ_1K)
-
 /*--------------------------------------------------------------------------*/
 /*--------------------------------------------------------------------------*/
 
 
 static int omap_cf_ss_init(struct pcmcia_socket *s)
 static int omap_cf_ss_init(struct pcmcia_socket *s)

+ 18 - 24
drivers/pcmcia/pcmcia_ioctl.c

@@ -62,16 +62,15 @@ static struct pcmcia_device *get_pcmcia_device(struct pcmcia_socket *s,
 						unsigned int function)
 						unsigned int function)
 {
 {
 	struct pcmcia_device *p_dev = NULL;
 	struct pcmcia_device *p_dev = NULL;
-	unsigned long flags;
 
 
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 		if (p_dev->func == function) {
 		if (p_dev->func == function) {
-			spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+			mutex_unlock(&s->ops_mutex);
 			return pcmcia_get_dev(p_dev);
 			return pcmcia_get_dev(p_dev);
 		}
 		}
 	}
 	}
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 	return NULL;
 	return NULL;
 }
 }
 
 
@@ -169,7 +168,6 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
 {
 {
 	struct pcmcia_socket *s;
 	struct pcmcia_socket *s;
 	int ret = -ENOSYS;
 	int ret = -ENOSYS;
-	unsigned long flags;
 
 
 	down_read(&pcmcia_socket_list_rwsem);
 	down_read(&pcmcia_socket_list_rwsem);
 	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
 	list_for_each_entry(s, &pcmcia_socket_list, socket_list) {
@@ -182,14 +180,13 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
 
 
 			/* you can't use the old interface if the new
 			/* you can't use the old interface if the new
 			 * one was used before */
 			 * one was used before */
-			spin_lock_irqsave(&s->lock, flags);
+			mutex_lock(&s->ops_mutex);
 			if ((s->resource_setup_new) &&
 			if ((s->resource_setup_new) &&
 			    !(s->resource_setup_old)) {
 			    !(s->resource_setup_old)) {
-				spin_unlock_irqrestore(&s->lock, flags);
+				mutex_unlock(&s->ops_mutex);
 				continue;
 				continue;
 			} else if (!(s->resource_setup_old))
 			} else if (!(s->resource_setup_old))
 				s->resource_setup_old = 1;
 				s->resource_setup_old = 1;
-			spin_unlock_irqrestore(&s->lock, flags);
 
 
 			switch (adj->Resource) {
 			switch (adj->Resource) {
 			case RES_MEMORY_RANGE:
 			case RES_MEMORY_RANGE:
@@ -208,10 +205,9 @@ static int pcmcia_adjust_resource_info(adjust_t *adj)
 				 * last call to adjust_resource_info, we
 				 * last call to adjust_resource_info, we
 				 * always need to assume this is the latest
 				 * always need to assume this is the latest
 				 * one... */
 				 * one... */
-				spin_lock_irqsave(&s->lock, flags);
 				s->resource_setup_done = 1;
 				s->resource_setup_done = 1;
-				spin_unlock_irqrestore(&s->lock, flags);
 			}
 			}
+			mutex_unlock(&s->ops_mutex);
 		}
 		}
 	}
 	}
 	up_read(&pcmcia_socket_list_rwsem);
 	up_read(&pcmcia_socket_list_rwsem);
@@ -470,7 +466,6 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
 	struct pcmcia_driver *p_drv;
 	struct pcmcia_driver *p_drv;
 	struct pcmcia_device *p_dev;
 	struct pcmcia_device *p_dev;
 	int ret = 0;
 	int ret = 0;
-	unsigned long flags;
 
 
 	s = pcmcia_get_socket(s);
 	s = pcmcia_get_socket(s);
 	if (!s)
 	if (!s)
@@ -490,7 +485,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
 		goto err_put_driver;
 		goto err_put_driver;
 	}
 	}
 
 
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 		if (p_dev->func == bind_info->function) {
 		if (p_dev->func == bind_info->function) {
 			if ((p_dev->dev.driver == &p_drv->drv)) {
 			if ((p_dev->dev.driver == &p_drv->drv)) {
@@ -499,7 +494,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
 					 * registered, and it was registered
 					 * registered, and it was registered
 					 * by userspace before, we need to
 					 * by userspace before, we need to
 					 * return the "instance". */
 					 * return the "instance". */
-					spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+					mutex_unlock(&s->ops_mutex);
 					bind_info->instance = p_dev;
 					bind_info->instance = p_dev;
 					ret = -EBUSY;
 					ret = -EBUSY;
 					goto err_put_module;
 					goto err_put_module;
@@ -507,7 +502,7 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
 					/* the correct driver managed to bind
 					/* the correct driver managed to bind
 					 * itself magically to the correct
 					 * itself magically to the correct
 					 * device. */
 					 * device. */
-					spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+					mutex_unlock(&s->ops_mutex);
 					p_dev->cardmgr = p_drv;
 					p_dev->cardmgr = p_drv;
 					ret = 0;
 					ret = 0;
 					goto err_put_module;
 					goto err_put_module;
@@ -516,12 +511,12 @@ static int bind_request(struct pcmcia_socket *s, bind_info_t *bind_info)
 				/* there's already a device available where
 				/* there's already a device available where
 				 * no device has been bound to yet. So we don't
 				 * no device has been bound to yet. So we don't
 				 * need to register a device! */
 				 * need to register a device! */
-				spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+				mutex_unlock(&s->ops_mutex);
 				goto rescan;
 				goto rescan;
 			}
 			}
 		}
 		}
 	}
 	}
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 
 
 	p_dev = pcmcia_device_add(s, bind_info->function);
 	p_dev = pcmcia_device_add(s, bind_info->function);
 	if (!p_dev) {
 	if (!p_dev) {
@@ -578,7 +573,6 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
 	dev_node_t *node;
 	dev_node_t *node;
 	struct pcmcia_device *p_dev;
 	struct pcmcia_device *p_dev;
 	struct pcmcia_driver *p_drv;
 	struct pcmcia_driver *p_drv;
-	unsigned long flags;
 	int ret = 0;
 	int ret = 0;
 
 
 #ifdef CONFIG_CARDBUS
 #ifdef CONFIG_CARDBUS
@@ -617,7 +611,7 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
 	}
 	}
 #endif
 #endif
 
 
-	spin_lock_irqsave(&pcmcia_dev_list_lock, flags);
+	mutex_lock(&s->ops_mutex);
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 	list_for_each_entry(p_dev, &s->devices_list, socket_device_list) {
 		if (p_dev->func == bind_info->function) {
 		if (p_dev->func == bind_info->function) {
 			p_dev = pcmcia_get_dev(p_dev);
 			p_dev = pcmcia_get_dev(p_dev);
@@ -626,11 +620,11 @@ static int get_device_info(struct pcmcia_socket *s, bind_info_t *bind_info, int
 			goto found;
 			goto found;
 		}
 		}
 	}
 	}
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 	return -ENODEV;
 	return -ENODEV;
 
 
  found:
  found:
-	spin_unlock_irqrestore(&pcmcia_dev_list_lock, flags);
+	mutex_unlock(&s->ops_mutex);
 
 
 	p_drv = to_pcmcia_drv(p_dev->dev.driver);
 	p_drv = to_pcmcia_drv(p_dev->dev.driver);
 	if (p_drv && !p_dev->_locked) {
 	if (p_drv && !p_dev->_locked) {
@@ -931,16 +925,16 @@ static int ds_ioctl(struct inode *inode, struct file *file,
 	ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
 	ret = pccard_validate_cis(s, &buf->cisinfo.Chains);
 	break;
 	break;
     case DS_SUSPEND_CARD:
     case DS_SUSPEND_CARD:
-	ret = pcmcia_suspend_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
 	break;
 	break;
     case DS_RESUME_CARD:
     case DS_RESUME_CARD:
-	ret = pcmcia_resume_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
 	break;
 	break;
     case DS_EJECT_CARD:
     case DS_EJECT_CARD:
-	err = pcmcia_eject_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
 	break;
 	break;
     case DS_INSERT_CARD:
     case DS_INSERT_CARD:
-	err = pcmcia_insert_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
 	break;
 	break;
     case DS_ACCESS_CONFIGURATION_REGISTER:
     case DS_ACCESS_CONFIGURATION_REGISTER:
 	if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {
 	if ((buf->conf_reg.Action == CS_WRITE) && !capable(CAP_SYS_ADMIN)) {

+ 134 - 35
drivers/pcmcia/pcmcia_resource.c

@@ -43,6 +43,39 @@ module_param(io_speed, int, 0444);
 static u8 pcmcia_used_irq[NR_IRQS];
 static u8 pcmcia_used_irq[NR_IRQS];
 #endif
 #endif
 
 
+static int pcmcia_adjust_io_region(struct resource *res, unsigned long start,
+				   unsigned long end, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->adjust_io_region)
+		return s->resource_ops->adjust_io_region(res, start, end, s);
+	return -ENOMEM;
+}
+
+static struct resource *pcmcia_find_io_region(unsigned long base, int num,
+					      unsigned long align,
+					      struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_io)
+		return s->resource_ops->find_io(base, num, align, s);
+	return NULL;
+}
+
+int pcmcia_validate_mem(struct pcmcia_socket *s)
+{
+	if (s->resource_ops->validate_mem)
+		return s->resource_ops->validate_mem(s);
+	/* if there is no callback, we can assume that everything is OK */
+	return 0;
+}
+
+struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
+				 int low, struct pcmcia_socket *s)
+{
+	if (s->resource_ops->find_mem)
+		return s->resource_ops->find_mem(base, num, align, low, s);
+	return NULL;
+}
+
 
 
 /** alloc_io_space
 /** alloc_io_space
  *
  *
@@ -158,14 +191,18 @@ int pcmcia_access_configuration_register(struct pcmcia_device *p_dev,
 		return -EINVAL;
 		return -EINVAL;
 
 
 	s = p_dev->socket;
 	s = p_dev->socket;
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 	c = p_dev->function_config;
 
 
 	if (!(c->state & CONFIG_LOCKED)) {
 	if (!(c->state & CONFIG_LOCKED)) {
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EACCES;
 		return -EACCES;
 	}
 	}
 
 
 	addr = (c->ConfigBase + reg->Offset) >> 1;
 	addr = (c->ConfigBase + reg->Offset) >> 1;
+	mutex_unlock(&s->ops_mutex);
 
 
 	switch (reg->Action) {
 	switch (reg->Action) {
 	case CS_READ:
 	case CS_READ:
@@ -190,6 +227,7 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
 			memreq_t *req)
 			memreq_t *req)
 {
 {
 	struct pcmcia_socket *s = p_dev->socket;
 	struct pcmcia_socket *s = p_dev->socket;
+	int ret;
 
 
 	wh--;
 	wh--;
 	if (wh >= MAX_WIN)
 	if (wh >= MAX_WIN)
@@ -198,12 +236,13 @@ int pcmcia_map_mem_page(struct pcmcia_device *p_dev, window_handle_t wh,
 		dev_dbg(&s->dev, "failure: requested page is zero\n");
 		dev_dbg(&s->dev, "failure: requested page is zero\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+	mutex_lock(&s->ops_mutex);
 	s->win[wh].card_start = req->CardOffset;
 	s->win[wh].card_start = req->CardOffset;
-	if (s->ops->set_mem_map(s, &s->win[wh]) != 0) {
-		dev_dbg(&s->dev, "failed to set_mem_map\n");
-		return -EIO;
-	}
-	return 0;
+	ret = s->ops->set_mem_map(s, &s->win[wh]);
+	if (ret)
+		dev_warn(&s->dev, "failed to set_mem_map\n");
+	mutex_unlock(&s->ops_mutex);
+	return ret;
 } /* pcmcia_map_mem_page */
 } /* pcmcia_map_mem_page */
 EXPORT_SYMBOL(pcmcia_map_mem_page);
 EXPORT_SYMBOL(pcmcia_map_mem_page);
 
 
@@ -219,14 +258,18 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	config_t *c;
 	config_t *c;
 
 
 	s = p_dev->socket;
 	s = p_dev->socket;
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 	c = p_dev->function_config;
 
 
 	if (!(s->state & SOCKET_PRESENT)) {
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
 		dev_dbg(&s->dev, "No card present\n");
+		mutex_unlock(&s->ops_mutex);
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 	if (!(c->state & CONFIG_LOCKED)) {
 	if (!(c->state & CONFIG_LOCKED)) {
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
 		dev_dbg(&s->dev, "Configuration isnt't locked\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EACCES;
 		return -EACCES;
 	}
 	}
 
 
@@ -251,10 +294,12 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	    (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 	    (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 		if (mod->Vpp1 != mod->Vpp2) {
 		if (mod->Vpp1 != mod->Vpp2) {
 			dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n");
 			dev_dbg(&s->dev, "Vpp1 and Vpp2 must be the same\n");
+			mutex_unlock(&s->ops_mutex);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 		s->socket.Vpp = mod->Vpp1;
 		s->socket.Vpp = mod->Vpp1;
 		if (s->ops->set_socket(s, &s->socket)) {
 		if (s->ops->set_socket(s, &s->socket)) {
+			mutex_unlock(&s->ops_mutex);
 			dev_printk(KERN_WARNING, &s->dev,
 			dev_printk(KERN_WARNING, &s->dev,
 				   "Unable to set VPP\n");
 				   "Unable to set VPP\n");
 			return -EIO;
 			return -EIO;
@@ -262,6 +307,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 	} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
 	} else if ((mod->Attributes & CONF_VPP1_CHANGE_VALID) ||
 		   (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 		   (mod->Attributes & CONF_VPP2_CHANGE_VALID)) {
 		dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
 		dev_dbg(&s->dev, "changing Vcc is not allowed at this time\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -286,6 +332,7 @@ int pcmcia_modify_configuration(struct pcmcia_device *p_dev,
 			s->ops->set_io_map(s, &io_on);
 			s->ops->set_io_map(s, &io_on);
 		}
 		}
 	}
 	}
+	mutex_unlock(&s->ops_mutex);
 
 
 	return 0;
 	return 0;
 } /* modify_configuration */
 } /* modify_configuration */
@@ -296,9 +343,11 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 {
 {
 	pccard_io_map io = { 0, 0, 0, 0, 1 };
 	pccard_io_map io = { 0, 0, 0, 0, 1 };
 	struct pcmcia_socket *s = p_dev->socket;
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	config_t *c;
 	int i;
 	int i;
 
 
+	mutex_lock(&s->ops_mutex);
+	c = p_dev->function_config;
 	if (p_dev->_locked) {
 	if (p_dev->_locked) {
 		p_dev->_locked = 0;
 		p_dev->_locked = 0;
 		if (--(s->lock_count) == 0) {
 		if (--(s->lock_count) == 0) {
@@ -321,6 +370,7 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 				s->ops->set_io_map(s, &io);
 				s->ops->set_io_map(s, &io);
 			}
 			}
 	}
 	}
+	mutex_unlock(&s->ops_mutex);
 
 
 	return 0;
 	return 0;
 } /* pcmcia_release_configuration */
 } /* pcmcia_release_configuration */
@@ -337,10 +387,14 @@ int pcmcia_release_configuration(struct pcmcia_device *p_dev)
 static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 {
 {
 	struct pcmcia_socket *s = p_dev->socket;
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	int ret = -EINVAL;
+	config_t *c;
+
+	mutex_lock(&s->ops_mutex);
+	c = p_dev->function_config;
 
 
 	if (!p_dev->_io)
 	if (!p_dev->_io)
-		return -EINVAL;
+		goto out;
 
 
 	p_dev->_io = 0;
 	p_dev->_io = 0;
 
 
@@ -348,7 +402,7 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 	    (c->io.NumPorts1 != req->NumPorts1) ||
 	    (c->io.NumPorts1 != req->NumPorts1) ||
 	    (c->io.BasePort2 != req->BasePort2) ||
 	    (c->io.BasePort2 != req->BasePort2) ||
 	    (c->io.NumPorts2 != req->NumPorts2))
 	    (c->io.NumPorts2 != req->NumPorts2))
-		return -EINVAL;
+		goto out;
 
 
 	c->state &= ~CONFIG_IO_REQ;
 	c->state &= ~CONFIG_IO_REQ;
 
 
@@ -356,28 +410,38 @@ static int pcmcia_release_io(struct pcmcia_device *p_dev, io_req_t *req)
 	if (req->NumPorts2)
 	if (req->NumPorts2)
 		release_io_space(s, req->BasePort2, req->NumPorts2);
 		release_io_space(s, req->BasePort2, req->NumPorts2);
 
 
-	return 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_release_io */
 } /* pcmcia_release_io */
 
 
 
 
 static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 {
 {
 	struct pcmcia_socket *s = p_dev->socket;
 	struct pcmcia_socket *s = p_dev->socket;
-	config_t *c = p_dev->function_config;
+	config_t *c;
+	int ret = -EINVAL;
+
+	mutex_lock(&s->ops_mutex);
+
+	c = p_dev->function_config;
 
 
 	if (!p_dev->_irq)
 	if (!p_dev->_irq)
-		return -EINVAL;
+		goto out;
+
 	p_dev->_irq = 0;
 	p_dev->_irq = 0;
 
 
 	if (c->state & CONFIG_LOCKED)
 	if (c->state & CONFIG_LOCKED)
-		return -EACCES;
+		goto out;
+
 	if (c->irq.Attributes != req->Attributes) {
 	if (c->irq.Attributes != req->Attributes) {
 		dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
 		dev_dbg(&s->dev, "IRQ attributes must match assigned ones\n");
-		return -EINVAL;
+		goto out;
 	}
 	}
 	if (s->irq.AssignedIRQ != req->AssignedIRQ) {
 	if (s->irq.AssignedIRQ != req->AssignedIRQ) {
 		dev_dbg(&s->dev, "IRQ must match assigned one\n");
 		dev_dbg(&s->dev, "IRQ must match assigned one\n");
-		return -EINVAL;
+		goto out;
 	}
 	}
 	if (--s->irq.Config == 0) {
 	if (--s->irq.Config == 0) {
 		c->state &= ~CONFIG_IRQ_REQ;
 		c->state &= ~CONFIG_IRQ_REQ;
@@ -390,8 +454,12 @@ static int pcmcia_release_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 #ifdef CONFIG_PCMCIA_PROBE
 #ifdef CONFIG_PCMCIA_PROBE
 	pcmcia_used_irq[req->AssignedIRQ]--;
 	pcmcia_used_irq[req->AssignedIRQ]--;
 #endif
 #endif
+	ret = 0;
 
 
-	return 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_release_irq */
 } /* pcmcia_release_irq */
 
 
 
 
@@ -404,10 +472,12 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
 	if (wh >= MAX_WIN)
 	if (wh >= MAX_WIN)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	mutex_lock(&s->ops_mutex);
 	win = &s->win[wh];
 	win = &s->win[wh];
 
 
 	if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) {
 	if (!(p_dev->_win & CLIENT_WIN_REQ(wh))) {
 		dev_dbg(&s->dev, "not releasing unknown window\n");
 		dev_dbg(&s->dev, "not releasing unknown window\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -423,6 +493,7 @@ int pcmcia_release_window(struct pcmcia_device *p_dev, window_handle_t wh)
 		win->res = NULL;
 		win->res = NULL;
 	}
 	}
 	p_dev->_win &= ~CLIENT_WIN_REQ(wh);
 	p_dev->_win &= ~CLIENT_WIN_REQ(wh);
+	mutex_unlock(&s->ops_mutex);
 
 
 	return 0;
 	return 0;
 } /* pcmcia_release_window */
 } /* pcmcia_release_window */
@@ -445,8 +516,11 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 		dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n");
 		dev_dbg(&s->dev, "IntType may not be INT_CARDBUS\n");
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
+
+	mutex_lock(&s->ops_mutex);
 	c = p_dev->function_config;
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
 	if (c->state & CONFIG_LOCKED) {
+		mutex_unlock(&s->ops_mutex);
 		dev_dbg(&s->dev, "Configuration is locked\n");
 		dev_dbg(&s->dev, "Configuration is locked\n");
 		return -EACCES;
 		return -EACCES;
 	}
 	}
@@ -454,6 +528,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 	/* Do power control.  We don't allow changes in Vcc. */
 	/* Do power control.  We don't allow changes in Vcc. */
 	s->socket.Vpp = req->Vpp;
 	s->socket.Vpp = req->Vpp;
 	if (s->ops->set_socket(s, &s->socket)) {
 	if (s->ops->set_socket(s, &s->socket)) {
+		mutex_unlock(&s->ops_mutex);
 		dev_printk(KERN_WARNING, &s->dev,
 		dev_printk(KERN_WARNING, &s->dev,
 			   "Unable to set socket state\n");
 			   "Unable to set socket state\n");
 		return -EINVAL;
 		return -EINVAL;
@@ -476,6 +551,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 		s->socket.io_irq = 0;
 		s->socket.io_irq = 0;
 	s->ops->set_socket(s, &s->socket);
 	s->ops->set_socket(s, &s->socket);
 	s->lock_count++;
 	s->lock_count++;
+	mutex_unlock(&s->ops_mutex);
 
 
 	/* Set up CIS configuration registers */
 	/* Set up CIS configuration registers */
 	base = c->ConfigBase = req->ConfigBase;
 	base = c->ConfigBase = req->ConfigBase;
@@ -524,6 +600,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 
 
 	/* Configure I/O windows */
 	/* Configure I/O windows */
 	if (c->state & CONFIG_IO_REQ) {
 	if (c->state & CONFIG_IO_REQ) {
+		mutex_lock(&s->ops_mutex);
 		iomap.speed = io_speed;
 		iomap.speed = io_speed;
 		for (i = 0; i < MAX_IO_WIN; i++)
 		for (i = 0; i < MAX_IO_WIN; i++)
 			if (s->io[i].res) {
 			if (s->io[i].res) {
@@ -542,6 +619,7 @@ int pcmcia_request_configuration(struct pcmcia_device *p_dev,
 				s->ops->set_io_map(s, &iomap);
 				s->ops->set_io_map(s, &iomap);
 				s->io[i].Config++;
 				s->io[i].Config++;
 			}
 			}
+		mutex_unlock(&s->ops_mutex);
 	}
 	}
 
 
 	c->state |= CONFIG_LOCKED;
 	c->state |= CONFIG_LOCKED;
@@ -560,54 +638,65 @@ int pcmcia_request_io(struct pcmcia_device *p_dev, io_req_t *req)
 {
 {
 	struct pcmcia_socket *s = p_dev->socket;
 	struct pcmcia_socket *s = p_dev->socket;
 	config_t *c;
 	config_t *c;
+	int ret = -EINVAL;
+
+	mutex_lock(&s->ops_mutex);
 
 
 	if (!(s->state & SOCKET_PRESENT)) {
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
 		dev_dbg(&s->dev, "No card present\n");
-		return -ENODEV;
+		goto out;
 	}
 	}
 
 
 	if (!req)
 	if (!req)
-		return -EINVAL;
+		goto out;
+
 	c = p_dev->function_config;
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
 	if (c->state & CONFIG_LOCKED) {
 		dev_dbg(&s->dev, "Configuration is locked\n");
 		dev_dbg(&s->dev, "Configuration is locked\n");
-		return -EACCES;
+		goto out;
 	}
 	}
 	if (c->state & CONFIG_IO_REQ) {
 	if (c->state & CONFIG_IO_REQ) {
 		dev_dbg(&s->dev, "IO already configured\n");
 		dev_dbg(&s->dev, "IO already configured\n");
-		return -EBUSY;
+		goto out;
 	}
 	}
 	if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
 	if (req->Attributes1 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS)) {
 		dev_dbg(&s->dev, "bad attribute setting for IO region 1\n");
 		dev_dbg(&s->dev, "bad attribute setting for IO region 1\n");
-		return -EINVAL;
+		goto out;
 	}
 	}
 	if ((req->NumPorts2 > 0) &&
 	if ((req->NumPorts2 > 0) &&
 	    (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
 	    (req->Attributes2 & (IO_SHARED | IO_FORCE_ALIAS_ACCESS))) {
 		dev_dbg(&s->dev, "bad attribute setting for IO region 2\n");
 		dev_dbg(&s->dev, "bad attribute setting for IO region 2\n");
-		return -EINVAL;
+		goto out;
 	}
 	}
 
 
 	dev_dbg(&s->dev, "trying to allocate resource 1\n");
 	dev_dbg(&s->dev, "trying to allocate resource 1\n");
-	if (alloc_io_space(s, req->Attributes1, &req->BasePort1,
-			   req->NumPorts1, req->IOAddrLines)) {
+	ret = alloc_io_space(s, req->Attributes1, &req->BasePort1,
+			     req->NumPorts1, req->IOAddrLines);
+	if (ret) {
 		dev_dbg(&s->dev, "allocation of resource 1 failed\n");
 		dev_dbg(&s->dev, "allocation of resource 1 failed\n");
-		return -EBUSY;
+		goto out;
 	}
 	}
 
 
 	if (req->NumPorts2) {
 	if (req->NumPorts2) {
 		dev_dbg(&s->dev, "trying to allocate resource 2\n");
 		dev_dbg(&s->dev, "trying to allocate resource 2\n");
-		if (alloc_io_space(s, req->Attributes2, &req->BasePort2,
-				   req->NumPorts2, req->IOAddrLines)) {
+		ret = alloc_io_space(s, req->Attributes2, &req->BasePort2,
+				     req->NumPorts2, req->IOAddrLines);
+		if (ret) {
 			dev_dbg(&s->dev, "allocation of resource 2 failed\n");
 			dev_dbg(&s->dev, "allocation of resource 2 failed\n");
 			release_io_space(s, req->BasePort1, req->NumPorts1);
 			release_io_space(s, req->BasePort1, req->NumPorts1);
-			return -EBUSY;
+			goto out;
 		}
 		}
 	}
 	}
 
 
 	c->io = *req;
 	c->io = *req;
 	c->state |= CONFIG_IO_REQ;
 	c->state |= CONFIG_IO_REQ;
 	p_dev->_io = 1;
 	p_dev->_io = 1;
-	return 0;
+	dev_dbg(&s->dev, "allocating resources succeeded: %d\n", ret);
+
+out:
+	mutex_unlock(&s->ops_mutex);
+
+	return ret;
 } /* pcmcia_request_io */
 } /* pcmcia_request_io */
 EXPORT_SYMBOL(pcmcia_request_io);
 EXPORT_SYMBOL(pcmcia_request_io);
 
 
@@ -636,18 +725,20 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	int ret = -EINVAL, irq = 0;
 	int ret = -EINVAL, irq = 0;
 	int type;
 	int type;
 
 
+	mutex_lock(&s->ops_mutex);
+
 	if (!(s->state & SOCKET_PRESENT)) {
 	if (!(s->state & SOCKET_PRESENT)) {
 		dev_dbg(&s->dev, "No card present\n");
 		dev_dbg(&s->dev, "No card present\n");
-		return -ENODEV;
+		goto out;
 	}
 	}
 	c = p_dev->function_config;
 	c = p_dev->function_config;
 	if (c->state & CONFIG_LOCKED) {
 	if (c->state & CONFIG_LOCKED) {
 		dev_dbg(&s->dev, "Configuration is locked\n");
 		dev_dbg(&s->dev, "Configuration is locked\n");
-		return -EACCES;
+		goto out;
 	}
 	}
 	if (c->state & CONFIG_IRQ_REQ) {
 	if (c->state & CONFIG_IRQ_REQ) {
 		dev_dbg(&s->dev, "IRQ already configured\n");
 		dev_dbg(&s->dev, "IRQ already configured\n");
-		return -EBUSY;
+		goto out;
 	}
 	}
 
 
 	/* Decide what type of interrupt we are registering */
 	/* Decide what type of interrupt we are registering */
@@ -708,7 +799,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	if (ret && !s->irq.AssignedIRQ) {
 	if (ret && !s->irq.AssignedIRQ) {
 		if (!s->pci_irq) {
 		if (!s->pci_irq) {
 			dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
 			dev_printk(KERN_INFO, &s->dev, "no IRQ found\n");
-			return ret;
+			goto out;
 		}
 		}
 		type = IRQF_SHARED;
 		type = IRQF_SHARED;
 		irq = s->pci_irq;
 		irq = s->pci_irq;
@@ -720,7 +811,7 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 		if (ret) {
 		if (ret) {
 			dev_printk(KERN_INFO, &s->dev,
 			dev_printk(KERN_INFO, &s->dev,
 				"request_irq() failed\n");
 				"request_irq() failed\n");
-			return ret;
+			goto out;
 		}
 		}
 	}
 	}
 
 
@@ -743,7 +834,10 @@ int pcmcia_request_irq(struct pcmcia_device *p_dev, irq_req_t *req)
 	pcmcia_used_irq[irq]++;
 	pcmcia_used_irq[irq]++;
 #endif
 #endif
 
 
-	return 0;
+	ret = 0;
+out:
+	mutex_unlock(&s->ops_mutex);
+	return ret;
 } /* pcmcia_request_irq */
 } /* pcmcia_request_irq */
 EXPORT_SYMBOL(pcmcia_request_irq);
 EXPORT_SYMBOL(pcmcia_request_irq);
 
 
@@ -796,6 +890,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
+	mutex_lock(&s->ops_mutex);
 	win = &s->win[w];
 	win = &s->win[w];
 
 
 	if (!(s->features & SS_CAP_STATIC_MAP)) {
 	if (!(s->features & SS_CAP_STATIC_MAP)) {
@@ -803,6 +898,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 						      (req->Attributes & WIN_MAP_BELOW_1MB), s);
 						      (req->Attributes & WIN_MAP_BELOW_1MB), s);
 		if (!win->res) {
 		if (!win->res) {
 			dev_dbg(&s->dev, "allocating mem region failed\n");
 			dev_dbg(&s->dev, "allocating mem region failed\n");
+			mutex_unlock(&s->ops_mutex);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 	}
 	}
@@ -821,8 +917,10 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 	if (req->Attributes & WIN_USE_WAIT)
 	if (req->Attributes & WIN_USE_WAIT)
 		win->flags |= MAP_USE_WAIT;
 		win->flags |= MAP_USE_WAIT;
 	win->card_start = 0;
 	win->card_start = 0;
+
 	if (s->ops->set_mem_map(s, win) != 0) {
 	if (s->ops->set_mem_map(s, win) != 0) {
 		dev_dbg(&s->dev, "failed to set memory mapping\n");
 		dev_dbg(&s->dev, "failed to set memory mapping\n");
+		mutex_unlock(&s->ops_mutex);
 		return -EIO;
 		return -EIO;
 	}
 	}
 	s->state |= SOCKET_WIN_REQ(w);
 	s->state |= SOCKET_WIN_REQ(w);
@@ -833,6 +931,7 @@ int pcmcia_request_window(struct pcmcia_device *p_dev, win_req_t *req, window_ha
 	else
 	else
 		req->Base = win->res->start;
 		req->Base = win->res->start;
 
 
+	mutex_unlock(&s->ops_mutex);
 	*wh = w + 1;
 	*wh = w + 1;
 
 
 	return 0;
 	return 0;

+ 0 - 48
drivers/pcmcia/rsrc_mgr.c

@@ -21,60 +21,12 @@
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cistpl.h>
 #include "cs_internal.h"
 #include "cs_internal.h"
 
 
-
-int pcmcia_validate_mem(struct pcmcia_socket *s)
-{
-	if (s->resource_ops->validate_mem)
-		return s->resource_ops->validate_mem(s);
-	/* if there is no callback, we can assume that everything is OK */
-	return 0;
-}
-EXPORT_SYMBOL(pcmcia_validate_mem);
-
-int pcmcia_adjust_io_region(struct resource *res, unsigned long r_start,
-		     unsigned long r_end, struct pcmcia_socket *s)
-{
-	if (s->resource_ops->adjust_io_region)
-		return s->resource_ops->adjust_io_region(res, r_start, r_end, s);
-	return -ENOMEM;
-}
-EXPORT_SYMBOL(pcmcia_adjust_io_region);
-
-struct resource *pcmcia_find_io_region(unsigned long base, int num,
-		   unsigned long align, struct pcmcia_socket *s)
-{
-	if (s->resource_ops->find_io)
-		return s->resource_ops->find_io(base, num, align, s);
-	return NULL;
-}
-EXPORT_SYMBOL(pcmcia_find_io_region);
-
-struct resource *pcmcia_find_mem_region(u_long base, u_long num, u_long align,
-				 int low, struct pcmcia_socket *s)
-{
-	if (s->resource_ops->find_mem)
-		return s->resource_ops->find_mem(base, num, align, low, s);
-	return NULL;
-}
-EXPORT_SYMBOL(pcmcia_find_mem_region);
-
-void release_resource_db(struct pcmcia_socket *s)
-{
-	if (s->resource_ops->exit)
-		s->resource_ops->exit(s);
-}
-
-
 static int static_init(struct pcmcia_socket *s)
 static int static_init(struct pcmcia_socket *s)
 {
 {
-	unsigned long flags;
-
 	/* the good thing about SS_CAP_STATIC_MAP sockets is
 	/* the good thing about SS_CAP_STATIC_MAP sockets is
 	 * that they don't need a resource database */
 	 * that they don't need a resource database */
 
 
-	spin_lock_irqsave(&s->lock, flags);
 	s->resource_setup_done = 1;
 	s->resource_setup_done = 1;
-	spin_unlock_irqrestore(&s->lock, flags);
 
 
 	return 0;
 	return 0;
 }
 }

+ 179 - 106
drivers/pcmcia/rsrc_nonstatic.c

@@ -55,11 +55,10 @@ struct resource_map {
 
 
 struct socket_data {
 struct socket_data {
 	struct resource_map		mem_db;
 	struct resource_map		mem_db;
+	struct resource_map		mem_db_valid;
 	struct resource_map		io_db;
 	struct resource_map		io_db;
-	unsigned int			rsrc_mem_probe;
 };
 };
 
 
-static DEFINE_MUTEX(rsrc_mutex);
 #define MEM_PROBE_LOW	(1 << 0)
 #define MEM_PROBE_LOW	(1 << 0)
 #define MEM_PROBE_HIGH	(1 << 1)
 #define MEM_PROBE_HIGH	(1 << 1)
 
 
@@ -125,8 +124,10 @@ static int add_interval(struct resource_map *map, u_long base, u_long num)
 	struct resource_map *p, *q;
 	struct resource_map *p, *q;
 
 
 	for (p = map; ; p = p->next) {
 	for (p = map; ; p = p->next) {
-		if ((p != map) && (p->base+p->num-1 >= base))
-			return -1;
+		if ((p != map) && (p->base+p->num >= base)) {
+			p->num = max(num + base - p->base, p->num);
+			return 0;
+		}
 		if ((p->next == map) || (p->next->base > base+num-1))
 		if ((p->next == map) || (p->next->base > base+num-1))
 			break;
 			break;
 	}
 	}
@@ -264,36 +265,44 @@ static void do_io_probe(struct pcmcia_socket *s, unsigned int base,
 }
 }
 #endif
 #endif
 
 
-/*======================================================================
-
-    This is tricky... when we set up CIS memory, we try to validate
-    the memory window space allocations.
-
-======================================================================*/
+/*======================================================================*/
 
 
-/* Validation function for cards with a valid CIS */
+/**
+ * readable() - iomem validation function for cards with a valid CIS
+ */
 static int readable(struct pcmcia_socket *s, struct resource *res,
 static int readable(struct pcmcia_socket *s, struct resource *res,
 		    unsigned int *count)
 		    unsigned int *count)
 {
 {
-	int ret = -1;
+	int ret = -EINVAL;
+
+	if (s->fake_cis) {
+		dev_dbg(&s->dev, "fake CIS is being used: can't validate mem\n");
+		return 0;
+	}
 
 
 	s->cis_mem.res = res;
 	s->cis_mem.res = res;
 	s->cis_virt = ioremap(res->start, s->map_size);
 	s->cis_virt = ioremap(res->start, s->map_size);
 	if (s->cis_virt) {
 	if (s->cis_virt) {
-		ret = pccard_validate_cis(s, count);
-		/* invalidate mapping and CIS cache */
+		mutex_unlock(&s->ops_mutex);
+		/* as we're only called from pcmcia.c, we're safe */
+		if (s->callback->validate)
+			ret = s->callback->validate(s, count);
+		/* invalidate mapping */
+		mutex_lock(&s->ops_mutex);
 		iounmap(s->cis_virt);
 		iounmap(s->cis_virt);
 		s->cis_virt = NULL;
 		s->cis_virt = NULL;
-		destroy_cis_cache(s);
 	}
 	}
 	s->cis_mem.res = NULL;
 	s->cis_mem.res = NULL;
-	if ((ret != 0) || (*count == 0))
-		return 0;
-	return 1;
+	if ((ret) || (*count == 0))
+		return -EINVAL;
+	return 0;
 }
 }
 
 
-/* Validation function for simple memory cards */
-static int checksum(struct pcmcia_socket *s, struct resource *res)
+/**
+ * checksum() - iomem validation function for simple memory cards
+ */
+static int checksum(struct pcmcia_socket *s, struct resource *res,
+		    unsigned int *value)
 {
 {
 	pccard_mem_map map;
 	pccard_mem_map map;
 	int i, a = 0, b = -1, d;
 	int i, a = 0, b = -1, d;
@@ -321,61 +330,90 @@ static int checksum(struct pcmcia_socket *s, struct resource *res)
 		iounmap(virt);
 		iounmap(virt);
 	}
 	}
 
 
-	return (b == -1) ? -1 : (a>>1);
+	if (b == -1)
+		return -EINVAL;
+
+	*value = a;
+
+	return 0;
 }
 }
 
 
-static int
-cis_readable(struct pcmcia_socket *s, unsigned long base, unsigned long size)
+/**
+ * do_validate_mem() - low level validate a memory region for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ * @base:	start address of resource to check
+ * @size:	size of resource to check
+ * @validate:	validation function to use
+ *
+ * do_validate_mem() splits up the memory region which is to be checked
+ * into two parts. Both are passed to the @validate() function. If
+ * @validate() returns non-zero, or the value parameter to @validate()
+ * is zero, or the value parameter is different between both calls,
+ * the check fails, and -EINVAL is returned. Else, 0 is returned.
+ */
+static int do_validate_mem(struct pcmcia_socket *s,
+			   unsigned long base, unsigned long size,
+			   int validate (struct pcmcia_socket *s,
+					 struct resource *res,
+					 unsigned int *value))
 {
 {
+	struct socket_data *s_data = s->resource_data;
 	struct resource *res1, *res2;
 	struct resource *res1, *res2;
-	unsigned int info1, info2;
-	int ret = 0;
+	unsigned int info1 = 1, info2 = 1;
+	int ret = -EINVAL;
 
 
 	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
 	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
 	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
 	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
 			"PCMCIA memprobe");
 			"PCMCIA memprobe");
 
 
 	if (res1 && res2) {
 	if (res1 && res2) {
-		ret = readable(s, res1, &info1);
-		ret += readable(s, res2, &info2);
+		ret = 0;
+		if (validate) {
+			ret = validate(s, res1, &info1);
+			ret += validate(s, res2, &info2);
+		}
 	}
 	}
 
 
 	free_region(res2);
 	free_region(res2);
 	free_region(res1);
 	free_region(res1);
 
 
-	return (ret == 2) && (info1 == info2);
-}
+	dev_dbg(&s->dev, "cs: memory probe 0x%06lx-0x%06lx: %p %p %u %u %u",
+		base, base+size-1, res1, res2, ret, info1, info2);
 
 
-static int
-checksum_match(struct pcmcia_socket *s, unsigned long base, unsigned long size)
-{
-	struct resource *res1, *res2;
-	int a = -1, b = -1;
-
-	res1 = claim_region(s, base, size/2, IORESOURCE_MEM, "PCMCIA memprobe");
-	res2 = claim_region(s, base + size/2, size/2, IORESOURCE_MEM,
-			"PCMCIA memprobe");
+	if ((ret) || (info1 != info2) || (info1 == 0))
+		return -EINVAL;
 
 
-	if (res1 && res2) {
-		a = checksum(s, res1);
-		b = checksum(s, res2);
+	if (validate && !s->fake_cis) {
+		/* move it to the validated data set */
+		add_interval(&s_data->mem_db_valid, base, size);
+		sub_interval(&s_data->mem_db, base, size);
 	}
 	}
 
 
-	free_region(res2);
-	free_region(res1);
-
-	return (a == b) && (a >= 0);
+	return 0;
 }
 }
 
 
-/*======================================================================
-
-    The memory probe.  If the memory list includes a 64K-aligned block
-    below 1MB, we probe in 64K chunks, and as soon as we accumulate at
-    least mem_limit free space, we quit.
-
-======================================================================*/
 
 
-static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
+/**
+ * do_mem_probe() - validate a memory region for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ * @base:	start address of resource to check
+ * @num:	size of resource to check
+ * @validate:	validation function to use
+ * @fallback:	validation function to use if validate fails
+ *
+ * do_mem_probe() checks a memory region for use by the PCMCIA subsystem.
+ * To do so, the area is split up into sensible parts, and then passed
+ * into the @validate() function. Only if @validate() and @fallback() fail,
+ * the area is marked as unavaibale for use by the PCMCIA subsystem. The
+ * function returns the size of the usable memory area.
+ */
+static int do_mem_probe(struct pcmcia_socket *s, u_long base, u_long num,
+			int validate (struct pcmcia_socket *s,
+				      struct resource *res,
+				      unsigned int *value),
+			int fallback (struct pcmcia_socket *s,
+				      struct resource *res,
+				      unsigned int *value))
 {
 {
 	struct socket_data *s_data = s->resource_data;
 	struct socket_data *s_data = s->resource_data;
 	u_long i, j, bad, fail, step;
 	u_long i, j, bad, fail, step;
@@ -393,15 +431,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
 	for (i = j = base; i < base+num; i = j + step) {
 	for (i = j = base; i < base+num; i = j + step) {
 		if (!fail) {
 		if (!fail) {
 			for (j = i; j < base+num; j += step) {
 			for (j = i; j < base+num; j += step) {
-				if (cis_readable(s, j, step))
+				if (!do_validate_mem(s, j, step, validate))
 					break;
 					break;
 			}
 			}
 			fail = ((i == base) && (j == base+num));
 			fail = ((i == base) && (j == base+num));
 		}
 		}
-		if (fail) {
-			for (j = i; j < base+num; j += 2*step)
-				if (checksum_match(s, j, step) &&
-					checksum_match(s, j + step, step))
+		if ((fail) && (fallback)) {
+			for (j = i; j < base+num; j += step)
+				if (!do_validate_mem(s, j, step, fallback))
 					break;
 					break;
 		}
 		}
 		if (i != j) {
 		if (i != j) {
@@ -416,8 +453,14 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s)
 	return num - bad;
 	return num - bad;
 }
 }
 
 
+
 #ifdef CONFIG_PCMCIA_PROBE
 #ifdef CONFIG_PCMCIA_PROBE
 
 
+/**
+ * inv_probe() - top-to-bottom search for one usuable high memory area
+ * @s:		PCMCIA socket to validate
+ * @m:		resource_map to check
+ */
 static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
 static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
 {
 {
 	struct socket_data *s_data = s->resource_data;
 	struct socket_data *s_data = s->resource_data;
@@ -432,9 +475,18 @@ static u_long inv_probe(struct resource_map *m, struct pcmcia_socket *s)
 	}
 	}
 	if (m->base < 0x100000)
 	if (m->base < 0x100000)
 		return 0;
 		return 0;
-	return do_mem_probe(m->base, m->num, s);
+	return do_mem_probe(s, m->base, m->num, readable, checksum);
 }
 }
 
 
+/**
+ * validate_mem() - memory probe function
+ * @s:		PCMCIA socket to validate
+ * @probe_mask: MEM_PROBE_LOW | MEM_PROBE_HIGH
+ *
+ * The memory probe.  If the memory list includes a 64K-aligned block
+ * below 1MB, we probe in 64K chunks, and as soon as we accumulate at
+ * least mem_limit free space, we quit. Returns 0 on usuable ports.
+ */
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
 {
 	struct resource_map *m, mm;
 	struct resource_map *m, mm;
@@ -446,6 +498,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 	if (probe_mask & MEM_PROBE_HIGH) {
 	if (probe_mask & MEM_PROBE_HIGH) {
 		if (inv_probe(s_data->mem_db.next, s) > 0)
 		if (inv_probe(s_data->mem_db.next, s) > 0)
 			return 0;
 			return 0;
+		if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+			return 0;
 		dev_printk(KERN_NOTICE, &s->dev,
 		dev_printk(KERN_NOTICE, &s->dev,
 			   "cs: warning: no high memory space available!\n");
 			   "cs: warning: no high memory space available!\n");
 		return -ENODEV;
 		return -ENODEV;
@@ -457,7 +511,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 		if (mm.base >= 0x100000)
 		if (mm.base >= 0x100000)
 			continue;
 			continue;
 		if ((mm.base | mm.num) & 0xffff) {
 		if ((mm.base | mm.num) & 0xffff) {
-			ok += do_mem_probe(mm.base, mm.num, s);
+			ok += do_mem_probe(s, mm.base, mm.num, readable,
+					   checksum);
 			continue;
 			continue;
 		}
 		}
 		/* Special probe for 64K-aligned block */
 		/* Special probe for 64K-aligned block */
@@ -467,7 +522,8 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 				if (ok >= mem_limit)
 				if (ok >= mem_limit)
 					sub_interval(&s_data->mem_db, b, 0x10000);
 					sub_interval(&s_data->mem_db, b, 0x10000);
 				else
 				else
-					ok += do_mem_probe(b, 0x10000, s);
+					ok += do_mem_probe(s, b, 0x10000,
+							   readable, checksum);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -480,6 +536,13 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 
 
 #else /* CONFIG_PCMCIA_PROBE */
 #else /* CONFIG_PCMCIA_PROBE */
 
 
+/**
+ * validate_mem() - memory probe function
+ * @s:		PCMCIA socket to validate
+ * @probe_mask: ignored
+ *
+ * Returns 0 on usuable ports.
+ */
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 {
 {
 	struct resource_map *m, mm;
 	struct resource_map *m, mm;
@@ -488,7 +551,7 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 
 
 	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
 	for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) {
 		mm = *m;
 		mm = *m;
-		ok += do_mem_probe(mm.base, mm.num, s);
+		ok += do_mem_probe(s, mm.base, mm.num, readable, checksum);
 	}
 	}
 	if (ok > 0)
 	if (ok > 0)
 		return 0;
 		return 0;
@@ -498,31 +561,31 @@ static int validate_mem(struct pcmcia_socket *s, unsigned int probe_mask)
 #endif /* CONFIG_PCMCIA_PROBE */
 #endif /* CONFIG_PCMCIA_PROBE */
 
 
 
 
-/*
+/**
+ * pcmcia_nonstatic_validate_mem() - try to validate iomem for PCMCIA use
+ * @s:		PCMCIA socket to validate
+ *
+ * This is tricky... when we set up CIS memory, we try to validate
+ * the memory window space allocations.
+ *
  * Locking note: Must be called with skt_mutex held!
  * Locking note: Must be called with skt_mutex held!
  */
  */
 static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
 static int pcmcia_nonstatic_validate_mem(struct pcmcia_socket *s)
 {
 {
 	struct socket_data *s_data = s->resource_data;
 	struct socket_data *s_data = s->resource_data;
 	unsigned int probe_mask = MEM_PROBE_LOW;
 	unsigned int probe_mask = MEM_PROBE_LOW;
-	int ret = 0;
+	int ret;
 
 
-	if (!probe_mem)
+	if (!probe_mem || !(s->state & SOCKET_PRESENT))
 		return 0;
 		return 0;
 
 
-	mutex_lock(&rsrc_mutex);
-
 	if (s->features & SS_CAP_PAGE_REGS)
 	if (s->features & SS_CAP_PAGE_REGS)
 		probe_mask = MEM_PROBE_HIGH;
 		probe_mask = MEM_PROBE_HIGH;
 
 
-	if (probe_mask & ~s_data->rsrc_mem_probe) {
-		if (s->state & SOCKET_PRESENT)
-			ret = validate_mem(s, probe_mask);
-		if (!ret)
-			s_data->rsrc_mem_probe |= probe_mask;
-	}
+	ret = validate_mem(s, probe_mask);
 
 
-	mutex_unlock(&rsrc_mutex);
+	if (s_data->mem_db_valid.next != &s_data->mem_db_valid)
+		return 0;
 
 
 	return ret;
 	return ret;
 }
 }
@@ -602,7 +665,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
 	struct socket_data *s_data = s->resource_data;
 	struct socket_data *s_data = s->resource_data;
 	int ret = -ENOMEM;
 	int ret = -ENOMEM;
 
 
-	mutex_lock(&rsrc_mutex);
 	for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
 	for (m = s_data->io_db.next; m != &s_data->io_db; m = m->next) {
 		unsigned long start = m->base;
 		unsigned long start = m->base;
 		unsigned long end = m->base + m->num - 1;
 		unsigned long end = m->base + m->num - 1;
@@ -613,7 +675,6 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star
 		ret = adjust_resource(res, r_start, r_end - r_start + 1);
 		ret = adjust_resource(res, r_start, r_end - r_start + 1);
 		break;
 		break;
 	}
 	}
-	mutex_unlock(&rsrc_mutex);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -647,7 +708,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
 	data.offset = base & data.mask;
 	data.offset = base & data.mask;
 	data.map = &s_data->io_db;
 	data.map = &s_data->io_db;
 
 
-	mutex_lock(&rsrc_mutex);
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
 	if (s->cb_dev) {
 	if (s->cb_dev) {
 		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
 		ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num, 1,
@@ -656,7 +716,6 @@ static struct resource *nonstatic_find_io_region(unsigned long base, int num,
 #endif
 #endif
 		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
 		ret = allocate_resource(&ioport_resource, res, num, min, ~0UL,
 					1, pcmcia_align, &data);
 					1, pcmcia_align, &data);
-	mutex_unlock(&rsrc_mutex);
 
 
 	if (ret != 0) {
 	if (ret != 0) {
 		kfree(res);
 		kfree(res);
@@ -672,15 +731,15 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
 	struct socket_data *s_data = s->resource_data;
 	struct socket_data *s_data = s->resource_data;
 	struct pcmcia_align_data data;
 	struct pcmcia_align_data data;
 	unsigned long min, max;
 	unsigned long min, max;
-	int ret, i;
+	int ret, i, j;
 
 
 	low = low || !(s->features & SS_CAP_PAGE_REGS);
 	low = low || !(s->features & SS_CAP_PAGE_REGS);
 
 
 	data.mask = align - 1;
 	data.mask = align - 1;
 	data.offset = base & data.mask;
 	data.offset = base & data.mask;
-	data.map = &s_data->mem_db;
 
 
 	for (i = 0; i < 2; i++) {
 	for (i = 0; i < 2; i++) {
+		data.map = &s_data->mem_db_valid;
 		if (low) {
 		if (low) {
 			max = 0x100000UL;
 			max = 0x100000UL;
 			min = base < max ? base : 0;
 			min = base < max ? base : 0;
@@ -689,17 +748,23 @@ static struct resource *nonstatic_find_mem_region(u_long base, u_long num,
 			min = 0x100000UL + base;
 			min = 0x100000UL + base;
 		}
 		}
 
 
-		mutex_lock(&rsrc_mutex);
+		for (j = 0; j < 2; j++) {
 #ifdef CONFIG_PCI
 #ifdef CONFIG_PCI
-		if (s->cb_dev) {
-			ret = pci_bus_alloc_resource(s->cb_dev->bus, res, num,
-						     1, min, 0,
-						     pcmcia_align, &data);
-		} else
+			if (s->cb_dev) {
+				ret = pci_bus_alloc_resource(s->cb_dev->bus,
+							res, num, 1, min, 0,
+							pcmcia_align, &data);
+			} else
 #endif
 #endif
-			ret = allocate_resource(&iomem_resource, res, num, min,
-						max, 1, pcmcia_align, &data);
-		mutex_unlock(&rsrc_mutex);
+			{
+				ret = allocate_resource(&iomem_resource,
+							res, num, min, max, 1,
+							pcmcia_align, &data);
+			}
+			if (ret == 0)
+				break;
+			data.map = &s_data->mem_db;
+		}
 		if (ret == 0 || low)
 		if (ret == 0 || low)
 			break;
 			break;
 		low = 1;
 		low = 1;
@@ -722,25 +787,18 @@ static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned
 	if (end < start)
 	if (end < start)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	mutex_lock(&rsrc_mutex);
 	switch (action) {
 	switch (action) {
 	case ADD_MANAGED_RESOURCE:
 	case ADD_MANAGED_RESOURCE:
 		ret = add_interval(&data->mem_db, start, size);
 		ret = add_interval(&data->mem_db, start, size);
+		if (!ret)
+			do_mem_probe(s, start, size, NULL, NULL);
 		break;
 		break;
 	case REMOVE_MANAGED_RESOURCE:
 	case REMOVE_MANAGED_RESOURCE:
 		ret = sub_interval(&data->mem_db, start, size);
 		ret = sub_interval(&data->mem_db, start, size);
-		if (!ret) {
-			struct pcmcia_socket *socket;
-			down_read(&pcmcia_socket_list_rwsem);
-			list_for_each_entry(socket, &pcmcia_socket_list, socket_list)
-				release_cis_mem(socket);
-			up_read(&pcmcia_socket_list_rwsem);
-		}
 		break;
 		break;
 	default:
 	default:
 		ret = -EINVAL;
 		ret = -EINVAL;
 	}
 	}
-	mutex_unlock(&rsrc_mutex);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -758,7 +816,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
 	if (end > IO_SPACE_LIMIT)
 	if (end > IO_SPACE_LIMIT)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	mutex_lock(&rsrc_mutex);
 	switch (action) {
 	switch (action) {
 	case ADD_MANAGED_RESOURCE:
 	case ADD_MANAGED_RESOURCE:
 		if (add_interval(&data->io_db, start, size) != 0) {
 		if (add_interval(&data->io_db, start, size) != 0) {
@@ -777,7 +834,6 @@ static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long
 		ret = -EINVAL;
 		ret = -EINVAL;
 		break;
 		break;
 	}
 	}
-	mutex_unlock(&rsrc_mutex);
 
 
 	return ret;
 	return ret;
 }
 }
@@ -860,6 +916,7 @@ static int nonstatic_init(struct pcmcia_socket *s)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	data->mem_db.next = &data->mem_db;
 	data->mem_db.next = &data->mem_db;
+	data->mem_db_valid.next = &data->mem_db_valid;
 	data->io_db.next = &data->io_db;
 	data->io_db.next = &data->io_db;
 
 
 	s->resource_data = (void *) data;
 	s->resource_data = (void *) data;
@@ -874,7 +931,10 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
 	struct socket_data *data = s->resource_data;
 	struct socket_data *data = s->resource_data;
 	struct resource_map *p, *q;
 	struct resource_map *p, *q;
 
 
-	mutex_lock(&rsrc_mutex);
+	for (p = data->mem_db_valid.next; p != &data->mem_db_valid; p = q) {
+		q = p->next;
+		kfree(p);
+	}
 	for (p = data->mem_db.next; p != &data->mem_db; p = q) {
 	for (p = data->mem_db.next; p != &data->mem_db; p = q) {
 		q = p->next;
 		q = p->next;
 		kfree(p);
 		kfree(p);
@@ -883,7 +943,6 @@ static void nonstatic_release_resource_db(struct pcmcia_socket *s)
 		q = p->next;
 		q = p->next;
 		kfree(p);
 		kfree(p);
 	}
 	}
-	mutex_unlock(&rsrc_mutex);
 }
 }
 
 
 
 
@@ -910,7 +969,7 @@ static ssize_t show_io_db(struct device *dev,
 	struct resource_map *p;
 	struct resource_map *p;
 	ssize_t ret = 0;
 	ssize_t ret = 0;
 
 
-	mutex_lock(&rsrc_mutex);
+	mutex_lock(&s->ops_mutex);
 	data = s->resource_data;
 	data = s->resource_data;
 
 
 	for (p = data->io_db.next; p != &data->io_db; p = p->next) {
 	for (p = data->io_db.next; p != &data->io_db; p = p->next) {
@@ -922,7 +981,7 @@ static ssize_t show_io_db(struct device *dev,
 				((unsigned long) p->base + p->num - 1));
 				((unsigned long) p->base + p->num - 1));
 	}
 	}
 
 
-	mutex_unlock(&rsrc_mutex);
+	mutex_unlock(&s->ops_mutex);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -950,9 +1009,11 @@ static ssize_t store_io_db(struct device *dev,
 	if (end_addr < start_addr)
 	if (end_addr < start_addr)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	mutex_lock(&s->ops_mutex);
 	ret = adjust_io(s, add, start_addr, end_addr);
 	ret = adjust_io(s, add, start_addr, end_addr);
 	if (!ret)
 	if (!ret)
 		s->resource_setup_new = 1;
 		s->resource_setup_new = 1;
+	mutex_unlock(&s->ops_mutex);
 
 
 	return ret ? ret : count;
 	return ret ? ret : count;
 }
 }
@@ -966,9 +1027,19 @@ static ssize_t show_mem_db(struct device *dev,
 	struct resource_map *p;
 	struct resource_map *p;
 	ssize_t ret = 0;
 	ssize_t ret = 0;
 
 
-	mutex_lock(&rsrc_mutex);
+	mutex_lock(&s->ops_mutex);
 	data = s->resource_data;
 	data = s->resource_data;
 
 
+	for (p = data->mem_db_valid.next; p != &data->mem_db_valid;
+	     p = p->next) {
+		if (ret > (PAGE_SIZE - 10))
+			continue;
+		ret += snprintf(&buf[ret], (PAGE_SIZE - ret - 1),
+				"0x%08lx - 0x%08lx\n",
+				((unsigned long) p->base),
+				((unsigned long) p->base + p->num - 1));
+	}
+
 	for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
 	for (p = data->mem_db.next; p != &data->mem_db; p = p->next) {
 		if (ret > (PAGE_SIZE - 10))
 		if (ret > (PAGE_SIZE - 10))
 			continue;
 			continue;
@@ -978,7 +1049,7 @@ static ssize_t show_mem_db(struct device *dev,
 				((unsigned long) p->base + p->num - 1));
 				((unsigned long) p->base + p->num - 1));
 	}
 	}
 
 
-	mutex_unlock(&rsrc_mutex);
+	mutex_unlock(&s->ops_mutex);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1006,9 +1077,11 @@ static ssize_t store_mem_db(struct device *dev,
 	if (end_addr < start_addr)
 	if (end_addr < start_addr)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	mutex_lock(&s->ops_mutex);
 	ret = adjust_memory(s, add, start_addr, end_addr);
 	ret = adjust_memory(s, add, start_addr, end_addr);
 	if (!ret)
 	if (!ret)
 		s->resource_setup_new = 1;
 		s->resource_setup_new = 1;
+	mutex_unlock(&s->ops_mutex);
 
 
 	return ret ? ret : count;
 	return ret ? ret : count;
 }
 }

+ 21 - 175
drivers/pcmcia/socket_sysfs.c

@@ -88,15 +88,14 @@ static DEVICE_ATTR(card_vcc, 0444, pccard_show_vcc, NULL);
 static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr,
 static ssize_t pccard_store_insert(struct device *dev, struct device_attribute *attr,
 				   const char *buf, size_t count)
 				   const char *buf, size_t count)
 {
 {
-	ssize_t ret;
 	struct pcmcia_socket *s = to_socket(dev);
 	struct pcmcia_socket *s = to_socket(dev);
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	ret = pcmcia_insert_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_INSERT);
 
 
-	return ret ? ret : count;
+	return count;
 }
 }
 static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
 static DEVICE_ATTR(card_insert, 0200, NULL, pccard_store_insert);
 
 
@@ -113,18 +112,22 @@ static ssize_t pccard_store_card_pm_state(struct device *dev,
 					  struct device_attribute *attr,
 					  struct device_attribute *attr,
 					  const char *buf, size_t count)
 					  const char *buf, size_t count)
 {
 {
-	ssize_t ret = -EINVAL;
 	struct pcmcia_socket *s = to_socket(dev);
 	struct pcmcia_socket *s = to_socket(dev);
+	ssize_t ret = count;
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	if (!(s->state & SOCKET_SUSPEND) && !strncmp(buf, "off", 3))
-		ret = pcmcia_suspend_card(s);
-	else if ((s->state & SOCKET_SUSPEND) && !strncmp(buf, "on", 2))
-		ret = pcmcia_resume_card(s);
+	if (!strncmp(buf, "off", 3))
+		pcmcia_parse_uevents(s, PCMCIA_UEVENT_SUSPEND);
+	else {
+		if (!strncmp(buf, "on", 2))
+			pcmcia_parse_uevents(s, PCMCIA_UEVENT_RESUME);
+		else
+			ret = -EINVAL;
+	}
 
 
-	return ret ? -ENODEV : count;
+	return ret;
 }
 }
 static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state);
 static DEVICE_ATTR(card_pm_state, 0644, pccard_show_card_pm_state, pccard_store_card_pm_state);
 
 
@@ -132,15 +135,14 @@ static ssize_t pccard_store_eject(struct device *dev,
 				  struct device_attribute *attr,
 				  struct device_attribute *attr,
 				  const char *buf, size_t count)
 				  const char *buf, size_t count)
 {
 {
-	ssize_t ret;
 	struct pcmcia_socket *s = to_socket(dev);
 	struct pcmcia_socket *s = to_socket(dev);
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	ret = pcmcia_eject_card(s);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_EJECT);
 
 
-	return ret ? ret : count;
+	return count;
 }
 }
 static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
 static DEVICE_ATTR(card_eject, 0200, NULL, pccard_store_eject);
 
 
@@ -167,7 +169,9 @@ static ssize_t pccard_store_irq_mask(struct device *dev,
 	ret = sscanf(buf, "0x%x\n", &mask);
 	ret = sscanf(buf, "0x%x\n", &mask);
 
 
 	if (ret == 1) {
 	if (ret == 1) {
+		mutex_lock(&s->ops_mutex);
 		s->irq_mask &= mask;
 		s->irq_mask &= mask;
+		mutex_unlock(&s->ops_mutex);
 		ret = 0;
 		ret = 0;
 	}
 	}
 
 
@@ -187,163 +191,21 @@ static ssize_t pccard_store_resource(struct device *dev,
 				     struct device_attribute *attr,
 				     struct device_attribute *attr,
 				     const char *buf, size_t count)
 				     const char *buf, size_t count)
 {
 {
-	unsigned long flags;
 	struct pcmcia_socket *s = to_socket(dev);
 	struct pcmcia_socket *s = to_socket(dev);
 
 
 	if (!count)
 	if (!count)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	spin_lock_irqsave(&s->lock, flags);
+	mutex_lock(&s->ops_mutex);
 	if (!s->resource_setup_done)
 	if (!s->resource_setup_done)
 		s->resource_setup_done = 1;
 		s->resource_setup_done = 1;
-	spin_unlock_irqrestore(&s->lock, flags);
-
-	mutex_lock(&s->skt_mutex);
-	if ((s->callback) &&
-	    (s->state & SOCKET_PRESENT) &&
-	    !(s->state & SOCKET_CARDBUS)) {
-		if (try_module_get(s->callback->owner)) {
-			s->callback->requery(s, 0);
-			module_put(s->callback->owner);
-		}
-	}
-	mutex_unlock(&s->skt_mutex);
-
-	return count;
-}
-static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
+	mutex_unlock(&s->ops_mutex);
 
 
-
-static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off, size_t count)
-{
-	tuple_t tuple;
-	int status, i;
-	loff_t pointer = 0;
-	ssize_t ret = 0;
-	u_char *tuplebuffer;
-	u_char *tempbuffer;
-
-	tuplebuffer = kmalloc(sizeof(u_char) * 256, GFP_KERNEL);
-	if (!tuplebuffer)
-		return -ENOMEM;
-
-	tempbuffer = kmalloc(sizeof(u_char) * 258, GFP_KERNEL);
-	if (!tempbuffer) {
-		ret = -ENOMEM;
-		goto free_tuple;
-	}
-
-	memset(&tuple, 0, sizeof(tuple_t));
-
-	tuple.Attributes = TUPLE_RETURN_LINK | TUPLE_RETURN_COMMON;
-	tuple.DesiredTuple = RETURN_FIRST_TUPLE;
-	tuple.TupleOffset = 0;
-
-	status = pccard_get_first_tuple(s, BIND_FN_ALL, &tuple);
-	while (!status) {
-		tuple.TupleData = tuplebuffer;
-		tuple.TupleDataMax = 255;
-		memset(tuplebuffer, 0, sizeof(u_char) * 255);
-
-		status = pccard_get_tuple_data(s, &tuple);
-		if (status)
-			break;
-
-		if (off < (pointer + 2 + tuple.TupleDataLen)) {
-			tempbuffer[0] = tuple.TupleCode & 0xff;
-			tempbuffer[1] = tuple.TupleLink & 0xff;
-			for (i = 0; i < tuple.TupleDataLen; i++)
-				tempbuffer[i + 2] = tuplebuffer[i] & 0xff;
-
-			for (i = 0; i < (2 + tuple.TupleDataLen); i++) {
-				if (((i + pointer) >= off) &&
-				    (i + pointer) < (off + count)) {
-					buf[ret] = tempbuffer[i];
-					ret++;
-				}
-			}
-		}
-
-		pointer += 2 + tuple.TupleDataLen;
-
-		if (pointer >= (off + count))
-			break;
-
-		if (tuple.TupleCode == CISTPL_END)
-			break;
-		status = pccard_get_next_tuple(s, BIND_FN_ALL, &tuple);
-	}
-
-	kfree(tempbuffer);
- free_tuple:
-	kfree(tuplebuffer);
-
-	return ret;
-}
-
-static ssize_t pccard_show_cis(struct kobject *kobj,
-			       struct bin_attribute *bin_attr,
-			       char *buf, loff_t off, size_t count)
-{
-	unsigned int size = 0x200;
-
-	if (off >= size)
-		count = 0;
-	else {
-		struct pcmcia_socket *s;
-		unsigned int chains;
-
-		if (off + count > size)
-			count = size - off;
-
-		s = to_socket(container_of(kobj, struct device, kobj));
-
-		if (!(s->state & SOCKET_PRESENT))
-			return -ENODEV;
-		if (pccard_validate_cis(s, &chains))
-			return -EIO;
-		if (!chains)
-			return -ENODATA;
-
-		count = pccard_extract_cis(s, buf, off, count);
-	}
-
-	return count;
-}
-
-static ssize_t pccard_store_cis(struct kobject *kobj,
-				struct bin_attribute *bin_attr,
-				char *buf, loff_t off, size_t count)
-{
-	struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj));
-	int error;
-
-	if (off)
-		return -EINVAL;
-
-	if (count >= CISTPL_MAX_CIS_SIZE)
-		return -EINVAL;
-
-	if (!(s->state & SOCKET_PRESENT))
-		return -ENODEV;
-
-	error = pcmcia_replace_cis(s, buf, count);
-	if (error)
-		return -EIO;
-
-	mutex_lock(&s->skt_mutex);
-	if ((s->callback) && (s->state & SOCKET_PRESENT) &&
-	    !(s->state & SOCKET_CARDBUS)) {
-		if (try_module_get(s->callback->owner)) {
-			s->callback->requery(s, 1);
-			module_put(s->callback->owner);
-		}
-	}
-	mutex_unlock(&s->skt_mutex);
+	pcmcia_parse_uevents(s, PCMCIA_UEVENT_REQUERY);
 
 
 	return count;
 	return count;
 }
 }
-
+static DEVICE_ATTR(available_resources_setup_done, 0600, pccard_show_resource, pccard_store_resource);
 
 
 static struct attribute *pccard_socket_attributes[] = {
 static struct attribute *pccard_socket_attributes[] = {
 	&dev_attr_card_type.attr,
 	&dev_attr_card_type.attr,
@@ -362,28 +224,12 @@ static const struct attribute_group socket_attrs = {
 	.attrs = pccard_socket_attributes,
 	.attrs = pccard_socket_attributes,
 };
 };
 
 
-static struct bin_attribute pccard_cis_attr = {
-	.attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR },
-	.size = 0x200,
-	.read = pccard_show_cis,
-	.write = pccard_store_cis,
-};
-
 int pccard_sysfs_add_socket(struct device *dev)
 int pccard_sysfs_add_socket(struct device *dev)
 {
 {
-	int ret = 0;
-
-	ret = sysfs_create_group(&dev->kobj, &socket_attrs);
-	if (!ret) {
-		ret = sysfs_create_bin_file(&dev->kobj, &pccard_cis_attr);
-		if (ret)
-			sysfs_remove_group(&dev->kobj, &socket_attrs);
-	}
-	return ret;
+	return sysfs_create_group(&dev->kobj, &socket_attrs);
 }
 }
 
 
 void pccard_sysfs_remove_socket(struct device *dev)
 void pccard_sysfs_remove_socket(struct device *dev)
 {
 {
-	sysfs_remove_bin_file(&dev->kobj, &pccard_cis_attr);
 	sysfs_remove_group(&dev->kobj, &socket_attrs);
 	sysfs_remove_group(&dev->kobj, &socket_attrs);
 }
 }

+ 5 - 0
drivers/pcmcia/yenta_socket.c

@@ -37,6 +37,11 @@ static int pwr_irqs_off;
 module_param(pwr_irqs_off, bool, 0644);
 module_param(pwr_irqs_off, bool, 0644);
 MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!");
 MODULE_PARM_DESC(pwr_irqs_off, "Force IRQs off during power-on of slot. Use only when seeing IRQ storms!");
 
 
+static char o2_speedup[] = "default";
+module_param_string(o2_speedup, o2_speedup, sizeof(o2_speedup), 0444);
+MODULE_PARM_DESC(o2_speedup, "Use prefetch/burst for O2-bridges: 'on', 'off' "
+	"or 'default' (uses recommended behaviour for the detected bridge)");
+
 #define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args)
 #define debug(x, s, args...) dev_dbg(&s->dev->dev, x, ##args)
 
 
 /* Don't ask.. */
 /* Don't ask.. */

+ 4 - 3
drivers/serial/serial_cs.c

@@ -696,11 +696,11 @@ static int serial_config(struct pcmcia_device * link)
 		info->multi = info->quirk->multi;
 		info->multi = info->quirk->multi;
 
 
 	if (info->multi > 1)
 	if (info->multi > 1)
-		multi_config(link);
+		i = multi_config(link);
 	else
 	else
-		simple_config(link);
+		i = simple_config(link);
 
 
-	if (info->ndev == 0)
+	if (i || info->ndev == 0)
 		goto failed;
 		goto failed;
 
 
 	/*
 	/*
@@ -715,6 +715,7 @@ static int serial_config(struct pcmcia_device * link)
 	return 0;
 	return 0;
 
 
 failed:
 failed:
+	dev_warn(&link->dev, "serial_cs: failed to initialize\n");
 	serial_remove(link);
 	serial_remove(link);
 	return -ENODEV;
 	return -ENODEV;
 }
 }

+ 1 - 1
include/pcmcia/ds.h

@@ -40,7 +40,7 @@ struct net_device;
  * Documentation/pcmcia/driver.txt for details.
  * Documentation/pcmcia/driver.txt for details.
 */
 */
 struct pcmcia_dynids {
 struct pcmcia_dynids {
-	spinlock_t		lock;
+	struct mutex		lock;
 	struct list_head	list;
 	struct list_head	list;
 };
 };
 
 

+ 23 - 17
include/pcmcia/ss.h

@@ -134,9 +134,9 @@ struct pccard_operations {
 
 
 struct pcmcia_socket {
 struct pcmcia_socket {
 	struct module			*owner;
 	struct module			*owner;
-	spinlock_t			lock;
 	socket_state_t			socket;
 	socket_state_t			socket;
 	u_int				state;
 	u_int				state;
+	u_int				suspended_state;	/* state before suspend */
 	u_short				functions;
 	u_short				functions;
 	u_short				lock_count;
 	u_short				lock_count;
 	pccard_mem_map			cis_mem;
 	pccard_mem_map			cis_mem;
@@ -200,9 +200,14 @@ struct pcmcia_socket {
 	struct task_struct		*thread;
 	struct task_struct		*thread;
 	struct completion		thread_done;
 	struct completion		thread_done;
 	unsigned int			thread_events;
 	unsigned int			thread_events;
-	/* protects socket h/w state */
+	unsigned int			sysfs_events;
+
+	/* For the non-trivial interaction between these locks,
+	 * see Documentation/pcmcia/locking.txt */
 	struct mutex			skt_mutex;
 	struct mutex			skt_mutex;
-	/* protects thread_events */
+	struct mutex			ops_mutex;
+
+	/* protects thread_events and sysfs_events */
 	spinlock_t			thread_lock;
 	spinlock_t			thread_lock;
 
 
 	/* pcmcia (16-bit) */
 	/* pcmcia (16-bit) */
@@ -225,30 +230,19 @@ struct pcmcia_socket {
 		u8			busy:1;
 		u8			busy:1;
 		/* pcmcia module is being unloaded */
 		/* pcmcia module is being unloaded */
 		u8			dead:1;
 		u8			dead:1;
-		/* a multifunction-device add event is pending */
-		u8			device_add_pending:1;
-		/* the pending event adds a mfc (1) or pfc (0) */
-		u8			mfc_pfc:1;
+		/* the PCMCIA card consists of two pseudo devices */
+		u8			has_pfc:1;
 
 
-		u8			reserved:3;
+		u8			reserved:4;
 	} pcmcia_state;
 	} pcmcia_state;
 
 
 
 
-	/* for adding further pseudo-multifunction devices */
-	struct work_struct		device_add;
-
 #ifdef CONFIG_PCMCIA_IOCTL
 #ifdef CONFIG_PCMCIA_IOCTL
 	struct user_info_t		*user;
 	struct user_info_t		*user;
 	wait_queue_head_t		queue;
 	wait_queue_head_t		queue;
 #endif /* CONFIG_PCMCIA_IOCTL */
 #endif /* CONFIG_PCMCIA_IOCTL */
 #endif /* CONFIG_PCMCIA */
 #endif /* CONFIG_PCMCIA */
 
 
-	/* cardbus (32-bit) */
-#ifdef CONFIG_CARDBUS
-	struct resource			*cb_cis_res;
-	void __iomem			*cb_cis_virt;
-#endif /* CONFIG_CARDBUS */
-
 	/* socket device */
 	/* socket device */
 	struct device			dev;
 	struct device			dev;
 	/* data internal to the socket driver */
 	/* data internal to the socket driver */
@@ -263,13 +257,25 @@ struct pcmcia_socket {
  * - pccard_static_ops		iomem and ioport areas are assigned statically
  * - pccard_static_ops		iomem and ioport areas are assigned statically
  * - pccard_iodyn_ops		iomem areas is assigned statically, ioport
  * - pccard_iodyn_ops		iomem areas is assigned statically, ioport
  *				areas dynamically
  *				areas dynamically
+ *				If this option is selected, use
+ *				"select PCCARD_IODYN" in Kconfig.
  * - pccard_nonstatic_ops	iomem and ioport areas are assigned dynamically.
  * - pccard_nonstatic_ops	iomem and ioport areas are assigned dynamically.
  *				If this option is selected, use
  *				If this option is selected, use
  *				"select PCCARD_NONSTATIC" in Kconfig.
  *				"select PCCARD_NONSTATIC" in Kconfig.
+ *
  */
  */
 extern struct pccard_resource_ops pccard_static_ops;
 extern struct pccard_resource_ops pccard_static_ops;
+#if defined(CONFIG_PCMCIA) || defined(CONFIG_PCMCIA_MODULE)
 extern struct pccard_resource_ops pccard_iodyn_ops;
 extern struct pccard_resource_ops pccard_iodyn_ops;
 extern struct pccard_resource_ops pccard_nonstatic_ops;
 extern struct pccard_resource_ops pccard_nonstatic_ops;
+#else
+/* If PCMCIA is not used, but only CARDBUS, these functions are not used
+ * at all. Therefore, do not use the large (240K!) rsrc_nonstatic module
+ */
+#define pccard_iodyn_ops pccard_static_ops
+#define pccard_nonstatic_ops pccard_static_ops
+#endif
+
 
 
 /* socket drivers are expected to use these callbacks in their .drv struct */
 /* socket drivers are expected to use these callbacks in their .drv struct */
 extern int pcmcia_socket_dev_suspend(struct device *dev);
 extern int pcmcia_socket_dev_suspend(struct device *dev);