diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-07-15 03:32:22 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-07-15 03:32:22 +0000 |
commit | f1da2c3860e301527d56a1ef0b56c649ee7c4b1b (patch) | |
tree | 562b5d2e8b9cb62eb983d78ff6bcf9789e08fcf6 /drivers/parport | |
parent | 00f11569ac8ca73cbcdef8822de1583e79aee571 (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/ChangeLog | 38 | ||||
-rw-r--r-- | drivers/parport/init.c | 4 | ||||
-rw-r--r-- | drivers/parport/share.c | 255 |
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); |