patch-2.1.126 linux/drivers/misc/parport_share.c
Next file: linux/drivers/net/3c505.c
Previous file: linux/drivers/misc/parport_procfs.c
Back to the patch index
Back to the overall index
- Lines: 264
- Date:
Fri Oct 9 12:20:27 1998
- Orig file:
v2.1.125/linux/drivers/misc/parport_share.c
- Orig date:
Wed Sep 9 14:51:07 1998
diff -u --recursive --new-file v2.1.125/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
@@ -27,6 +27,7 @@
#include <linux/sched.h>
#include <asm/spinlock.h>
+#include <asm/irq.h>
#ifdef CONFIG_KMOD
#include <linux/kmod.h>
@@ -55,19 +56,12 @@
return portlist;
}
-void parport_null_intr_func(int irq, void *dev_id, struct pt_regs *regs)
-{
- /* Null function - does nothing. IRQs are pointed here whenever
- there is no real handler for them. */
-}
-
struct parport *parport_register_port(unsigned long base, int irq, int dma,
struct parport_operations *ops)
{
struct parport *tmp;
int portnum;
char *name;
- unsigned long flags;
/* Check for a previously registered port.
NOTE: we will ignore irq and dma if we find a previously
@@ -111,7 +105,9 @@
tmp->ops = ops;
tmp->number = portnum;
memset (&tmp->probe_info, 0, sizeof (struct parport_device_info));
- spin_lock_init (&tmp->lock);
+ spin_lock_init(&tmp->cad_lock);
+ spin_lock_init(&tmp->waitlist_lock);
+ spin_lock_init(&tmp->pardevice_lock);
name = kmalloc(15, GFP_KERNEL);
if (!name) {
@@ -122,14 +118,19 @@
sprintf(name, "parport%d", portnum);
tmp->name = name;
- /* Chain the entry to our list. */
- spin_lock_irqsave (&parportlist_lock, flags);
+ /*
+ * Chain the entry to our list.
+ *
+ * This function must not run from an irq handler so we don' t need
+ * to clear irq on the local CPU. -arca
+ */
+ spin_lock(&parportlist_lock);
if (portlist_tail)
portlist_tail->next = tmp;
portlist_tail = tmp;
if (!portlist)
portlist = tmp;
- spin_unlock_irqrestore (&parportlist_lock, flags);
+ spin_unlock(&parportlist_lock);
tmp->probe_info.class = PARPORT_CLASS_LEGACY; /* assume the worst */
tmp->waithead = tmp->waittail = NULL;
@@ -140,8 +141,8 @@
void parport_unregister_port(struct parport *port)
{
struct parport *p;
- unsigned long flags;
- spin_lock_irqsave (&parportlist_lock, flags);
+
+ spin_lock(&parportlist_lock);
if (portlist == port) {
if ((portlist = port->next) == NULL)
portlist_tail = NULL;
@@ -155,7 +156,7 @@
else printk (KERN_WARNING
"%s not found in port list!\n", port->name);
}
- spin_unlock_irqrestore (&parportlist_lock, flags);
+ spin_unlock(&parportlist_lock);
if (port->probe_info.class_name)
kfree (port->probe_info.class_name);
if (port->probe_info.mfr)
@@ -195,7 +196,13 @@
int flags, void *handle)
{
struct pardevice *tmp;
- unsigned long flgs;
+
+ if (port->flags & PARPORT_FLAG_EXCL) {
+ /* An exclusive device is registered. */
+ printk (KERN_DEBUG "%s: no more devices allowed\n",
+ port->name);
+ return NULL;
+ }
if (flags & PARPORT_DEV_LURK) {
if (!pf || !kf) {
@@ -242,12 +249,30 @@
/* Chain this onto the list */
tmp->prev = NULL;
- spin_lock_irqsave (&port->lock, flgs);
+ /*
+ * This function must not run from an irq handler so we don' t need
+ * to clear irq on the local CPU. -arca
+ */
+ spin_lock(&port->pardevice_lock);
+
+ if (flags & PARPORT_DEV_EXCL) {
+ if (port->devices) {
+ spin_unlock (&port->pardevice_lock);
+ kfree (tmp->state);
+ kfree (tmp);
+ printk (KERN_DEBUG
+ "%s: cannot grant exclusive access for "
+ "device %s\n", port->name, name);
+ return NULL;
+ }
+ port->flags |= PARPORT_FLAG_EXCL;
+ }
+
tmp->next = port->devices;
if (port->devices)
port->devices->prev = tmp;
port->devices = tmp;
- spin_unlock_irqrestore (&port->lock, flgs);
+ spin_unlock(&port->pardevice_lock);
inc_parport_count();
port->ops->inc_use_count();
@@ -262,7 +287,6 @@
void parport_unregister_device(struct pardevice *dev)
{
struct parport *port;
- unsigned long flags;
#ifdef PARPORT_PARANOID
if (dev == NULL) {
@@ -279,14 +303,18 @@
return;
}
- spin_lock_irqsave (&port->lock, flags);
+ spin_lock(&port->pardevice_lock);
if (dev->next)
dev->next->prev = dev->prev;
if (dev->prev)
dev->prev->next = dev->next;
else
port->devices = dev->next;
- spin_unlock_irqrestore (&port->lock, flags);
+
+ if (dev->flags & PARPORT_DEV_EXCL)
+ port->flags &= ~PARPORT_FLAG_EXCL;
+
+ spin_unlock(&port->pardevice_lock);
kfree(dev->state);
kfree(dev);
@@ -337,7 +365,7 @@
dev->waiting = 0;
/* Take ourselves out of the wait list again. */
- spin_lock_irqsave (&port->lock, flags);
+ spin_lock_irqsave (&port->waitlist_lock, flags);
if (dev->waitprev)
dev->waitprev->waitnext = dev->waitnext;
else
@@ -346,28 +374,27 @@
dev->waitnext->waitprev = dev->waitprev;
else
port->waittail = dev->waitprev;
- spin_unlock_irqrestore (&port->lock, flags);
+ spin_unlock_irqrestore (&port->waitlist_lock, flags);
dev->waitprev = dev->waitnext = NULL;
}
+ if (oldcad && port->irq != PARPORT_IRQ_NONE && !oldcad->irq_func)
+ /*
+ * If there was an irq pending it should hopefully happen
+ * before return from enable_irq(). -arca
+ */
+ enable_irq(port->irq);
+
+ /*
+ * Avoid running irq handlers if the pardevice doesn' t use it. -arca
+ */
+ if (port->irq != PARPORT_IRQ_NONE && !dev->irq_func)
+ disable_irq(port->irq);
+
/* Now we do the change of devices */
- spin_lock_irqsave(&port->lock, flags);
+ write_lock_irqsave(&port->cad_lock, flags);
port->cad = dev;
- spin_unlock_irqrestore(&port->lock, flags);
-
- /* Swap the IRQ handlers. */
- if (port->irq != PARPORT_IRQ_NONE) {
- if (oldcad && oldcad->irq_func) {
- free_irq(port->irq, oldcad->private);
- request_irq(port->irq, parport_null_intr_func,
- SA_INTERRUPT, port->name, NULL);
- }
- if (dev->irq_func) {
- free_irq(port->irq, NULL);
- request_irq(port->irq, dev->irq_func,
- SA_INTERRUPT, dev->name, dev->private);
- }
- }
+ write_unlock_irqrestore(&port->cad_lock, flags);
/* Restore control registers */
port->ops->restore_state(port, dev->state);
@@ -379,10 +406,10 @@
interest. This is only allowed for devices sleeping in
parport_claim_or_block(), or those with a wakeup function. */
if (dev->waiting & 2 || dev->wakeup) {
- spin_lock_irqsave (&port->lock, flags);
+ spin_lock_irqsave (&port->waitlist_lock, flags);
if (port->cad == NULL) {
/* The port got released in the meantime. */
- spin_unlock_irqrestore (&port->lock, flags);
+ spin_unlock_irqrestore (&port->waitlist_lock, flags);
goto try_again;
}
if (test_and_set_bit(0, &dev->waiting) == 0) {
@@ -395,7 +422,7 @@
} else
port->waithead = port->waittail = dev;
}
- spin_unlock_irqrestore (&port->lock, flags);
+ spin_unlock_irqrestore (&port->waitlist_lock, flags);
}
return -EAGAIN;
}
@@ -451,19 +478,19 @@
"when not owner\n", port->name, dev->name);
return;
}
- spin_lock_irqsave(&port->lock, flags);
+ write_lock_irqsave(&port->cad_lock, flags);
port->cad = NULL;
- spin_unlock_irqrestore(&port->lock, flags);
+ write_unlock_irqrestore(&port->cad_lock, flags);
+
+ /*
+ * Reenable irq and so discard the eventually pending irq while
+ * cad is NULL. -arca
+ */
+ if (port->irq != PARPORT_IRQ_NONE && !dev->irq_func)
+ enable_irq(port->irq);
/* Save control registers */
port->ops->save_state(port, dev->state);
-
- /* Point IRQs somewhere harmless. */
- if (port->irq != PARPORT_IRQ_NONE && dev->irq_func) {
- free_irq(port->irq, dev->private);
- request_irq(port->irq, parport_null_intr_func,
- SA_INTERRUPT, port->name, NULL);
- }
/* If anybody is waiting, find out who's been there longest and
then wake them up. (Note: no locking required) */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov