parport starts using device-model and we now have parport under /sys/bus. As the ports are discovered they are added as device under /sys/bus/parport. As and when other drivers register new device, they will be registered as a subdevice under the relevant parport. Signed-off-by: Sudip Mukherjee <sudip@xxxxxxxxxxxxxxx> --- drivers/parport/procfs.c | 15 ++- drivers/parport/share.c | 236 ++++++++++++++++++++++++++++++++++++++++++++--- include/linux/parport.h | 29 +++++- 3 files changed, 267 insertions(+), 13 deletions(-) diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 3b47080..1ce363b 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -21,6 +21,7 @@ #include <linux/parport.h> #include <linux/ctype.h> #include <linux/sysctl.h> +#include <linux/device.h> #include <asm/uaccess.h> @@ -558,8 +559,18 @@ int parport_device_proc_unregister(struct pardevice *device) static int __init parport_default_proc_register(void) { + int ret; + parport_default_sysctl_table.sysctl_header = register_sysctl_table(parport_default_sysctl_table.dev_dir); + if (!parport_default_sysctl_table.sysctl_header) + return -ENOMEM; + ret = bus_register(&parport_bus_type); + if (ret) { + unregister_sysctl_table(parport_default_sysctl_table. + sysctl_header); + return ret; + } return 0; } @@ -570,6 +581,7 @@ static void __exit parport_default_proc_unregister(void) sysctl_header); parport_default_sysctl_table.sysctl_header = NULL; } + bus_unregister(&parport_bus_type); } #else /* no sysctl or no procfs*/ @@ -596,11 +608,12 @@ int parport_device_proc_unregister(struct pardevice *device) static int __init parport_default_proc_register (void) { - return 0; + return bus_register(&parport_bus_type); } static void __exit parport_default_proc_unregister (void) { + bus_unregister(&parport_bus_type); } #endif diff --git a/drivers/parport/share.c b/drivers/parport/share.c index 3fa6624..452b2c0 100644 --- a/drivers/parport/share.c +++ b/drivers/parport/share.c @@ -10,6 +10,8 @@ * based on work by Grant Guenther <grant@xxxxxxxxxx> * and Philip Blundell * + * Added Device-Model - Sudip Mukherjee <sudip@xxxxxxxxxxxxxxx> + * * Any part of this program may be used in documents licensed under * the GNU Free Documentation License, Version 1.1 or any later version * published by the Free Software Foundation. @@ -29,6 +31,7 @@ #include <linux/slab.h> #include <linux/sched.h> #include <linux/kmod.h> +#include <linux/device.h> #include <linux/spinlock.h> #include <linux/mutex.h> @@ -100,6 +103,11 @@ static struct parport_operations dead_ops = { .owner = NULL, }; +struct bus_type parport_bus_type = { + .name = "parport", +}; +EXPORT_SYMBOL(parport_bus_type); + /* Call attach(port) for each registered driver. */ static void attach_driver_chain(struct parport *port) { @@ -157,6 +165,7 @@ int parport_register_driver (struct parport_driver *drv) if (list_empty(&portlist)) get_lowlevel_driver (); + drv->devmodel = false; mutex_lock(®istration_lock); list_for_each_entry(port, &portlist, list) @@ -167,6 +176,57 @@ int parport_register_driver (struct parport_driver *drv) return 0; } +/* + * __parport_register_drv - register a new parport driver + * @drv: the driver structure to register + * @owner: owner module of drv + * @mod_name: module name string + * + * Adds the driver structure to the list of registered drivers. + * Returns a negative value on error, otherwise 0. + * If no error occurred, the driver remains registered even if + * no device was claimed during registration. + */ +int __parport_register_drv(struct parport_driver *drv, + struct module *owner, const char *mod_name) +{ + struct parport *port; + int ret, err = 0; + bool attached = false; + + if (list_empty(&portlist)) + get_lowlevel_driver(); + + /* initialize common driver fields */ + drv->driver.name = drv->name; + drv->driver.bus = &parport_bus_type; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + drv->devmodel = true; + ret = driver_register(&drv->driver); + if (ret) + return ret; + + mutex_lock(®istration_lock); + list_for_each_entry(port, &portlist, list) { + ret = drv->attach_ret(port, drv); + if (ret == 0) + attached = true; + else + err = ret; + } + if (attached) + list_add(&drv->list, &drivers); + mutex_unlock(®istration_lock); + if (!attached) { + driver_unregister(&drv->driver); + return err; + } + + return 0; +} +EXPORT_SYMBOL(__parport_register_drv); + /** * parport_unregister_driver - deregister a parallel port device driver * @drv: structure describing the driver that was given to @@ -193,11 +253,15 @@ void parport_unregister_driver (struct parport_driver *drv) list_for_each_entry(port, &portlist, list) drv->detach(port); mutex_unlock(®istration_lock); + if (drv->devmodel) + driver_unregister(&drv->driver); } -static void free_port (struct parport *port) +static void free_port(struct device *dev) { int d; + struct parport *port = to_parport_dev(dev); + spin_lock(&full_list_lock); list_del(&port->full_list); spin_unlock(&full_list_lock); @@ -223,8 +287,9 @@ static void free_port (struct parport *port) struct parport *parport_get_port (struct parport *port) { - atomic_inc (&port->ref_count); - return port; + struct device *dev = get_device(&port->ddev); + + return to_parport_dev(dev); } /** @@ -237,11 +302,7 @@ struct parport *parport_get_port (struct parport *port) void parport_put_port (struct parport *port) { - if (atomic_dec_and_test (&port->ref_count)) - /* Can destroy it now. */ - free_port (port); - - return; + put_device(&port->ddev); } /** @@ -281,6 +342,7 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, int num; int device; char *name; + int ret; tmp = kzalloc(sizeof(struct parport), GFP_KERNEL); if (!tmp) { @@ -333,6 +395,9 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, */ sprintf(name, "parport%d", tmp->portnum = tmp->number); tmp->name = name; + tmp->ddev.bus = &parport_bus_type; + tmp->ddev.release = free_port; + dev_set_name(&tmp->ddev, name); for (device = 0; device < 5; device++) /* assume the worst */ @@ -340,6 +405,13 @@ struct parport *parport_register_port(unsigned long base, int irq, int dma, tmp->waithead = tmp->waittail = NULL; + ret = device_register(&tmp->ddev); + if (ret) { + list_del(&tmp->full_list); + kfree(tmp); + return NULL; + } + return tmp; } @@ -575,6 +647,7 @@ parport_register_device(struct parport *port, const char *name, tmp->irq_func = irq_func; tmp->waiting = 0; tmp->timeout = 5 * HZ; + tmp->devmodel = false; /* Chain this onto the list */ tmp->prev = NULL; @@ -630,6 +703,133 @@ parport_register_device(struct parport *port, const char *name, return NULL; } +void free_pardevice(struct device *dev) +{ +} + +struct pardevice * +parport_register_dev(struct parport *port, const char *name, + int (*pf)(void *), void (*kf)(void *), + void (*irq_func)(void *), int flags, + void *handle, struct parport_driver *drv) +{ + struct pardevice *tmp; + int ret; + char *devname; + + if (port->physport->flags & PARPORT_FLAG_EXCL) { + /* An exclusive device is registered. */ + pr_debug("%s: no more devices allowed\n", + port->name); + return NULL; + } + + if (flags & PARPORT_DEV_LURK) { + if (!pf || !kf) { + pr_info("%s: refused to register lurking device (%s) without callbacks\n", + port->name, name); + return NULL; + } + } + + if (!try_module_get(port->ops->owner)) + return NULL; + + parport_get_port(port); + + tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); + if (!tmp) { + pr_warn("%s: memory squeeze, couldn't register %s.\n", + port->name, name); + goto out; + } + + tmp->state = kmalloc(sizeof(*tmp->state), GFP_KERNEL); + if (!tmp->state) { + pr_warn("%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; + + tmp->dev.parent = &port->ddev; + devname = kstrdup(name, GFP_KERNEL); + dev_set_name(&tmp->dev, "%s", name); + tmp->dev.driver = &drv->driver; + tmp->dev.release = free_pardevice; + tmp->devmodel = true; + ret = device_register(&tmp->dev); + if (ret) + goto out_free_all; + + /* 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); + pr_debug("%s: cannot grant exclusive access for device %s\n", + port->name, name); + goto out_free_dev; + } + 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 = NULL; + 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); + if (!test_and_set_bit(PARPORT_DEVPROC_REGISTERED, &port->devflags)) { + port->proc_device = tmp; + parport_device_proc_register(tmp); + } + + return tmp; +out_free_dev: + put_device(&tmp->dev); +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 @@ -691,7 +891,10 @@ void parport_unregister_device(struct pardevice *dev) spin_unlock_irq(&port->waitlist_lock); kfree(dev->state); - kfree(dev); + if (dev->devmodel) + device_unregister(&dev->dev); + else + kfree(dev); module_put(port->ops->owner); parport_put_port (port); @@ -774,6 +977,7 @@ int parport_claim(struct pardevice *dev) struct pardevice *oldcad; struct parport *port = dev->port->physport; unsigned long flags; + int ret; if (port->cad == dev) { printk(KERN_INFO "%s: %s already owner\n", @@ -802,6 +1006,13 @@ int parport_claim(struct pardevice *dev) } } + if (dev->devmodel) { + ret = device_attach(&dev->dev); + if (ret != 1) { + return -ENODEV; + } + } + /* Can't fail from now on, so mark ourselves as no longer waiting. */ if (dev->waiting & 1) { dev->waiting = 0; @@ -926,8 +1137,8 @@ int parport_claim_or_block(struct pardevice *dev) dev->port->physport->cad ? dev->port->physport->cad->name:"nobody"); #endif - } - dev->waiting = 0; + } else if (r == 0) + dev->waiting = 0; return r; } @@ -954,6 +1165,8 @@ void parport_release(struct pardevice *dev) "when not owner\n", port->name, dev->name); return; } + if (dev->devmodel) + device_release_driver(&dev->dev); #ifdef CONFIG_PARPORT_1284 /* If this is on a mux port, deselect it. */ @@ -1022,6 +1235,7 @@ EXPORT_SYMBOL(parport_remove_port); EXPORT_SYMBOL(parport_register_driver); EXPORT_SYMBOL(parport_unregister_driver); EXPORT_SYMBOL(parport_register_device); +EXPORT_SYMBOL(parport_register_dev); EXPORT_SYMBOL(parport_unregister_device); EXPORT_SYMBOL(parport_get_port); EXPORT_SYMBOL(parport_put_port); diff --git a/include/linux/parport.h b/include/linux/parport.h index c22f125..61b4e4e 100644 --- a/include/linux/parport.h +++ b/include/linux/parport.h @@ -13,6 +13,7 @@ #include <linux/wait.h> #include <linux/irqreturn.h> #include <linux/semaphore.h> +#include <linux/device.h> #include <asm/ptrace.h> #include <uapi/linux/parport.h> @@ -145,6 +146,8 @@ struct pardevice { unsigned int flags; struct pardevice *next; struct pardevice *prev; + struct device dev; + bool devmodel; struct parport_state *state; /* saved status over preemption */ wait_queue_head_t wait_q; unsigned long int time; @@ -195,7 +198,7 @@ struct parport { * This may unfortulately be null if the * port has a legacy driver. */ - + struct device ddev; /* to link with the bus */ struct parport *physport; /* If this is a non-default mux parport, i.e. we're a clone of a real @@ -245,15 +248,22 @@ struct parport { struct parport *slaves[3]; }; +#define to_parport_dev(n) container_of(n, struct parport, ddev) + #define DEFAULT_SPIN_TIME 500 /* us */ struct parport_driver { const char *name; void (*attach) (struct parport *); void (*detach) (struct parport *); + int (*attach_ret)(struct parport *, struct parport_driver *); + struct device_driver driver; + bool devmodel; struct list_head list; }; +extern struct bus_type parport_bus_type; + /* parport_register_port registers a new parallel port at the given address (if one does not already exist) and returns a pointer to it. This entails claiming the I/O region, IRQ and DMA. NULL is returned @@ -274,8 +284,19 @@ extern void parport_remove_port(struct parport *port); /* Register a new high-level driver. */ extern int parport_register_driver (struct parport_driver *); +int __must_check __parport_register_drv(struct parport_driver *, + struct module *, + const char *mod_name); +/* + * parport_register_drv must be a macro so that KBUILD_MODNAME can + * be expanded + */ +#define parport_register_drv(driver) \ + __parport_register_drv(driver, THIS_MODULE, KBUILD_MODNAME) + /* Unregister a high-level driver. */ extern void parport_unregister_driver (struct parport_driver *); +void parport_unregister_driver(struct parport_driver *); /* If parport_register_driver doesn't fit your needs, perhaps * parport_find_xxx does. */ @@ -301,6 +322,12 @@ struct pardevice *parport_register_device(struct parport *port, void (*irq_func)(void *), int flags, void *handle); +struct pardevice * +parport_register_dev(struct parport *port, const char *name, + int (*pf)(void *), void (*kf)(void *), + void (*irq_func)(void *), int flags, + void *handle, struct parport_driver *drv); + /* parport_unregister unlinks a device from the chain. */ extern void parport_unregister_device(struct pardevice *dev); -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-doc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html