patch-2.4.0-test4 linux/drivers/parport/share.c

Next file: linux/drivers/pcmcia/ds.c
Previous file: linux/drivers/parport/parport_pc.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test3/linux/drivers/parport/share.c linux/drivers/parport/share.c
@@ -88,17 +88,57 @@
 	dead_read		/* byte */
 };
 
-static void call_driver_chain(int attach, struct parport *port)
+/* Call attach(port) for each registered driver. */
+static void attach_driver_chain(struct parport *port)
 {
 	struct parport_driver *drv;
+	void (**attach) (struct parport *);
+	int count = 0, i;
+
+	/* This is complicated because attach() must be able to block,
+	 * but we can't let it do that while we're holding a
+	 * spinlock. */
 
 	spin_lock (&driverlist_lock);
-	for (drv = driver_chain; drv; drv = drv->next) {
-		if (attach)
-			drv->attach (port);
-		else
-			drv->detach (port);
+	for (drv = driver_chain; drv; drv = drv->next)
+		count++;
+	spin_unlock (&driverlist_lock);
+
+	/* Drivers can unregister here; that's okay.  If they register
+	 * they'll be given an attach during parport_register_driver,
+	 * so that's okay too.  The only worry is that someone might
+	 * get given an attach twice if they registered just before
+	 * this function gets called. */
+
+	/* Hmm, this could be fixed with a generation number..
+	 * FIXME */
+
+	attach = kmalloc (sizeof (void(*)(struct parport *)) * count,
+			  GFP_KERNEL);
+	if (!attach) {
+		printk (KERN_WARNING "parport: not enough memory to attach\n");
+		return;
 	}
+
+	spin_lock (&driverlist_lock);
+	for (i = 0, drv = driver_chain; drv && i < count; drv = drv->next)
+		attach[i] = drv->attach;
+	spin_unlock (&driverlist_lock);
+
+	for (count = 0; count < i; count++)
+		(*attach[i]) (port);
+
+	kfree (attach);
+}
+
+/* Call detach(port) for each registered driver. */
+static void detach_driver_chain(struct parport *port)
+{
+	struct parport_driver *drv;
+
+	spin_lock (&driverlist_lock);
+	for (drv = driver_chain; drv; drv = drv->next)
+		drv->detach (port);
 	spin_unlock (&driverlist_lock);
 }
 
@@ -121,28 +161,63 @@
  *	The @drv structure is allocated by the caller and must not be
  *	deallocated until after calling parport_unregister_driver().
  *
+ *	The driver's attach() function may block.  The port that
+ *	attach() is given will be valid for the duration of the
+ *	callback, but if the driver wants to take a copy of the
+ *	pointer it must call parport_get_port() to do so.  Calling
+ *	parport_register_device() on that port will do this for you.
+ *
+ *	The driver's detach() function may not block.  The port that
+ *	detach() is given will be valid for the duration of the
+ *	callback, but if the driver wants to take a copy of the
+ *	pointer it must call parport_get_port() to do so.
+ *
  *	Returns 0 on success.  Currently it always succeeds.
  **/
 
 int parport_register_driver (struct parport_driver *drv)
 {
 	struct parport *port;
-
-	spin_lock (&driverlist_lock);
-	drv->next = driver_chain;
-	driver_chain = drv;
-	spin_unlock (&driverlist_lock);
+	struct parport **ports;
+	int count = 0, i;
 
 	/* We have to take the portlist lock for this to be sure
 	 * that port is valid for the duration of the callback. */
+
+	/* This is complicated by the fact that attach must be allowed
+	 * to block, so we can't be holding any spinlocks when we call
+	 * it.  But we need to hold a spinlock to iterate over the
+	 * list of ports.. */
+
 	spin_lock (&parportlist_lock);
 	for (port = portlist; port; port = port->next)
-		drv->attach (port);
+		count++;
 	spin_unlock (&parportlist_lock);
 
+	ports = kmalloc (sizeof (struct parport *) * count, GFP_KERNEL);
+	if (!ports)
+		printk (KERN_WARNING "parport: not enough memory to attach\n");
+	else {
+		spin_lock (&parportlist_lock);
+		for (i = 0, port = portlist; port && i < count;
+		     port = port->next)
+			ports[i] = port;
+		spin_unlock (&parportlist_lock);
+
+		for (count = 0; count < i; count++)
+			drv->attach (ports[count]);
+
+		kfree (ports);
+	}
+
 	if (!portlist)
 		get_lowlevel_driver ();
 
+	spin_lock (&driverlist_lock);
+	drv->next = driver_chain;
+	driver_chain = drv;
+	spin_unlock (&driverlist_lock);
+
 	return 0;
 }
 
@@ -162,6 +237,11 @@
  *	If the caller's attach() function can block, it is their
  *	responsibility to make sure to wait for it to exit before
  *	unloading.
+ *
+ *	All the driver's detach() calls are guaranteed to have
+ *	finished by the time this function returns.
+ *
+ *	The driver's detach() call is not allowed to block.
  **/
 
 void parport_unregister_driver (struct parport_driver *arg)
@@ -194,6 +274,57 @@
 	}
 }
 
