share.c
来自「linux 内核源代码」· C语言 代码 · 共 1,026 行 · 第 1/2 页
C
1,026 行
* The %PARPORT_DEV_EXCL flag is for preventing port sharing, and * so should only be used when sharing the port with other device * drivers is impossible and would lead to incorrect behaviour. * Use it sparingly! Normally, @flags will be zero. * * This function returns a pointer to a structure that represents * the device on the port, or %NULL if there is not enough memory * to allocate space for that structure. **/struct pardevice *parport_register_device(struct parport *port, const char *name, int (*pf)(void *), void (*kf)(void *), void (*irq_func)(void *), int flags, void *handle){ struct pardevice *tmp; if (port->physport->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) { printk(KERN_INFO "%s: refused to register lurking device (%s) without callbacks\n", port->name, name); return NULL; } } /* We up our own module reference count, and that of the port on which a device is to be registered, to ensure that neither of us gets unloaded while we sleep in (e.g.) kmalloc. */ if (!try_module_get(port->ops->owner)) { return NULL; } parport_get_port (port); tmp = kmalloc(sizeof(struct pardevice), GFP_KERNEL); if (tmp == NULL) { printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name); goto out; } tmp->state = kmalloc(sizeof(struct parport_state), GFP_KERNEL); if (tmp->state == NULL) { printk(KERN_WARNING "%s: memory squeeze, couldn't register %s.\n", port->name, name); goto out_free_pardevice; } tmp->name = name; tmp->port = port; tmp->daisy = -1; tmp->preempt = pf; tmp->wakeup = kf; tmp->private = handle; tmp->flags = flags; tmp->irq_func = irq_func; tmp->waiting = 0; tmp->timeout = 5 * HZ; /* Chain this onto the list */ tmp->prev = NULL; /* * 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->physport->pardevice_lock); if (flags & PARPORT_DEV_EXCL) { if (port->physport->devices) { spin_unlock (&port->physport->pardevice_lock); printk (KERN_DEBUG "%s: cannot grant exclusive access for " "device %s\n", port->name, name); goto out_free_all; } port->flags |= PARPORT_FLAG_EXCL; } tmp->next = port->physport->devices; wmb(); /* Make sure that tmp->next is written before it's added to the list; see comments marked 'no locking required' */ if (port->physport->devices) port->physport->devices->prev = tmp; port->physport->devices = tmp; spin_unlock(&port->physport->pardevice_lock); init_waitqueue_head(&tmp->wait_q); tmp->timeslice = parport_default_timeslice; tmp->waitnext = tmp->waitprev = NULL; /* * This has to be run as last thing since init_state may need other * pardevice fields. -arca */ port->ops->init_state(tmp, tmp->state); parport_device_proc_register(tmp); return tmp; out_free_all: kfree(tmp->state); out_free_pardevice: kfree(tmp); out: parport_put_port (port); module_put(port->ops->owner); return NULL;}/** * parport_unregister_device - deregister a device on a parallel port * @dev: pointer to structure representing device * * This undoes the effect of parport_register_device(). **/void parport_unregister_device(struct pardevice *dev){ struct parport *port;#ifdef PARPORT_PARANOID if (dev == NULL) { printk(KERN_ERR "parport_unregister_device: passed NULL\n"); return; }#endif parport_device_proc_unregister(dev); port = dev->port->physport; if (port->cad == dev) { printk(KERN_DEBUG "%s: %s forgot to release port\n", port->name, dev->name); parport_release (dev); } 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; if (dev->flags & PARPORT_DEV_EXCL) port->flags &= ~PARPORT_FLAG_EXCL; spin_unlock(&port->pardevice_lock); /* Make sure we haven't left any pointers around in the wait * list. */ spin_lock (&port->waitlist_lock); if (dev->waitprev || dev->waitnext || port->waithead == dev) { if (dev->waitprev) dev->waitprev->waitnext = dev->waitnext; else port->waithead = dev->waitnext; if (dev->waitnext) dev->waitnext->waitprev = dev->waitprev; else port->waittail = dev->waitprev; } spin_unlock (&port->waitlist_lock); kfree(dev->state); kfree(dev); module_put(port->ops->owner); parport_put_port (port);}/** * 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; if (list_empty(&portlist)) get_lowlevel_driver (); spin_lock (&parportlist_lock); list_for_each_entry(port, &portlist, list) { 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; if (list_empty(&portlist)) get_lowlevel_driver (); spin_lock (&parportlist_lock); list_for_each_entry(port, &portlist, list) { 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 * * This function will not block and so can be used from interrupt * context. If parport_claim() succeeds in claiming access to * the port it returns zero and the port is available to use. It * may fail (returning non-zero) if the port is in use by another * driver and that driver is not willing to relinquish control of * the port. **/int parport_claim(struct pardevice *dev){ struct pardevice *oldcad; struct parport *port = dev->port->physport; unsigned long flags; if (port->cad == dev) { printk(KERN_INFO "%s: %s already owner\n", dev->port->name,dev->name); return 0; } /* Preempt any current device */ write_lock_irqsave (&port->cad_lock, flags); if ((oldcad = port->cad) != NULL) { if (oldcad->preempt) { if (oldcad->preempt(oldcad->private)) goto blocked; port->ops->save_state(port, dev->state); } else goto blocked; if (port->cad != oldcad) { /* I think we'll actually deadlock rather than get here, but just in case.. */ printk(KERN_WARNING "%s: %s released port when preempted!\n", port->name, oldcad->name); if (port->cad) goto blocked; } } /* Can't fail from now on, so mark ourselves as no longer waiting. */ if (dev->waiting & 1) { dev->waiting = 0; /* Take ourselves out of the wait list again. */ spin_lock_irq (&port->waitlist_lock); if (dev->waitprev) dev->waitprev->waitnext = dev->waitnext; else port->waithead = dev->waitnext; if (dev->waitnext) dev->waitnext->waitprev = dev->waitprev; else port->waittail = dev->waitprev; spin_unlock_irq (&port->waitlist_lock); dev->waitprev = dev->waitnext = NULL; } /* Now we do the change of devices */ port->cad = dev;#ifdef CONFIG_PARPORT_1284 /* If it's a mux port, select it. */ if (dev->port->muxport >= 0) { /* FIXME */ port->muxsel = dev->port->muxport; } /* If it's a daisy chain device, select it. */ if (dev->daisy >= 0) { /* This could be lazier. */ if (!parport_daisy_select (port, dev->daisy, IEEE1284_MODE_COMPAT)) port->daisy = dev->daisy; }#endif /* IEEE1284.3 support */ /* Restore control registers */ port->ops->restore_state(port, dev->state); write_unlock_irqrestore(&port->cad_lock, flags); dev->time = jiffies; return 0;blocked: /* If this is the first time we tried to claim the port, register an interest. This is only allowed for devices sleeping in parport_claim_or_block(), or those with a wakeup function. */ /* The cad_lock is still held for writing here */ if (dev->waiting & 2 || dev->wakeup) { spin_lock (&port->waitlist_lock); if (test_and_set_bit(0, &dev->waiting) == 0) { /* First add ourselves to the end of the wait list. */ dev->waitnext = NULL; dev->waitprev = port->waittail; if (port->waittail) { port->waittail->waitnext = dev; port->waittail = dev; } else port->waithead = port->waittail = dev; } spin_unlock (&port->waitlist_lock); } write_unlock_irqrestore (&port->cad_lock, flags); return -EAGAIN;}/** * parport_claim_or_block - claim access to a parallel port device * @dev: pointer to structure representing a device on the port * * This behaves like parport_claim(), but will block if necessary * to wait for the port to be free. A return value of 1 * indicates that it slept; 0 means that it succeeded without * needing to sleep. A negative error code indicates failure. **/int parport_claim_or_block(struct pardevice *dev){ int r; /* Signal to parport_claim() that we can wait even without a wakeup function. */ dev->waiting = 2; /* Try to claim the port. If this fails, we need to sleep. */ r = parport_claim(dev); if (r == -EAGAIN) {#ifdef PARPORT_DEBUG_SHARING printk(KERN_DEBUG "%s: parport_claim() returned -EAGAIN\n", dev->name);#endif /* * FIXME!!! Use the proper locking for dev->waiting, * and make this use the "wait_event_interruptible()" * interfaces. The cli/sti that used to be here * did nothing. * * See also parport_release() */ /* If dev->waiting is clear now, an interrupt gave us the port and we would deadlock if we slept. */ if (dev->waiting) { interruptible_sleep_on (&dev->wait_q); if (signal_pending (current)) { return -EINTR; } r = 1; } else { r = 0;#ifdef PARPORT_DEBUG_SHARING printk(KERN_DEBUG "%s: didn't sleep in parport_claim_or_block()\n", dev->name);#endif }#ifdef PARPORT_DEBUG_SHARING if (dev->port->physport->cad != dev) printk(KERN_DEBUG "%s: exiting parport_claim_or_block " "but %s owns port!\n", dev->name, dev->port->physport->cad ? dev->port->physport->cad->name:"nobody");#endif } dev->waiting = 0; return r;}/** * parport_release - give up access to a parallel port device * @dev: pointer to structure representing parallel port device * * This function cannot fail, but it should not be called without * the port claimed. Similarly, if the port is already claimed * you should not try claiming it again. **/void parport_release(struct pardevice *dev){ struct parport *port = dev->port->physport; struct pardevice *pd; unsigned long flags; /* Make sure that dev is the current device */ write_lock_irqsave(&port->cad_lock, flags); if (port->cad != dev) { write_unlock_irqrestore (&port->cad_lock, flags); printk(KERN_WARNING "%s: %s tried to release parport " "when not owner\n", port->name, dev->name); return; }#ifdef CONFIG_PARPORT_1284 /* If this is on a mux port, deselect it. */ if (dev->port->muxport >= 0) { /* FIXME */ port->muxsel = -1; } /* If this is a daisy device, deselect it. */ if (dev->daisy >= 0) { parport_daisy_deselect_all (port); port->daisy = -1; }#endif port->cad = NULL; write_unlock_irqrestore(&port->cad_lock, flags); /* Save control registers */ port->ops->save_state(port, dev->state); /* If anybody is waiting, find out who's been there longest and then wake them up. (Note: no locking required) */ /* !!! LOCKING IS NEEDED HERE */ for (pd = port->waithead; pd; pd = pd->waitnext) { if (pd->waiting & 2) { /* sleeping in claim_or_block */ parport_claim(pd); if (waitqueue_active(&pd->wait_q)) wake_up_interruptible(&pd->wait_q); return; } else if (pd->wakeup) { pd->wakeup(pd->private); if (dev->port->cad) /* racy but no matter */ return; } else { printk(KERN_ERR "%s: don't know how to wake %s\n", port->name, pd->name); } } /* Nobody was waiting, so walk the list to see if anyone is interested in being woken up. (Note: no locking required) */ /* !!! LOCKING IS NEEDED HERE */ for (pd = port->devices; (port->cad == NULL) && pd; pd = pd->next) { if (pd->wakeup && pd != dev) pd->wakeup(pd->private); }}irqreturn_t parport_irq_handler(int irq, void *dev_id){ struct parport *port = dev_id; parport_generic_irq(port); return IRQ_HANDLED;}/* Exported symbols for modules. */EXPORT_SYMBOL(parport_claim);EXPORT_SYMBOL(parport_claim_or_block);EXPORT_SYMBOL(parport_release);EXPORT_SYMBOL(parport_register_port);EXPORT_SYMBOL(parport_announce_port);EXPORT_SYMBOL(parport_remove_port);EXPORT_SYMBOL(parport_register_driver);EXPORT_SYMBOL(parport_unregister_driver);EXPORT_SYMBOL(parport_register_device);EXPORT_SYMBOL(parport_unregister_device);EXPORT_SYMBOL(parport_get_port);EXPORT_SYMBOL(parport_put_port);EXPORT_SYMBOL(parport_find_number);EXPORT_SYMBOL(parport_find_base);EXPORT_SYMBOL(parport_irq_handler);MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?