diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/Kconfig --- a/linux-2.6-xen-sparse/drivers/xen/Kconfig Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Kconfig Thu Apr 27 19:59:04 2006 @@ -73,6 +73,29 @@ config XEN_PCIDEV_BE_DEBUG bool "PCI Backend Debugging" depends on XEN_PCIDEV_BACKEND + default n + +config XEN_PNP + bool + default y + depends on XEN + ---help--- + This is a dependency option. PNP is dependent on this which should + have as its dependencies the requirements for PNP support on the + current platform (such as ISA/ACPI). + +config XEN_PNPDEV_BACKEND + tristate "PNP Backend" + select PNP + default y if XEN_PRIVILEGED_GUEST + ---help--- + The PNP backend enables driver domains to export physicals devices + that are discovered through some Plug-N-Play means (PNPBIOS, ACPI, + etc.). + +config XEN_PNP_BE_DEBUG + bool "PNP Backend Debugging" + depends on XEN_PNPDEV_BACKEND default n config XEN_BLKDEV_BACKEND diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/Makefile --- a/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/Makefile Thu Apr 27 19:59:04 2006 @@ -17,6 +17,7 @@ obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/ obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/ obj-$(CONFIG_XEN_TPMDEV_FRONTEND) += tpmfront/ +obj-$(CONFIG_XEN_PNPDEV_BACKEND) += pnpback/ +obj-$(CONFIG_XEN_PNPDEV_FRONTEND) += pnpfront/ obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/ - diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/pnp/Kconfig --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/pnp/Kconfig Thu Apr 27 19:59:04 2006 @@ -0,0 +1,55 @@ +# +# Plug and Play configuration +# + +menu "Plug and Play support" + +config PNP + bool "Plug and Play support" + depends on ISA || ACPI || XEN_PNP + ---help--- + Plug and Play (PnP) is a standard for peripherals which allows those + peripherals to be configured by software, e.g. assign IRQ's or other + parameters. No jumpers on the cards are needed, instead the values + are provided to the cards from the BIOS, from the operating system, + or using a user-space utility. + + Say Y here if you would like Linux to configure your Plug and Play + devices. You should then also say Y to all of the protocols below. + Alternatively, you can say N here and configure your PnP devices + using user space utilities such as the isapnptools package. + + If unsure, say Y. + +config PNP_DEBUG + bool "PnP Debug Messages" + depends on PNP + help + Say Y if you want the Plug and Play Layer to print debug messages. + This is useful if you are developing a PnP driver or troubleshooting. + +comment "Protocols" + depends on PNP + +source "drivers/pnp/isapnp/Kconfig" + +source "drivers/pnp/pnpbios/Kconfig" + +source "drivers/pnp/pnpacpi/Kconfig" + +config XEN_PNPDEV_FRONTEND + bool "Xen PNP Frontend" + depends on XEN_PNP && PNP + default y + ---help--- + The PNP frontend enables driver domains to receive physicals devices + that are discovered through some Plug-N-Play means (PNPBIOS, ACPI, + etc.). + +config XEN_PNP_FE_DEBUG + bool "PNP Frontend Debugging" + depends on XEN_PNPDEV_FRONTEND + default n + +endmenu + diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpback/Makefile --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpback/Makefile Thu Apr 27 19:59:04 2006 @@ -0,0 +1,7 @@ +obj-${CONFIG_XEN_PNPDEV_BACKEND} += pnpback.o + +pnpback-y := pnp_stub.o xenbus.o + +ifeq ($(CONFIG_XEN_PNP_BE_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpback/pnp_stub.c --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpback/pnp_stub.c Thu Apr 27 19:59:04 2006 @@ -0,0 +1,410 @@ +/* + * PNP Stub Driver - Grabs devices in backend to be exported to a driver domain + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#include +#include +#include "pnpback.h" + +static char *pnp_devs_to_hide = NULL; +module_param_named(hide, pnp_devs_to_hide, charp, 0444); + +enum pnp_id_type { + PNPID_NONE = 0, + PNPID_DEV_ID = 1, + PNPID_BUS_ID = 2, +}; + +struct pnp_id_entry { + struct list_head list; + enum pnp_id_type type; + + /* PNPID_DEV_ID (a.k.a. EisaId) */ + struct pnp_id dev_id; + + /* PNPID_BUS_ID (unique identifier for devices, + * see /sys/bus/pnp/devices) + */ + unsigned char protocol_index; + unsigned char device_index; +}; +static LIST_HEAD(pnpback_devices_to_seize); + +struct pnp_dev_entry { + struct list_head list; + struct pnp_dev *dev; +}; +static LIST_HEAD(pnpback_devices); + +/* access to pnpback_devices and pnpback_devices_to_seize must be protected + * by this lock + */ +static DEFINE_SPINLOCK(pnpback_devices_lock); + +#define PNP_ID_ENTRY_DEV_ID(eisa_id) \ + { \ + .type = PNPID_DEV_ID, \ + .dev_id = { \ + .id = eisa_id \ + } \ + } + +/* Devices we shouldn't try and export */ +static struct pnp_id_entry pnp_id_blacklist[] = { + PNP_ID_ENTRY_DEV_ID("PNP0c00"), + PNP_ID_ENTRY_DEV_ID("PNP0c01"), + PNP_ID_ENTRY_DEV_ID("PNP0c02"), + {.type = PNPID_NONE}, +}; + +/* Devices that we shouldn't let ACPI disable - if ACPI supports disabling the + * serial port and it's in-use by Xen as a debug console, bad things can + * happen. */ +static struct pnp_id_entry pnp_id_disable_blacklist[] = { + PNP_ID_ENTRY_DEV_ID("PNP0500"), + PNP_ID_ENTRY_DEV_ID("PNP0501"), + {.type = PNPID_NONE}, +}; + +struct pnp_id_entry *str_to_pnp_id(char *pnp_id_str) +{ + int i, cnt; + int protocol, device; + struct pnp_id_entry *id; + + pr_debug("pnpback: str_to_pnp_id: parsing \"%s\"\n", pnp_id_str); + + /* skip whitespace */ + while (isspace(*pnp_id_str)) + pnp_id_str++; + + id = kzalloc(sizeof(*id), GFP_KERNEL); + if (!id) + return NULL; + + if (strchr(pnp_id_str, ':')) { + cnt = sscanf(pnp_id_str, "%x:%x", &protocol, &device); + if (cnt != 2) + goto parse_error; + + id->type = PNPID_BUS_ID; + id->protocol_index = protocol; + id->device_index = device; + + pr_debug("pnpback: str_to_pnp_id bus_id %02x:%02x\n", + protocol, device); + } else { + id->type = PNPID_DEV_ID; + + /* PNP IDs are 3 letters followed by 4 hex digits */ + for (i = 0; i < 7; i++, pnp_id_str++) { + if ((i < 3 && !isalpha(*pnp_id_str)) + || (i >= 3 && !isxdigit(*pnp_id_str))) + goto parse_error; + id->dev_id.id[i] = *pnp_id_str; + } + id->dev_id.id[7] = 0; + + pr_debug("pnpback: str_to_pnp_id dev_id %s\n", id->dev_id.id); + } + + return id; + + parse_error: + kfree(id); + return NULL; +} + +static int match_pnp_id(struct pnp_dev *dev, struct pnp_id_entry *id_entry) +{ + struct pnp_id *id; + + switch (id_entry->type) { + case PNPID_BUS_ID: + dev_dbg(&dev->dev, "compare to %02x:%02x\n", + id_entry->protocol_index, id_entry->device_index); + + if (dev->protocol->number == id_entry->protocol_index + && dev->number == id_entry->device_index) + return 1; + break; + + case PNPID_DEV_ID: + for (id = dev->id; id != NULL; id = id->next) { + dev_dbg(&dev->dev, "compare \"%s\" to \"%s\"\n", + id->id, id_entry->dev_id.id); + + if (!strnicmp(id->id, id_entry->dev_id.id, 7)) + return 1; + } + break; + + default: + /* unknown type, break to return false */ + break; + } + + return 0; +} + +static int match_pnp_id_to_seize(struct pnp_dev *dev) +{ + struct pnp_id_entry *id_entry; + int found = 0; + unsigned long flags; + + dev_dbg(&dev->dev, "begin match_pnp_id_to_seize\n"); + + spin_lock_irqsave(&pnpback_devices_lock, flags); + + list_for_each_entry(id_entry, &pnpback_devices_to_seize, list) { + if (match_pnp_id(dev, id_entry)) { + found = 1; + break; + } + } + + spin_unlock_irqrestore(&pnpback_devices_lock, flags); + + return found; +} + +static int is_pnp_id_in_array(struct pnp_dev *dev, struct pnp_id_entry *list) +{ + int i; + + for (i = 0; list[i].type != PNPID_NONE; i++) { + if (match_pnp_id(dev, &list[i])) + return 1; + } + + return 0; +} + +struct pnp_dev *pnpstub_get_pnp_dev(char *name, struct pnpback_device *pdev) +{ + struct pnp_id_entry *id_entry = str_to_pnp_id(name); + struct pnp_dev_entry *dev_entry; + struct pnp_dev *dev = NULL; + unsigned long flags; + + if (!id_entry) + return NULL; + + spin_lock_irqsave(&pnpback_devices_lock, flags); + list_for_each_entry(dev_entry, &pnpback_devices, list) { + if (!pnp_get_drvdata(dev_entry->dev) + && match_pnp_id(dev_entry->dev, id_entry)) { + dev = dev_entry->dev; + + /* Mark in-use */ + pnp_set_drvdata(dev, pdev); + + /* Do we need to activate the device here if not + * already active? All of my devices are already active. + */ + break; + } + } + spin_unlock_irqrestore(&pnpback_devices_lock, flags); + + return dev; +} + +void pnpstub_put_pnp_dev(struct pnp_dev *dev) +{ + struct pnp_dev_entry *dev_entry; + unsigned long flags; + + spin_lock_irqsave(&pnpback_devices_lock, flags); + list_for_each_entry(dev_entry, &pnpback_devices, list) { + if (dev_entry->dev == dev) { + /* Mark not in-use */ + pnp_set_drvdata(dev, NULL); + break; + } + } + spin_unlock_irqrestore(&pnpback_devices_lock, flags); +} + +/* We want to try and match any device in the system and then compare + * them to our list of devices to seize + */ +static const struct pnp_device_id pnp_dev_table[] = { + {"ANYDEVS", 0}, + {"", 0}, +}; + +static int __devinit pnpstub_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) +{ + int err = 0; + int i; + struct pnp_dev_entry *dev_entry; + unsigned long flags; + + dev_dbg(&dev->dev, "probing %s ID: %s\n", + pnp_dev_name(dev), dev_id->id); + + if (match_pnp_id_to_seize(dev)) { + if (is_pnp_id_in_array(dev, pnp_id_blacklist)) { + dev_err(&dev->dev, "Requested device is on blacklist!"); + err = -ENODEV; + goto out; + } + + for (i = 0; i < PNP_MAX_DMA; i++) { + if (!(dev->res.dma_resource[i].flags + & IORESOURCE_UNSET)) { + dev_err(&dev->dev, "can't support " + "devices which require a legacy " + "DMA controller!"); + err = -ENODEV; + goto out; + } + } + + dev_info(&dev->dev, "seizing %s\n", pnp_dev_name(dev)); + + dev_entry = kzalloc(sizeof(*dev_entry), GFP_KERNEL); + if (!dev_entry) { + err = -ENOMEM; + goto out; + } + + dev_entry->dev = dev; + + spin_lock_irqsave(&pnpback_devices_lock, flags); + list_add(&dev_entry->list, &pnpback_devices); + spin_unlock_irqrestore(&pnpback_devices_lock, flags); + } else { + /* Prevent disabling certain devices */ + if (is_pnp_id_in_array(dev, pnp_id_disable_blacklist)) + dev->capabilities &= ~PNP_DISABLE; + + /* Not our device */ + err = -ENODEV; + } + + out: + return err; +} + +static void __devexit pnpstub_remove(struct pnp_dev *dev) +{ + unsigned long flags; + struct pnpback_device *pdev; + struct pnp_dev_entry *dev_entry, *t; + + spin_lock_irqsave(&pnpback_devices_lock, flags); + + pdev = pnp_get_drvdata(dev); + + list_for_each_entry_safe(dev_entry, t, &pnpback_devices, list) { + if (dev_entry->dev == dev) { + list_del(&dev_entry->list); + kfree(dev_entry); + break; + } + } + + spin_unlock_irqrestore(&pnpback_devices_lock, flags); + + if (pdev) { + dev_warn(&dev->dev, "Removed device while exported to " + "driver domain!\n"); + dev_warn(&dev->dev, "Driver domain may still access I/O " + "resources!\n"); + dev_warn(&dev->dev, "Shutdown driver domain for using " + "device in any other domain.\n"); + pnpback_remove(pdev); + } +} + +static struct pnp_driver pnpstub_driver = { + .name = "pnpback", + .id_table = pnp_dev_table, + .probe = pnpstub_probe, + .remove = pnpstub_remove, +}; + +static int __init pnpstub_init(void) +{ + char *p = pnp_devs_to_hide; + char *end; + char *tmp = (char *)get_zeroed_page(GFP_KERNEL); + struct pnp_id_entry *id_entry; + int pos; + int err = 0; + + while (p && *p) { + pos = 0; + + /* skip whitespace */ + for (; isspace(*p); p++); + + end = strchr(p, ','); + if (end) { + for (; p < end && pos < (PAGE_SIZE - 1); p++, pos++) + tmp[pos] = *p; + p = end + 1; + } else { + for (; *p && pos < (PAGE_SIZE - 1); p++, pos++) + tmp[pos] = *p; + p = NULL; + } + tmp[pos] = 0; + + /* No more entries */ + if (pos == 0) + break; + + id_entry = str_to_pnp_id(tmp); + if (!id_entry) { + err = -EINVAL; + printk(KERN_ERR + "pnpback: parse error in pnpback.hide " + "at \"%s\"\n", tmp); + break; + } + + pr_debug("pnpback: adding \"%s\" to seize list\n", tmp); + spin_lock(&pnpback_devices_lock); + list_add(&id_entry->list, &pnpback_devices_to_seize); + spin_unlock(&pnpback_devices_lock); + } + + free_page((unsigned long)tmp); + + if (!err) { + pnp_register_driver(&pnpstub_driver); + + err = pnpback_xenbus_register(); + if (err) + pnp_unregister_driver(&pnpstub_driver); + } + + return err; +} + +static void __exit pnpstub_exit(void) +{ + pnpback_xenbus_unregister(); + pnp_unregister_driver(&pnpstub_driver); +} + +/* We use fs_initcall because we want to be the first driver installed in the + * PNP subsystem. That way, we get probed first for any new devices and have + * the best chance of seizing them from the system before other drivers can + * get their hands on them. + */ +fs_initcall(pnpstub_init); +module_exit(pnpstub_exit); + +MODULE_LICENSE("GPL"); diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpback/pnpback.h --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpback/pnpback.h Thu Apr 27 19:59:04 2006 @@ -0,0 +1,32 @@ +/* + * PNP Backend Common Data Structures & Function Declarations + * + * Author: Ryan Wilson + */ +#ifndef __XEN_PNPBACK_H__ +#define __XEN_PNPBACK_H__ + +#include +#include +#include + +struct pnpback_device { + spinlock_t lock; + + struct pnp_dev *pnp_dev; + + struct xenbus_device *xdev; + + struct xenbus_watch be_watch; + u8 be_watching; +}; + +/* Get/Put PNP Devices that are hidden from the PNP Backend Domain */ +struct pnp_dev *pnpstub_get_pnp_dev(char *pnp_id, struct pnpback_device *pdev); +void pnpstub_put_pnp_dev(struct pnp_dev *dev); + +void pnpback_remove(struct pnpback_device *pdev); + +int __init pnpback_xenbus_register(void); +void __exit pnpback_xenbus_unregister(void); +#endif diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpback/xenbus.c --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpback/xenbus.c Thu Apr 27 19:59:04 2006 @@ -0,0 +1,354 @@ +/* + * PNP Backend Xenbus Setup - handles setup with frontend and xend + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#include "pnpback.h" + +static struct pnpback_device *alloc_pdev(struct xenbus_device *xdev) +{ + struct pnpback_device *pdev; + + pdev = kmalloc(sizeof(*pdev), GFP_KERNEL); + if (pdev == NULL) + goto out; + dev_dbg(&xdev->dev, "allocated pdev @ 0x%p\n", pdev); + + pdev->xdev = xdev; + xdev->data = pdev; + + spin_lock_init(&pdev->lock); + + pdev->be_watching = 0; + pdev->pnp_dev = NULL; + + out: + return pdev; +} + +static void free_pdev(struct pnpback_device *pdev) +{ + if (pdev->be_watching) + unregister_xenbus_watch(&pdev->be_watch); + + if (pdev->pnp_dev) + pnpstub_put_pnp_dev(pdev->pnp_dev); + + pdev->xdev->data = NULL; + pdev->xdev = NULL; + + kfree(pdev); +} + +void pnpback_remove(struct pnpback_device *pdev) +{ + /* pnp_dev is released in free_pdev */ + device_unregister(&pdev->xdev->dev); +} + +static void pnpback_frontend_changed(struct xenbus_device *xdev, + XenbusState fe_state) +{ + struct pnpback_device *pdev = xdev->data; + + dev_dbg(&xdev->dev, "fe state changed %d\n", fe_state); + + switch (fe_state) { + case XenbusStateClosing: + xenbus_switch_state(xdev, XenbusStateClosing); + break; + + case XenbusStateClosed: + dev_dbg(&xdev->dev, "frontend is gone! unregister device\n"); + pnpback_remove(pdev); + break; + + default: + break; + } +} + +static char *join_path(const char *path1, ...) +{ + int len = strlen(path1); + char *j_path; + char *pathn; + va_list ap; + + /* determine length of new path */ + va_start(ap, path1); + pathn = va_arg(ap, char *); + while (pathn) { + len += 1 + strlen(pathn); + pathn = va_arg(ap, char *); + } + va_end(ap); + + j_path = kmalloc(len + 1, GFP_KERNEL); + if (!j_path) + return NULL; + + /* build path */ + strcpy(j_path, path1); + va_start(ap, path1); + pathn = va_arg(ap, char *); + while (pathn) { + strcat(j_path, "/"); + strcat(j_path, pathn); + pathn = va_arg(ap, char *); + } + va_end(ap); + + return j_path; +} + +int xenbus_write_resource(xenbus_transaction_t th, const char *dir, + const char *node, struct resource *res) +{ + int err; + char *res_dir = NULL; + + err = xenbus_mkdir(th, dir, node); + if (err) + goto out; + + res_dir = join_path(dir, node, NULL); + if (!res_dir) { + err = -ENOMEM; + goto out; + } + + err = xenbus_printf(th, res_dir, "start", "%lx", res->start); + if (err) + goto out; + + err = xenbus_printf(th, res_dir, "end", "%lx", res->end); + if (err) + goto out; + + err = xenbus_printf(th, res_dir, "flags", "%lx", res->flags); + + out: + kfree(res_dir); + return err; +} + +#define RES_NAME_LEN 32 +static int pnpback_export_device(struct pnpback_device *pdev, char *name) +{ + struct pnp_dev *dev; + int err = 0; + char resource_name[RES_NAME_LEN]; + struct pnp_id *id; + int i; + xenbus_transaction_t trans; + + dev_dbg(&pdev->xdev->dev, "exporting pnp_dev %s\n", name); + + dev = pnpstub_get_pnp_dev(name, pdev); + if (!dev) { + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, + "Couldn't locate PNP device %s!" + "perhaps already in-use?", name); + goto out; + } + pdev->pnp_dev = dev; + + start_transaction: + err = xenbus_transaction_start(&trans); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error starting transaction to write " + "configuration for frontend!"); + goto out; + } + + /* export pnp_id */ + for (id = dev->id, i = 0; id != NULL; id = id->next, i++) { + snprintf(resource_name, RES_NAME_LEN, "id-%d", i); + err = xenbus_printf(trans, pdev->xdev->nodename, resource_name, + "%s", id->id); + if (err) + goto abort_transaction; + } + + /* export i/o ports */ + for (i = 0; i < PNP_MAX_PORT; i++) { + snprintf(resource_name, RES_NAME_LEN, "port-%d", i); + err = xenbus_write_resource(trans, pdev->xdev->nodename, + resource_name, + &dev->res.port_resource[i]); + if (err) + goto abort_transaction; + } + + /* export i/o mem */ + for (i = 0; i < PNP_MAX_MEM; i++) { + snprintf(resource_name, RES_NAME_LEN, "mem-%d", i); + err = xenbus_write_resource(trans, pdev->xdev->nodename, + resource_name, + &dev->res.mem_resource[i]); + if (err) + goto abort_transaction; + } + + /* export irq */ + for (i = 0; i < PNP_MAX_IRQ; i++) { + snprintf(resource_name, RES_NAME_LEN, "irq-%d", i); + err = xenbus_write_resource(trans, pdev->xdev->nodename, + resource_name, + &dev->res.irq_resource[i]); + if (err) + goto abort_transaction; + } + + err = xenbus_transaction_end(trans, 0); + if (err == -EAGAIN) + goto start_transaction; + else if (err) + xenbus_dev_fatal(pdev->xdev, err, + "Error completing transaction"); + + out: + return err; + + abort_transaction: + xenbus_transaction_end(trans, 1); + return err; +} + +static int pnpback_setup_backend(struct pnpback_device *pdev) +{ + /* Get configuration from xend (if available now) */ + char *device_name = NULL; + int err = 0; + + spin_lock(&pdev->lock); + + /* It's possible we could get the call to setup twice, so make sure + * we're not already connected. + */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitWait) + goto out; + + dev_dbg(&pdev->xdev->dev, "getting be setup\n"); + + err = xenbus_gather(XBT_NULL, pdev->xdev->nodename, + "device", NULL, &device_name, NULL); + if (err < 0) { + /* wait longer for configuration to appear */ + err = 0; + goto out; + } + if (!device_name || strlen(device_name) == 0) { + err = -EINVAL; + xenbus_dev_fatal(pdev->xdev, err, "Error reading device name"); + goto out; + } + + err = pnpback_export_device(pdev, device_name); + if (err) + goto out; + + err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); + if (err) + xenbus_dev_fatal(pdev->xdev, err, + "Error switching to connected state!"); + + out: + kfree(device_name); + spin_unlock(&pdev->lock); + + return err; +} + +static void pnpback_be_watch(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + struct pnpback_device *pdev = + container_of(watch, struct pnpback_device, be_watch); + + switch (xenbus_read_driver_state(pdev->xdev->nodename)) { + case XenbusStateInitWait: + pnpback_setup_backend(pdev); + break; + + default: + break; + } +} + +static int pnpback_xenbus_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err = 0; + struct pnpback_device *pdev = alloc_pdev(dev); + + if (pdev == NULL) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, + "Error allocating pnpback_device struct"); + goto out; + } + + /* wait for xend to configure us */ + err = xenbus_switch_state(dev, XenbusStateInitWait); + if (err) + goto out; + + /* watch the backend node for backend configuration information */ + err = xenbus_watch_path(dev, dev->nodename, &pdev->be_watch, + pnpback_be_watch); + if (err) + goto out; + pdev->be_watching = 1; + + /* We need to force a call to our callback here in case + * xend already configured us! + */ + pnpback_be_watch(&pdev->be_watch, NULL, 0); + + out: + return err; +} + +static int pnpback_xenbus_remove(struct xenbus_device *dev) +{ + struct pnpback_device *pdev = dev->data; + + if (pdev != NULL) + free_pdev(pdev); + + return 0; +} + +static struct xenbus_device_id xenpnp_ids[] = { + {"pnp"}, + {{0}}, +}; + +static struct xenbus_driver xenbus_pnpback_driver = { + .name = "pnpback", + .owner = THIS_MODULE, + .ids = xenpnp_ids, + .probe = pnpback_xenbus_probe, + .remove = pnpback_xenbus_remove, + .otherend_changed = pnpback_frontend_changed, +}; + +int __init pnpback_xenbus_register(void) +{ + return xenbus_register_backend(&xenbus_pnpback_driver); +} + +void __exit pnpback_xenbus_unregister(void) +{ + xenbus_unregister_driver(&xenbus_pnpback_driver); +} diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpfront/Makefile --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpfront/Makefile Thu Apr 27 19:59:04 2006 @@ -0,0 +1,7 @@ +obj-${CONFIG_XEN_PNPDEV_FRONTEND} += pnpfront.o + +pnpfront-y := protocol.o xenbus.o + +ifeq ($(CONFIG_XEN_PNP_FE_DEBUG),y) +EXTRA_CFLAGS += -DDEBUG +endif diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpfront/pnpfront.h --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpfront/pnpfront.h Thu Apr 27 19:59:04 2006 @@ -0,0 +1,42 @@ +/* + * PNP Frontend Common Data Structures & Function Declarations + * + * Author: Ryan Wilson + */ +#ifndef __XEN_PNPFRONT_H__ +#define __XEN_PNPFRONT_H__ + +#include +#include +#include +#include +#include + +struct pnpfront_device { + spinlock_t lock; + struct pnp_dev *pnp_dev; + struct xenbus_device *xdev; +}; + +int pnpfront_add_device(struct pnp_dev *dev); +void pnpfront_remove_device(struct pnp_dev *dev); + +struct pnp_dev *pnpfront_alloc_pnp_dev(void); +void pnpfront_free_pnp_dev(struct pnp_dev *dev); + +int pnpfront_register_protocol(void); +void pnpfront_unregister_protocol(void); + +static inline struct pnp_dev *get_pnp_dev(struct pnp_dev *dev) +{ + if (dev && get_device(&dev->dev)) + return dev; + return NULL; +} + +static inline void put_pnp_dev(struct pnp_dev *dev) +{ + if (dev) + put_device(&dev->dev); +} +#endif diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpfront/protocol.c --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpfront/protocol.c Thu Apr 27 19:59:04 2006 @@ -0,0 +1,92 @@ +/* + * PNP Frontend Protocol Handler - the "bus" for PNP devices from the backend + * + * Author: Ryan Wilson + */ +#include +#include +#include "pnpfront.h" + +/* This is needed because pnp_remove_device was strangely removed... It + * appears that it wasn't because its use was discouraged but rather because + * no one was using it. + */ +#include "../../pnp/base.h" + +static unsigned char next_device_id = 0; +static unsigned char device_id_overflow = 0; +static DEFINE_SPINLOCK(next_device_id_lock); + +static struct pnp_protocol pnpfront_protocol = { + .name = "Xen PNP Frontend", +}; + +struct pnp_dev *pnpfront_alloc_pnp_dev(void) +{ + struct pnp_dev *dev = NULL; + + if (!device_id_overflow) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev) { + dev->active = 1; + + spin_lock(&next_device_id_lock); + dev->number = next_device_id++; + if (next_device_id == 0) + device_id_overflow = 1; + spin_unlock(&next_device_id_lock); + } + + pr_info("pnpfront: allocated pnp_dev @ 0x%p\n", dev); + } else + printk(KERN_ERR "pnpfront: device ID overflow!\n"); + + return dev; +} + +void pnpfront_free_pnp_dev(struct pnp_dev *dev) +{ + struct pnp_id *id, *next; + + if (dev) { + dev_dbg(&dev->dev, "pnpfront: free pnp_dev\n"); + /* Free the ID list */ + id = dev->id; + while (id) { + next = id->next; + kfree(id); + id = next; + } + + kfree(dev); + } +} + +int pnpfront_add_device(struct pnp_dev *pnp_dev) +{ + int err; + pnp_dev->protocol = &pnpfront_protocol; + + err = pnp_add_device(pnp_dev); + dev_dbg(&pnp_dev->dev, "pnp_add_device returned %d\n", err); + + return err; +} + +void pnpfront_remove_device(struct pnp_dev *dev) +{ + __pnp_remove_device(dev); +} + +int pnpfront_register_protocol(void) +{ + pr_info("pnpfront: Installing Xen PNP Frontend Protocol\n"); + return pnp_register_protocol(&pnpfront_protocol); +} + +void pnpfront_unregister_protocol(void) +{ + /* clean-up PNP devices? */ + + pnp_unregister_protocol(&pnpfront_protocol); +} diff -r 4e1b8be54311 -r aea3485be101 linux-2.6-xen-sparse/drivers/xen/pnpfront/xenbus.c --- /dev/null Thu Apr 27 12:38:21 2006 +++ b/linux-2.6-xen-sparse/drivers/xen/pnpfront/xenbus.c Thu Apr 27 19:59:04 2006 @@ -0,0 +1,371 @@ +/* + * PNP Frontend Xenbus Setup - handles setup with backend + * + * Author: Ryan Wilson + */ +#include +#include +#include +#include +#include +#include "pnpfront.h" + +static void pnpfront_disconnect(struct pnpfront_device *pdev); + +static struct pnpfront_device *alloc_pdev(struct xenbus_device *xdev) +{ + struct pnpfront_device *pdev; + + pdev = kzalloc(sizeof(struct pnpfront_device), GFP_KERNEL); + if (pdev == NULL) + goto out; + + xdev->data = pdev; + pdev->xdev = xdev; + + spin_lock_init(&pdev->lock); + + dev_dbg(&xdev->dev, "Allocated pdev @ 0x%p\n", pdev); + + out: + return pdev; +} + +static void free_pdev(struct pnpfront_device *pdev) +{ + dev_dbg(&pdev->xdev->dev, "freeing pdev @ 0x%p\n", pdev); + + /* make sure pnp_dev is disconnected */ + /* we shouldn't be contending for this lock... */ + spin_lock(&pdev->lock); + pnpfront_disconnect(pdev); + spin_unlock(&pdev->lock); + + pdev->xdev->data = NULL; + + kfree(pdev); +} + +static char *join_path(const char *path1, ...) +{ + int len = strlen(path1); + char *j_path; + char *pathn; + va_list ap; + + /* determine length of new path */ + va_start(ap, path1); + pathn = va_arg(ap, char *); + while (pathn) { + len += 1 + strlen(pathn); + pathn = va_arg(ap, char *); + } + va_end(ap); + + j_path = kmalloc(len + 1, GFP_KERNEL); + if (!j_path) + return NULL; + + /* build path */ + strcpy(j_path, path1); + va_start(ap, path1); + pathn = va_arg(ap, char *); + while (pathn) { + strcat(j_path, "/"); + strcat(j_path, pathn); + pathn = va_arg(ap, char *); + } + va_end(ap); + + return j_path; +} + +static int xenbus_read_resource(xenbus_transaction_t th, const char *dir, + const char *node, struct resource *res) +{ + int err; + char *res_dir = NULL; + + res_dir = join_path(dir, node, NULL); + if (!res_dir) { + err = -ENOMEM; + goto out; + } + + err = xenbus_scanf(th, res_dir, "start", "%lx", &res->start); + if (err < 0) + goto out; + + err = xenbus_scanf(th, res_dir, "end", "%lx", &res->end); + if (err < 0) + goto out; + + err = xenbus_scanf(th, res_dir, "flags", "%lx", &res->flags); + + if (err > 0) + err = 0; + out: + kfree(res_dir); + if (err) + pr_debug("xenbus_read_resource - err %d\n", err); + return err; +} + +#define NAME_LEN 32 +static int read_pnp_ids(struct pnp_dev *dev, const char *basepath) +{ + char id_name[NAME_LEN]; + char *tmp_id; + struct pnp_id *id; + int i; + int err = 0; + + /* import pnp_id */ + for (i = 0;; i++) { + id = kzalloc(sizeof(*id), GFP_KERNEL); + + snprintf(id_name, NAME_LEN, "id-%d", i); + err = xenbus_gather(XBT_NULL, basepath, + id_name, NULL, &tmp_id, + NULL); + if (err) + break; + + if (tmp_id && strlen(tmp_id) < PNP_ID_LEN) { + strcpy(id->id, tmp_id); + pnp_add_id(id, dev); + } else { + err = -EINVAL; + break; + } + } + + /* if we found at least one entry, then a -ENOENT isn't bad */ + if (dev->id != NULL && err == -ENOENT) + err = 0; + + return err; +} + +static int read_resource_table(struct pnp_dev *dev, const char *basepath) +{ + char resource_name[NAME_LEN]; + int i; + int err = 0; + + pnp_init_resource_table(&dev->res); + + /* read i/o ports */ + for (i = 0; i < PNP_MAX_PORT; i++) { + snprintf(resource_name, NAME_LEN, "port-%d", i); + err = xenbus_read_resource(XBT_NULL, basepath, resource_name, + &dev->res.port_resource[i]); + if (err && err != -ENOENT) + goto out; + } + + /* read i/o mem */ + for (i = 0; i < PNP_MAX_MEM; i++) { + snprintf(resource_name, NAME_LEN, "mem-%d", i); + err = xenbus_read_resource(XBT_NULL, basepath, resource_name, + &dev->res.mem_resource[i]); + if (err && err != -ENOENT) + goto out; + } + + /* read irq */ + for (i = 0; i < PNP_MAX_IRQ; i++) { + snprintf(resource_name, NAME_LEN, "irq-%d", i); + err = xenbus_read_resource(XBT_NULL, basepath, resource_name, + &dev->res.irq_resource[i]); + if (err && err != -ENOENT) + goto out; + } + + if (err == -ENOENT) + err = 0; + + out: + return err; +} + +static int pnpfront_try_connect(struct pnpfront_device *pdev) +{ + int err = 0; + struct pnp_dev *dev; + + spin_lock(&pdev->lock); + dev_dbg(&pdev->xdev->dev, "pnpfront_try_connect\n"); + + /* Only connect once */ + if (xenbus_read_driver_state(pdev->xdev->nodename) != + XenbusStateInitialised) + goto out; + + dev = pnpfront_alloc_pnp_dev(); + if (!dev) { + err = -ENOMEM; + xenbus_dev_fatal(pdev->xdev, err, "Error allocating pnp_dev"); + goto out; + } + + err = read_pnp_ids(dev, pdev->xdev->otherend); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, "Error reading PNP IDs"); + goto cleanup_free_pnp_dev; + } + + err = read_resource_table(dev, pdev->xdev->otherend); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, "Error reading resources"); + goto cleanup_free_pnp_dev; + } + + err = xenbus_switch_state(pdev->xdev, XenbusStateConnected); + if (err) + goto cleanup_free_pnp_dev; + + err = pnpfront_add_device(dev); + if (err) + goto cleanup_put_pnp_dev; + dev_dbg(&dev->dev, "added device\n"); + + pdev->pnp_dev = dev; + + out: + spin_unlock(&pdev->lock); + return err; + + /* Make sure we clean up when we fail */ + cleanup_free_pnp_dev: + pnpfront_free_pnp_dev(pdev->pnp_dev); + goto out; + + cleanup_put_pnp_dev: + put_pnp_dev(pdev->pnp_dev); + goto out; +} + +/* call with pdev->lock held */ +static void pnpfront_disconnect(struct pnpfront_device *pdev) +{ + dev_dbg(&pdev->xdev->dev, "pnpfront_disconnect\n"); + if (pdev->pnp_dev) { + pnpfront_remove_device(pdev->pnp_dev); + + put_pnp_dev(pdev->pnp_dev); + pdev->pnp_dev = NULL; + } +} + +static int pnpfront_try_disconnect(struct pnpfront_device *pdev) +{ + int err = 0; + XenbusState prev_state; + + spin_lock(&pdev->lock); + dev_dbg(&pdev->xdev->dev, "pnpfront_try_disconnect\n"); + + prev_state = xenbus_read_driver_state(pdev->xdev->nodename); + + if (prev_state < XenbusStateClosing) + err = xenbus_switch_state(pdev->xdev, XenbusStateClosing); + + if (!err && prev_state == XenbusStateConnected) + pnpfront_disconnect(pdev); + + spin_unlock(&pdev->lock); + + return err; +} + +static void pnpfront_backend_changed(struct xenbus_device *xdev, + XenbusState be_state) +{ + struct pnpfront_device *pdev = xdev->data; + + switch (be_state) { + case XenbusStateClosing: + dev_warn(&xdev->dev, "backend going away!\n"); + pnpfront_try_disconnect(pdev); + break; + + case XenbusStateClosed: + dev_warn(&xdev->dev, "backend went away!\n"); + pnpfront_try_disconnect(pdev); + + device_unregister(&pdev->xdev->dev); + break; + + case XenbusStateConnected: + pnpfront_try_connect(pdev); + break; + + default: + break; + } +} + +static int pnpfront_xenbus_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + int err = 0; + struct pnpfront_device *pdev = alloc_pdev(xdev); + + if (pdev == NULL) { + err = -ENOMEM; + xenbus_dev_fatal(xdev, err, + "Error allocating pnpfront_device struct"); + goto out; + } + + err = xenbus_switch_state(xdev, XenbusStateInitialised); + if (err) { + xenbus_dev_fatal(xdev, err, + "Error switching to initialized state!"); + goto out; + } + + err = pnpfront_try_connect(pdev); + + out: + return err; +} + +static int pnpfront_xenbus_remove(struct xenbus_device *xdev) +{ + if (xdev->data) + free_pdev(xdev->data); + + return 0; +} + +static struct xenbus_device_id xenpnp_ids[] = { + {"pnp"}, + {{0}}, +}; + +static struct xenbus_driver xenbus_pnpfront_driver = { + .name = "pnpfront", + .owner = THIS_MODULE, + .ids = xenpnp_ids, + .probe = pnpfront_xenbus_probe, + .remove = pnpfront_xenbus_remove, + .otherend_changed = pnpfront_backend_changed, +}; + +static int __init pnpfront_init(void) +{ + int err = 0; + + err = pnpfront_register_protocol(); + if (err < 0) + goto out; + + err = xenbus_register_frontend(&xenbus_pnpfront_driver); + + out: + return err; +} + +subsys_initcall(pnpfront_init);