summaryrefslogtreecommitdiffstats
path: root/drivers/parport
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-07-15 03:32:22 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-07-15 03:32:22 +0000
commitf1da2c3860e301527d56a1ef0b56c649ee7c4b1b (patch)
tree562b5d2e8b9cb62eb983d78ff6bcf9789e08fcf6 /drivers/parport
parent00f11569ac8ca73cbcdef8822de1583e79aee571 (diff)
Merge with Linux 2.4.0-test5-pre1. This works again on Origin UP.
The IP22 cache bugs which are plaguing some machines are still unfixed.
Diffstat (limited to 'drivers/parport')
-rw-r--r--drivers/parport/ChangeLog38
-rw-r--r--drivers/parport/init.c4
-rw-r--r--drivers/parport/share.c255
3 files changed, 246 insertions, 51 deletions
diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog
index d408c8796..34446db01 100644
--- a/drivers/parport/ChangeLog
+++ b/drivers/parport/ChangeLog
@@ -1,4 +1,40 @@
-2000-06-30 Gunther Mayer <gunther.mayer@braunschweig.okersurf.de>
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c: Documentation for parport_{get,port}_port,
+ parport_find_{number,base}.
+
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c (parport_unregister_device): Remove unneeded locking
+ (test cad==dev).
+ (parport_claim): Likewise.
+ (parport_find_number): New function.
+
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c (parport_register_port): Hold the parportlist_lock while
+ looking for a free parport number.
+ (parport_register_driver): Make sure that attach can block.
+ (attach_driver_chain): Likewise.
+
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c (call_driver_chain): Do reference counting things.
+ (parport_get_port): New function.
+ (parport_put_port): New function.
+ (parport_register_port): Initialise reference count to zero.
+ (parport_unregister_port): Check reference count rather than
+ driver list to see if we can free the port.
+
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c: Clarifications in doc comments.
+
+2000-07-12 Tim Waugh <twaugh@redhat.com>
+
+ * share.c (parport_unregister_port): Fix typo in comment.
+
+2000-07-11 Gunther Mayer <gunther.mayer@braunschweig.okersurf.de>
* parport_pc.c: Support for the full range of Timedia cards.
diff --git a/drivers/parport/init.c b/drivers/parport/init.c
index 416ca51cb..90280e371 100644
--- a/drivers/parport/init.c
+++ b/drivers/parport/init.c
@@ -180,6 +180,10 @@ EXPORT_SYMBOL(parport_unregister_driver);
EXPORT_SYMBOL(parport_register_device);
EXPORT_SYMBOL(parport_unregister_device);
EXPORT_SYMBOL(parport_enumerate);
+EXPORT_SYMBOL(parport_get_port);
+EXPORT_SYMBOL(parport_put_port);
+EXPORT_SYMBOL(parport_find_number);
+EXPORT_SYMBOL(parport_find_base);
EXPORT_SYMBOL(parport_negotiate);
EXPORT_SYMBOL(parport_write);
EXPORT_SYMBOL(parport_read);
diff --git a/drivers/parport/share.c b/drivers/parport/share.c
index 9fe0bb4be..92618e7b4 100644
--- a/drivers/parport/share.c
+++ b/drivers/parport/share.c
@@ -88,17 +88,57 @@ static struct parport_operations dead_ops = {
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,27 +161,62 @@ static void get_lowlevel_driver (void)
* 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;
+ struct parport **ports;
+ int count = 0, i;
- spin_lock (&driverlist_lock);
- drv->next = driver_chain;
- driver_chain = drv;
- spin_unlock (&driverlist_lock);
+ if (!portlist)
+ get_lowlevel_driver ();
/* 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);
- if (!portlist)
- get_lowlevel_driver ();
+ 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);
+ }
+
+ spin_lock (&driverlist_lock);
+ drv->next = driver_chain;
+ driver_chain = drv;
+ spin_unlock (&driverlist_lock);
return 0;
}
@@ -162,6 +237,11 @@ int parport_register_driver (struct parport_driver *drv)
* 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 @@ void parport_unregister_driver (struct parport_driver *arg)
}
}
+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 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
}
/* 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 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
/* 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 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma,
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 @@ void parport_announce_port (struct parport *port)
#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 @@ void parport_unregister_port(struct parport *port)
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 @@ void parport_unregister_port(struct parport *port)
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 @@ void parport_unregister_port(struct parport *port)
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(struct parport *port, const char *name,
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 @@ parport_register_device(struct parport *port, const char *name,
out:
dec_parport_count();
port->ops->dec_use_count();
+ parport_put_port (port);
return NULL;
}
@@ -636,7 +752,6 @@ parport_register_device(struct parport *port, const char *name,
void parport_unregister_device(struct pardevice *dev)
{
struct parport *port;
- unsigned long flags;
#ifdef PARPORT_PARANOID
if (dev == NULL) {
@@ -649,14 +764,11 @@ void parport_unregister_device(struct pardevice *dev)
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 @@ void parport_unregister_device(struct pardevice *dev)
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 @@ void parport_unregister_device(struct pardevice *dev)
}
/**
+ * 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 @@ int parport_claim(struct pardevice *dev)
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);