This patch adds some wrapper functions in the pnp layer. The intent is to allow memory address space claims by devices which are descendants (a child or grandchild of) a device which is already part of the pnp layer. This allows a device to make a resource claim that doesn't conflict with its "aunts" and "uncles." This is useful in a Hyper-V VM because some paravirtual "devices" need memory-mapped I/O space, and their aunts and uncles can be PCI devices. Furthermore, the hypervisor expresses the possible memory address combinations for the devices in the VM through the ACPI namespace. The paravirtual devices need to suballocate from the ACPI nodes, and they need to avoid conflicting with choices that the Linux PCI code makes about the PCI devices in the VM. It might seem like this should be done in the platform layer rather than the pnp layer, but the platform layer assumes that the configuration of the devices in the machine are static, or at least expressed by firmware in a static fashion. The nature of a Hyper-V VM is that new devices can be added while the machine is running, and the potential configurations for them are expressed as part of the paravirtual communications channel. This much more naturally aligns with the pnp layer. Signed-off-by: Jake Oshins <jakeo@xxxxxxxxxxxxx> --- drivers/pnp/Makefile | 2 +- drivers/pnp/base.h | 2 + drivers/pnp/core.c | 1 + drivers/pnp/descendant.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pnp.h | 23 ++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 drivers/pnp/descendant.c diff --git a/drivers/pnp/Makefile b/drivers/pnp/Makefile index bfba893..de315f9 100644 --- a/drivers/pnp/Makefile +++ b/drivers/pnp/Makefile @@ -4,7 +4,7 @@ obj-y := pnp.o -pnp-y := core.o card.o driver.o resource.o manager.o support.o interface.o quirks.o +pnp-y := core.o card.o driver.o resource.o manager.o support.o interface.o quirks.o descendant.o obj-$(CONFIG_PNPACPI) += pnpacpi/ obj-$(CONFIG_PNPBIOS) += pnpbios/ diff --git a/drivers/pnp/base.h b/drivers/pnp/base.h index c8873b0..0b86437 100644 --- a/drivers/pnp/base.h +++ b/drivers/pnp/base.h @@ -175,6 +175,8 @@ struct pnp_resource *pnp_add_bus_resource(struct pnp_dev *dev, resource_size_t start, resource_size_t end); +int __init pnpdescendant_init(void); + extern int pnp_debug; #if defined(CONFIG_PNP_DEBUG_MESSAGES) diff --git a/drivers/pnp/core.c b/drivers/pnp/core.c index cb6ce42..fc32912 100644 --- a/drivers/pnp/core.c +++ b/drivers/pnp/core.c @@ -212,6 +212,7 @@ void __pnp_remove_device(struct pnp_dev *dev) static int __init pnp_init(void) { + pnpdescendant_init(); return bus_register(&pnp_bus_type); } diff --git a/drivers/pnp/descendant.c b/drivers/pnp/descendant.c new file mode 100644 index 0000000..c25e40b --- /dev/null +++ b/drivers/pnp/descendant.c @@ -0,0 +1,117 @@ +/* + * descendant.c - Resource management for devices which are descendants + * (children or grandchildren of) devices which directly allocate resources, + * i.e. devices enumerated by ACPI, PCI, PCMCIA, ISAPNP or PNPBIOS + * + * Copyright (C) 2015 Microsoft + * Jake Oshins <jakeo@xxxxxxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/pnp.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include "base.h" + +int descendant_get_resources(struct pnp_dev *dev) +{ + return 0; +} + +int descendant_set_resources(struct pnp_dev *dev) +{ + return 0; +} + +int descendant_disable_resources(struct pnp_dev *dev) +{ + return 0; +} + +struct pnp_protocol descendant_protocol = { + .name = "Plug and Play descendants", + .get = descendant_get_resources, + .set = descendant_set_resources, + .disable = descendant_disable_resources, +}; + +/* + * This adds an option to the descendant device for memory address space. +*/ +int pnp_descendant_memory_option(struct pnp_dev *dev, resource_size_t min, + resource_size_t max, resource_size_t alignment, + resource_size_t size, unsigned char flags) +{ + return pnp_register_mem_resource(dev, 0, min, max, alignment, size, + flags); +} +EXPORT_SYMBOL(pnp_descendant_memory_option); + +/* + * This creates an entry in the plug-and-play layer for this device, which + * is a descendant of a device already in the plug-and-play layer. +*/ +int pnp_add_descendant(struct pnp_dev *dev) +{ + int error; + + error = pnp_add_device(dev); + if (error) { + put_device(&dev->dev); + return error; + } + + return 0; +} +EXPORT_SYMBOL(pnp_add_descendant); + +void pnp_remove_descendant(struct pnp_dev *dev) +{ + __pnp_remove_device(dev); +} +EXPORT_SYMBOL(pnp_remove_descendant); + +int __init pnpdescendant_init(void) +{ + return pnp_register_protocol(&descendant_protocol); +} + +struct pnp_dev *alloc_pnp_descendant(const char *pnpid) +{ + struct pnp_dev *dev; + + dev = pnp_alloc_dev(&descendant_protocol, 0, pnpid); + + if (!dev) + return NULL; + + pnp_init_resources(dev); + dev->capabilities |= PNP_CONFIGURABLE; + dev->capabilities |= PNP_READ; + dev->capabilities |= PNP_WRITE; + dev->capabilities |= PNP_DISABLE; + dev->capabilities |= PNP_REMOVABLE; + + return dev; +} +EXPORT_SYMBOL(alloc_pnp_descendant); + +void free_pnp_descendant(struct pnp_dev *dev) +{ + kfree(dev); +} +EXPORT_SYMBOL(free_pnp_descendant); + diff --git a/include/linux/pnp.h b/include/linux/pnp.h index 6512e9c..269a7af 100644 --- a/include/linux/pnp.h +++ b/include/linux/pnp.h @@ -477,6 +477,15 @@ int compare_pnp_id(struct pnp_id *pos, const char *id); int pnp_register_driver(struct pnp_driver *drv); void pnp_unregister_driver(struct pnp_driver *drv); +/* descendant support */ +struct pnp_dev *alloc_pnp_descendant(const char *pnpid); +void free_pnp_descendant(struct pnp_dev *dev); +int pnp_add_descendant(struct pnp_dev *dev); +void pnp_remove_descendant(struct pnp_dev *dev); +int pnp_descendant_memory_option(struct pnp_dev *dev, resource_size_t min, + resource_size_t max, resource_size_t alignment, + resource_size_t size, unsigned char flags); + #else /* device management */ @@ -508,6 +517,20 @@ static inline int compare_pnp_id(struct pnp_id *pos, const char *id) { return -E static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; } static inline void pnp_unregister_driver(struct pnp_driver *drv) { } +/* descendant support */ +static inline struct pnp_dev *alloc_pnp_descendant(const char *pnpid) +{ return NULL; } +static inline void free_pnp_descendant(struct pnp_dev *dev) { } +static inline int pnp_add_descendant(struct pnp_dev *dev) { return -ENODEV; } +static inline void pnp_remove_descendant(struct pnp_dev *dev) { } +static inline int pnp_descendant_memory_option(struct pnp_dev *dev, + resource_size_t min, + resource_size_t max, + resource_size_t alignment, + resource_size_t size, + unsigned char flags) +{ return -ENODEV; } + #endif /* CONFIG_PNP */ #endif /* _LINUX_PNP_H */ -- 1.9.1 _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel