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
- Lines: 410
- Date:
Wed Jul 12 16:24:34 2000
- Orig file:
v2.4.0-test3/linux/drivers/parport/share.c
- Orig date:
Mon Jul 10 16:47:24 2000
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)