From foo@baz Tue Apr 9 12:12:43 2002 Date: Tue, 09 Apr 2002 12:14:34 -0700 To: Greg KH From: Greg Kroah-Hartman Subject: PCI: PCI "piggy" bus to allow multiple pci drivers to bind to a single device Still under construction... Signed-off-by: Greg Kroah-Hartman --- drivers/pci/Makefile | 2 drivers/pci/piggy.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 256 insertions(+) --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -45,6 +45,8 @@ ifndef CONFIG_X86 obj-y += syscall.o endif +obj-y += piggy.o + ifeq ($(CONFIG_PCI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif --- /dev/null +++ b/drivers/pci/piggy.c @@ -0,0 +1,254 @@ +/* + * piggy.c + * + * Copyright (C) 2007 Greg Kroah-Hartman + * Copyright (C) 2007 Novell Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only. + * + */ + +#include +#include +#include +#include "pci.h" + +struct piggy_ids { + struct list_head node; + const struct pci_device_id *id_table; + const struct piggy_driver *driver; +}; + +struct piggy_driver { + struct list_head node; + struct pci_driver *pci_drv; + struct device_driver driver; +}; +#define to_piggy_driver(n) container_of(n, struct piggy_driver, driver) + +struct piggy_device { + struct list_head node; /* node in list of all piggy devices */ + struct pci_dev *pdev; /* backing pci device */ + struct device dev; /* Generic device interface */ +}; +#define to_piggy_dev(n) container_of(n, struct piggy_device, dev) + + +static struct list_head piggy_ids; +static struct list_head piggy_devices; +static struct list_head piggy_drivers; +static struct mutex piggy_drivers_lock; +static struct mutex bus_id_lock; + +static struct bus_type piggy_bus_type; + + +/* we want the option to bind to all pci devices */ +static struct pci_device_id all_ids[] = { + { PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) }, + { }, +}; + +/* returns the number of piggy drivers that will match this id */ +static int match_pci_ids(struct pci_dev *dev) +{ + struct piggy_driver *pig_drv; + const struct pci_device_id *pci_id; + int num_match = 0; + + mutex_lock(&piggy_drivers_lock); + list_for_each_entry(pig_drv, &piggy_drivers, node) { + pci_id = pci_match_device(pig_drv->pci_drv, dev); + if (pci_id) + ++num_match; + } + mutex_unlock(&piggy_drivers_lock); + + return num_match; +} + +static u32 get_pig_bus_id(void) +{ + static u32 pig_bus_id = 0; + u32 result; + + mutex_lock(&bus_id_lock); + result = pig_bus_id; + ++pig_bus_id; + mutex_unlock(&bus_id_lock); + return result; +} + +static void piggy_release(struct device *dev) +{ + struct piggy_device *pig = to_piggy_dev(dev); + + pci_dev_put(pig->pdev); + list_del(&pig->node); + kfree(pig); +} + + +static int pci_piggy_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct piggy_device *pig; + u32 bus_id; + int num_devices; + int result; + int i; + + /* if we don't match any devices at all, then we don't need to + * bind to the device */ + num_devices = match_pci_ids(pci_dev); + if (!num_devices) + return -ENODEV; + + bus_id = get_pig_bus_id(); + + /* create a number of piggy devices to bind to this pci device */ + for (i = 0; i < num_devices; ++i) { + pig = kzalloc(sizeof(*pig), GFP_KERNEL); + if (!pig) + return -ENOMEM; + INIT_LIST_HEAD(&pig->node); + pig->pdev = pci_dev_get(pci_dev); + pig->dev.parent = &pci_dev->dev; + bus_id = get_pig_bus_id(); + snprintf(pig->dev.bus_id, BUS_ID_SIZE, "pig%04d-%d", bus_id, i); + pig->dev.bus = &piggy_bus_type; + pig->dev.release = piggy_release; + result = device_register(&pig->dev); + if (result) { + dev_err(&pci_dev->dev, "pig device %s failed to be " + "registered %d\n", pig->dev.bus_id, result); + return result; + } + } + + return 0; +} + +static void pci_piggy_remove(struct pci_dev *dev) +{ +} + +static struct pci_driver pci_piggy_driver = { + .name = "piggy_bridge", + .probe = pci_piggy_probe, + .remove = pci_piggy_remove, + .id_table = all_ids, +}; + +static int piggy_device_match(struct device *dev, struct device_driver *drv) +{ + return 0; +} + +static int piggy_device_probe(struct device *dev) +{ + return 0; +} +static int piggy_device_remove(struct device *dev) +{ + return 0; +} + +static struct bus_type piggy_bus_type = { + .name = "pci-piggy", + .match = piggy_device_match, + .probe = piggy_device_probe, + .remove = piggy_device_remove, +}; + + +static int piggy_init(void) +{ + static bool initialized = false; + int result; + + if (initialized == true) + return 0; + + INIT_LIST_HEAD(&piggy_ids); + INIT_LIST_HEAD(&piggy_devices); + INIT_LIST_HEAD(&piggy_drivers); + + mutex_init(&piggy_drivers_lock); + mutex_init(&bus_id_lock); + + result = bus_register(&piggy_bus_type); + if (result) { + printk(KERN_ERR "PCI: can not register piggy bus\n"); + return result; + } + + result = pci_register_driver(&pci_piggy_driver); + if (result) { + printk(KERN_ERR "PCI: can't register piggy driver\n"); + return result; + } + initialized = true; + return 0; +} + + +int pci_piggy_driver_register(struct pci_driver *pci_drv, struct module *owner, + const char *mod_name) +{ + struct piggy_driver *pig_drv; + int result; + + result = piggy_init(); + if (result) + return result; + + /* FIXME create a pci driver here too? People are going to get + * confused...*/ + + pig_drv = kzalloc(sizeof(*pig_drv), GFP_KERNEL); + if (!pig_drv) + return -ENOMEM; + INIT_LIST_HEAD(&pig_drv->node); + pig_drv->pci_drv = pci_drv; + + pig_drv->driver.name = pci_drv->name; + pig_drv->driver.bus = &piggy_bus_type; + pig_drv->driver.owner = owner; + pig_drv->driver.mod_name = mod_name; + + mutex_lock(&piggy_drivers_lock); + list_add_tail(&piggy_drivers, &pig_drv->node); + mutex_unlock(&piggy_drivers_lock); + + return driver_register(&pig_drv->driver); +} + +void pci_piggy_driver_deregister(struct pci_driver *pci_drv) +{ + struct piggy_driver *match; + struct piggy_driver *pig_drv = NULL; + + /* walk the list and find me the driver with the same name */ + mutex_lock(&piggy_drivers_lock); + list_for_each_entry(match, &piggy_drivers, node) { + if (strcmp(match->driver.name, pci_drv->name) == 0) + pig_drv = match; + break; + } + mutex_unlock(&piggy_drivers_lock); + + if (!pig_drv) { + printk(KERN_ERR "PCI: piggy driver not found for pci driver " + "'%s'\n", pci_drv->name); + return; + } + + /* this is the one */ + list_del(&pig_drv->node); + driver_unregister(&pig_drv->driver); + kfree(pig_drv); + /* FIXME have to do some device lifecycle here probably...*/ +} +