+static void free_port (struct parport *port)
+{
+	int d;
+	for (d = 0; d < 5; d++) {
+		if (port->probe_info[d].class_name)
+			kfree (port->probe_info[d].class_name);
+		if (port->probe_info[d].mfr)
+			kfree (port->probe_info[d].mfr);
+		if (port->probe_info[d].model)
+			kfree (port->probe_info[d].model);
+		if (port->probe_info[d].cmdset)
+			kfree (port->probe_info[d].cmdset);
+		if (port->probe_info[d].description)
+			kfree (port->probe_info[d].description);
+	}
+
+	kfree(port->name);
+	kfree(port);
+}
+
+/**
+ *	parport_get_port - increment a port's reference count
+ *	@port: the port
+ *
+ *	This ensure's that a struct parport pointer remains valid
+ *	until the matching parport_put_port() call.
+ **/
+
+struct parport *parport_get_port (struct parport *port)
+{
+	atomic_inc (&port->ref_count);
+	return port;
+}
+
+/**
+ *	parport_put_port - decrement a port's reference count
+ *	@port: the port
+ *
+ *	This should be called once for each call to parport_get_port(),
+ *	once the port is no longer needed.
+ **/
+
+void parport_put_port (struct parport *port)
+{
+	if (atomic_dec_and_test (&port->ref_count))
+		/* Can destroy it now. */
+		free_port (port);
+
+	return;
+}
+
 /**
  *	parport_enumerate - return a list of the system's parallel ports
  *
@@ -260,6 +391,8 @@
 	}
 
 	/* Search for the lowest free parport number. */
+
+	spin_lock_irq (&parportlist_lock);
 	for (portnum = 0; ; portnum++) {
 		struct parport *itr = portlist;
 		while (itr) {
@@ -274,6 +407,7 @@
 			/* Got to the end of the list. */
 			break;
 	}
+	spin_unlock_irq (&parportlist_lock);
 	
 	/* Init our structure */
  	memset(tmp, 0, sizeof(struct parport));
@@ -296,6 +430,7 @@
 	tmp->ieee1284.phase = IEEE1284_PH_FWD_IDLE;
 	init_MUTEX_LOCKED (&tmp->ieee1284.irq); /* actually a semaphore at 0 */
 	tmp->spintime = parport_default_spintime;
+	atomic_set (&tmp->ref_count, 1);
 
 	name = kmalloc(15, GFP_KERNEL);
 	if (!name) {
@@ -372,27 +507,7 @@
 #endif
 
 	/* Let drivers know that a new port has arrived. */
-	call_driver_chain (1, port);
-}
-
-static void free_port (struct parport *port)
-{
-	int d;
-	for (d = 0; d < 5; d++) {
-		if (port->probe_info[d].class_name)
-			kfree (port->probe_info[d].class_name);
-		if (port->probe_info[d].mfr)
-			kfree (port->probe_info[d].mfr);
-		if (port->probe_info[d].model)
-			kfree (port->probe_info[d].model);
-		if (port->probe_info[d].cmdset)
-			kfree (port->probe_info[d].cmdset);
-		if (port->probe_info[d].description)
-			kfree (port->probe_info[d].description);
-	}
-
-	kfree(port->name);
-	kfree(port);
+	attach_driver_chain (port);
 }
 
 /**
@@ -421,7 +536,7 @@
 	port->ops = &dead_ops;
 
 	/* Spread the word. */
-	call_driver_chain (0, port);
+	detach_driver_chain (port);
 
 #ifdef CONFIG_PARPORT_1284
 	/* Forget the IEEE1284.3 topology of the port. */
@@ -431,7 +546,7 @@
 	spin_lock(&parportlist_lock);
 
 	/* We are protected from other people changing the list, but
-	 * they can see see it (using parport_enumerate).  So be
+	 * they can still see it (using parport_enumerate).  So be
 	 * careful about the order of writes.. */
 	if (portlist == port) {
 		if ((portlist = port->next) == NULL)
@@ -449,8 +564,7 @@
 	spin_unlock(&parportlist_lock);
 
 	/* Yes, parport_enumerate _is_ unsafe.  Don't use it. */
-	if (!port->devices)
-		free_port (port);
+	parport_put_port (port);
 }
 
 /**
@@ -552,6 +666,7 @@
            parport_register_device.. */
 	inc_parport_count();
 	port->ops->inc_use_count();
+	parport_get_port (port);
 
 	tmp = kmalloc(sizeof(struct pardevice), GFP_KERNEL);
 	if (tmp == NULL) {
@@ -623,6 +738,7 @@
  out:
 	dec_parport_count();
 	port->ops->dec_use_count();
+	parport_put_port (port);
 	return NULL;
 }
 
@@ -636,7 +752,6 @@
 void parport_unregister_device(struct pardevice *dev)
 {
 	struct parport *port;
-	unsigned long flags;
 
 #ifdef PARPORT_PARANOID
 	if (dev == NULL) {
@@ -649,14 +764,11 @@
 
 	port = dev->port->physport;
 
-	read_lock_irqsave (&port->cad_lock, flags);
 	if (port->cad == dev) {
-		read_unlock_irqrestore (&port->cad_lock, flags);
 		printk(KERN_DEBUG "%s: %s forgot to release port\n",
 		       port->name, dev->name);
 		parport_release (dev);
 	}
-	read_unlock_irqrestore (&port->cad_lock, flags);
 
 	spin_lock(&port->pardevice_lock);
 	if (dev->next)
@@ -676,11 +788,7 @@
 
 	dec_parport_count();
 	port->ops->dec_use_count();
-
-	/* If this was the last device on a port that's already gone away,
-	 * free up the resources. */
-	if (port->ops == &dead_ops && !port->devices)
-		free_port (port);
+	parport_put_port (port);
 
 	/* Yes, that's right, someone _could_ still have a pointer to
 	 * port, if they used parport_enumerate.  That's why they
@@ -689,6 +797,56 @@
 }
 
 /**
+ *	parport_find_number - find a parallel port by number
+ *	@number: parallel port number
+ *
+ *	This returns the parallel port with the specified number, or
+ *	%NULL if there is none.
+ *
+ *	There is an implicit parport_get_port() done already; to throw
+ *	away the reference to the port that parport_find_number()
+ *	gives you, use parport_put_port().
+ */
+
+struct parport *parport_find_number (int number)
+{
+	struct parport *port, *result = NULL;
+	spin_lock (&parportlist_lock);
+	for (port = portlist; port; port = port->next)
+		if (port->number == number) {
+			result = parport_get_port (port);
+			break;
+		}
+	spin_unlock (&parportlist_lock);
+	return result;
+}
+
+/**
+ *	parport_find_base - find a parallel port by base address
+ *	@base: base I/O address
+ *
+ *	This returns the parallel port with the specified base
+ *	address, or %NULL if there is none.
+ *
+ *	There is an implicit parport_get_port() done already; to throw
+ *	away the reference to the port that parport_find_base()
+ *	gives you, use parport_put_port().
+ */
+
+struct parport *parport_find_base (unsigned long base)
+{
+	struct parport *port, *result = NULL;
+	spin_lock (&parportlist_lock);
+	for (port = portlist; port; port = port->next)
+		if (port->base == base) {
+			result = parport_get_port (port);
+			break;
+		}
+	spin_unlock (&parportlist_lock);
+	return result;
+}
+
+/**
  *	parport_claim - claim access to a parallel port device
  *	@dev: pointer to structure representing a device on the port
  *
@@ -706,14 +864,11 @@
 	struct parport *port = dev->port->physport;
 	unsigned long flags;
 
-	read_lock_irqsave (&port->cad_lock, flags);
 	if (port->cad == dev) {
-		read_unlock_irqrestore (&port->cad_lock, flags);
 		printk(KERN_INFO "%s: %s already owner\n",
 		       dev->port->name,dev->name);
 		return 0;
 	}
-	read_unlock_irqrestore (&port->cad_lock, flags);
 
 	/* Preempt any current device */
 	write_lock_irqsave (&port->cad_lock, flags);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)