Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

uwb: add the umc bus

The UMC bus is used for the capabilities exposed by a UWB Multi-interface
Controller as described in the WHCI specification.

Signed-off-by: David Vrabel <david.vrabel@csr.com>

authored by

David Vrabel and committed by
David Vrabel
da389eac 2f86c3e6

+553
+6
drivers/uwb/Makefile
··· 1 1 obj-$(CONFIG_UWB) += uwb.o 2 + obj-$(CONFIG_UWB_WHCI) += umc.o 2 3 3 4 uwb-objs := \ 4 5 address.o \ ··· 19 18 scan.o \ 20 19 uwb-debug.o \ 21 20 uwbd.o 21 + 22 + umc-objs := \ 23 + umc-bus.o \ 24 + umc-dev.o \ 25 + umc-drv.o
+218
drivers/uwb/umc-bus.c
··· 1 + /* 2 + * Bus for UWB Multi-interface Controller capabilities. 3 + * 4 + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 5 + * 6 + * This file is released under the GNU GPL v2. 7 + */ 8 + #include <linux/kernel.h> 9 + #include <linux/sysfs.h> 10 + #include <linux/workqueue.h> 11 + #include <linux/uwb/umc.h> 12 + #include <linux/pci.h> 13 + 14 + static int umc_bus_unbind_helper(struct device *dev, void *data) 15 + { 16 + struct device *parent = data; 17 + 18 + if (dev->parent == parent && dev->driver) 19 + device_release_driver(dev); 20 + return 0; 21 + } 22 + 23 + /** 24 + * umc_controller_reset - reset the whole UMC controller 25 + * @umc: the UMC device for the radio controller. 26 + * 27 + * Drivers will be unbound from all UMC devices belonging to the 28 + * controller and then the radio controller will be rebound. The 29 + * radio controller is expected to do a full hardware reset when it is 30 + * probed. 31 + * 32 + * If this is called while a probe() or remove() is in progress it 33 + * will return -EAGAIN and not perform the reset. 34 + */ 35 + int umc_controller_reset(struct umc_dev *umc) 36 + { 37 + struct device *parent = umc->dev.parent; 38 + int ret; 39 + 40 + if (down_trylock(&parent->sem)) 41 + return -EAGAIN; 42 + bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper); 43 + ret = device_attach(&umc->dev); 44 + if (ret == 1) 45 + ret = 0; 46 + up(&parent->sem); 47 + 48 + return ret; 49 + } 50 + EXPORT_SYMBOL_GPL(umc_controller_reset); 51 + 52 + /** 53 + * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device. 54 + * @umc_drv: umc driver with match_data pointing to a zero-terminated 55 + * table of pci_device_id's. 56 + * @umc: umc device whose parent is to be matched. 57 + */ 58 + int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc) 59 + { 60 + const struct pci_device_id *id_table = umc_drv->match_data; 61 + struct pci_dev *pci; 62 + 63 + if (umc->dev.parent->bus != &pci_bus_type) 64 + return 0; 65 + 66 + pci = to_pci_dev(umc->dev.parent); 67 + return pci_match_id(id_table, pci) != NULL; 68 + } 69 + EXPORT_SYMBOL_GPL(umc_match_pci_id); 70 + 71 + static int umc_bus_rescan_helper(struct device *dev, void *data) 72 + { 73 + int ret = 0; 74 + 75 + if (!dev->driver) 76 + ret = device_attach(dev); 77 + 78 + return ret < 0 ? ret : 0; 79 + } 80 + 81 + static void umc_bus_rescan(void) 82 + { 83 + int err; 84 + 85 + /* 86 + * We can't use bus_rescan_devices() here as it deadlocks when 87 + * it tries to retake the dev->parent semaphore. 88 + */ 89 + err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper); 90 + if (err < 0) 91 + printk(KERN_WARNING "%s: rescan of bus failed: %d\n", 92 + KBUILD_MODNAME, err); 93 + } 94 + 95 + static int umc_bus_match(struct device *dev, struct device_driver *drv) 96 + { 97 + struct umc_dev *umc = to_umc_dev(dev); 98 + struct umc_driver *umc_driver = to_umc_driver(drv); 99 + 100 + if (umc->cap_id == umc_driver->cap_id) { 101 + if (umc_driver->match) 102 + return umc_driver->match(umc_driver, umc); 103 + else 104 + return 1; 105 + } 106 + return 0; 107 + } 108 + 109 + static int umc_device_probe(struct device *dev) 110 + { 111 + struct umc_dev *umc; 112 + struct umc_driver *umc_driver; 113 + int err; 114 + 115 + umc_driver = to_umc_driver(dev->driver); 116 + umc = to_umc_dev(dev); 117 + 118 + get_device(dev); 119 + err = umc_driver->probe(umc); 120 + if (err) 121 + put_device(dev); 122 + else 123 + umc_bus_rescan(); 124 + 125 + return err; 126 + } 127 + 128 + static int umc_device_remove(struct device *dev) 129 + { 130 + struct umc_dev *umc; 131 + struct umc_driver *umc_driver; 132 + 133 + umc_driver = to_umc_driver(dev->driver); 134 + umc = to_umc_dev(dev); 135 + 136 + umc_driver->remove(umc); 137 + put_device(dev); 138 + return 0; 139 + } 140 + 141 + static int umc_device_suspend(struct device *dev, pm_message_t state) 142 + { 143 + struct umc_dev *umc; 144 + struct umc_driver *umc_driver; 145 + int err = 0; 146 + 147 + umc = to_umc_dev(dev); 148 + 149 + if (dev->driver) { 150 + umc_driver = to_umc_driver(dev->driver); 151 + if (umc_driver->suspend) 152 + err = umc_driver->suspend(umc, state); 153 + } 154 + return err; 155 + } 156 + 157 + static int umc_device_resume(struct device *dev) 158 + { 159 + struct umc_dev *umc; 160 + struct umc_driver *umc_driver; 161 + int err = 0; 162 + 163 + umc = to_umc_dev(dev); 164 + 165 + if (dev->driver) { 166 + umc_driver = to_umc_driver(dev->driver); 167 + if (umc_driver->resume) 168 + err = umc_driver->resume(umc); 169 + } 170 + return err; 171 + } 172 + 173 + static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf) 174 + { 175 + struct umc_dev *umc = to_umc_dev(dev); 176 + 177 + return sprintf(buf, "0x%02x\n", umc->cap_id); 178 + } 179 + 180 + static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) 181 + { 182 + struct umc_dev *umc = to_umc_dev(dev); 183 + 184 + return sprintf(buf, "0x%04x\n", umc->version); 185 + } 186 + 187 + static struct device_attribute umc_dev_attrs[] = { 188 + __ATTR_RO(capability_id), 189 + __ATTR_RO(version), 190 + __ATTR_NULL, 191 + }; 192 + 193 + struct bus_type umc_bus_type = { 194 + .name = "umc", 195 + .match = umc_bus_match, 196 + .probe = umc_device_probe, 197 + .remove = umc_device_remove, 198 + .suspend = umc_device_suspend, 199 + .resume = umc_device_resume, 200 + .dev_attrs = umc_dev_attrs, 201 + }; 202 + EXPORT_SYMBOL_GPL(umc_bus_type); 203 + 204 + static int __init umc_bus_init(void) 205 + { 206 + return bus_register(&umc_bus_type); 207 + } 208 + module_init(umc_bus_init); 209 + 210 + static void __exit umc_bus_exit(void) 211 + { 212 + bus_unregister(&umc_bus_type); 213 + } 214 + module_exit(umc_bus_exit); 215 + 216 + MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus"); 217 + MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); 218 + MODULE_LICENSE("GPL");
+104
drivers/uwb/umc-dev.c
··· 1 + /* 2 + * UWB Multi-interface Controller device management. 3 + * 4 + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 5 + * 6 + * This file is released under the GNU GPL v2. 7 + */ 8 + #include <linux/kernel.h> 9 + #include <linux/uwb/umc.h> 10 + #define D_LOCAL 0 11 + #include <linux/uwb/debug.h> 12 + 13 + static void umc_device_release(struct device *dev) 14 + { 15 + struct umc_dev *umc = to_umc_dev(dev); 16 + 17 + kfree(umc); 18 + } 19 + 20 + /** 21 + * umc_device_create - allocate a child UMC device 22 + * @parent: parent of the new UMC device. 23 + * @n: index of the new device. 24 + * 25 + * The new UMC device will have a bus ID of the parent with '-n' 26 + * appended. 27 + */ 28 + struct umc_dev *umc_device_create(struct device *parent, int n) 29 + { 30 + struct umc_dev *umc; 31 + 32 + umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL); 33 + if (umc) { 34 + snprintf(umc->dev.bus_id, sizeof(umc->dev.bus_id), "%s-%d", 35 + parent->bus_id, n); 36 + umc->dev.parent = parent; 37 + umc->dev.bus = &umc_bus_type; 38 + umc->dev.release = umc_device_release; 39 + 40 + umc->dev.dma_mask = parent->dma_mask; 41 + } 42 + return umc; 43 + } 44 + EXPORT_SYMBOL_GPL(umc_device_create); 45 + 46 + /** 47 + * umc_device_register - register a UMC device 48 + * @umc: pointer to the UMC device 49 + * 50 + * The memory resource for the UMC device is acquired and the device 51 + * registered with the system. 52 + */ 53 + int umc_device_register(struct umc_dev *umc) 54 + { 55 + int err; 56 + 57 + d_fnstart(3, &umc->dev, "(umc_dev %p)\n", umc); 58 + 59 + err = request_resource(umc->resource.parent, &umc->resource); 60 + if (err < 0) { 61 + dev_err(&umc->dev, "can't allocate resource range " 62 + "%016Lx to %016Lx: %d\n", 63 + (unsigned long long)umc->resource.start, 64 + (unsigned long long)umc->resource.end, 65 + err); 66 + goto error_request_resource; 67 + } 68 + 69 + err = device_register(&umc->dev); 70 + if (err < 0) 71 + goto error_device_register; 72 + d_fnend(3, &umc->dev, "(umc_dev %p) = 0\n", umc); 73 + return 0; 74 + 75 + error_device_register: 76 + release_resource(&umc->resource); 77 + error_request_resource: 78 + d_fnend(3, &umc->dev, "(umc_dev %p) = %d\n", umc, err); 79 + return err; 80 + } 81 + EXPORT_SYMBOL_GPL(umc_device_register); 82 + 83 + /** 84 + * umc_device_unregister - unregister a UMC device 85 + * @umc: pointer to the UMC device 86 + * 87 + * First we unregister the device, make sure the driver can do it's 88 + * resource release thing and then we try to release any left over 89 + * resources. We take a ref to the device, to make sure it doesn't 90 + * dissapear under our feet. 91 + */ 92 + void umc_device_unregister(struct umc_dev *umc) 93 + { 94 + struct device *dev; 95 + if (!umc) 96 + return; 97 + dev = get_device(&umc->dev); 98 + d_fnstart(3, dev, "(umc_dev %p)\n", umc); 99 + device_unregister(&umc->dev); 100 + release_resource(&umc->resource); 101 + d_fnend(3, dev, "(umc_dev %p) = void\n", umc); 102 + put_device(dev); 103 + } 104 + EXPORT_SYMBOL_GPL(umc_device_unregister);
+31
drivers/uwb/umc-drv.c
··· 1 + /* 2 + * UWB Multi-interface Controller driver management. 3 + * 4 + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 5 + * 6 + * This file is released under the GNU GPL v2. 7 + */ 8 + #include <linux/kernel.h> 9 + #include <linux/uwb/umc.h> 10 + 11 + int __umc_driver_register(struct umc_driver *umc_drv, struct module *module, 12 + const char *mod_name) 13 + { 14 + umc_drv->driver.name = umc_drv->name; 15 + umc_drv->driver.owner = module; 16 + umc_drv->driver.mod_name = mod_name; 17 + umc_drv->driver.bus = &umc_bus_type; 18 + 19 + return driver_register(&umc_drv->driver); 20 + } 21 + EXPORT_SYMBOL_GPL(__umc_driver_register); 22 + 23 + /** 24 + * umc_driver_register - unregister a UMC capabiltity driver. 25 + * @umc_drv: pointer to the driver. 26 + */ 27 + void umc_driver_unregister(struct umc_driver *umc_drv) 28 + { 29 + driver_unregister(&umc_drv->driver); 30 + } 31 + EXPORT_SYMBOL_GPL(umc_driver_unregister);
+194
include/linux/uwb/umc.h
··· 1 + /* 2 + * UWB Multi-interface Controller support. 3 + * 4 + * Copyright (C) 2007 Cambridge Silicon Radio Ltd. 5 + * 6 + * This file is released under the GPLv2 7 + * 8 + * UMC (UWB Multi-interface Controller) capabilities (e.g., radio 9 + * controller, host controller) are presented as devices on the "umc" 10 + * bus. 11 + * 12 + * The radio controller is not strictly a UMC capability but it's 13 + * useful to present it as such. 14 + * 15 + * References: 16 + * 17 + * [WHCI] Wireless Host Controller Interface Specification for 18 + * Certified Wireless Universal Serial Bus, revision 0.95. 19 + * 20 + * How this works is kind of convoluted but simple. The whci.ko driver 21 + * loads when WHCI devices are detected. These WHCI devices expose 22 + * many devices in the same PCI function (they couldn't have reused 23 + * functions, no), so for each PCI function that exposes these many 24 + * devices, whci ceates a umc_dev [whci_probe() -> whci_add_cap()] 25 + * with umc_device_create() and adds it to the bus with 26 + * umc_device_register(). 27 + * 28 + * umc_device_register() calls device_register() which will push the 29 + * bus management code to load your UMC driver's somehting_probe() 30 + * that you have registered for that capability code. 31 + * 32 + * Now when the WHCI device is removed, whci_remove() will go over 33 + * each umc_dev assigned to each of the PCI function's capabilities 34 + * and through whci_del_cap() call umc_device_unregister() each 35 + * created umc_dev. Of course, if you are bound to the device, your 36 + * driver's something_remove() will be called. 37 + */ 38 + 39 + #ifndef _LINUX_UWB_UMC_H_ 40 + #define _LINUX_UWB_UMC_H_ 41 + 42 + #include <linux/device.h> 43 + #include <linux/pci.h> 44 + 45 + /* 46 + * UMC capability IDs. 47 + * 48 + * 0x00 is reserved so use it for the radio controller device. 49 + * 50 + * [WHCI] table 2-8 51 + */ 52 + #define UMC_CAP_ID_WHCI_RC 0x00 /* radio controller */ 53 + #define UMC_CAP_ID_WHCI_WUSB_HC 0x01 /* WUSB host controller */ 54 + 55 + /** 56 + * struct umc_dev - UMC capability device 57 + * 58 + * @version: version of the specification this capability conforms to. 59 + * @cap_id: capability ID. 60 + * @bar: PCI Bar (64 bit) where the resource lies 61 + * @resource: register space resource. 62 + * @irq: interrupt line. 63 + */ 64 + struct umc_dev { 65 + u16 version; 66 + u8 cap_id; 67 + u8 bar; 68 + struct resource resource; 69 + unsigned irq; 70 + struct device dev; 71 + }; 72 + 73 + #define to_umc_dev(d) container_of(d, struct umc_dev, dev) 74 + 75 + /** 76 + * struct umc_driver - UMC capability driver 77 + * @cap_id: supported capability ID. 78 + * @match: driver specific capability matching function. 79 + * @match_data: driver specific data for match() (e.g., a 80 + * table of pci_device_id's if umc_match_pci_id() is used). 81 + */ 82 + struct umc_driver { 83 + char *name; 84 + u8 cap_id; 85 + int (*match)(struct umc_driver *, struct umc_dev *); 86 + const void *match_data; 87 + 88 + int (*probe)(struct umc_dev *); 89 + void (*remove)(struct umc_dev *); 90 + int (*suspend)(struct umc_dev *, pm_message_t state); 91 + int (*resume)(struct umc_dev *); 92 + 93 + struct device_driver driver; 94 + }; 95 + 96 + #define to_umc_driver(d) container_of(d, struct umc_driver, driver) 97 + 98 + extern struct bus_type umc_bus_type; 99 + 100 + struct umc_dev *umc_device_create(struct device *parent, int n); 101 + int __must_check umc_device_register(struct umc_dev *umc); 102 + void umc_device_unregister(struct umc_dev *umc); 103 + 104 + int __must_check __umc_driver_register(struct umc_driver *umc_drv, 105 + struct module *mod, 106 + const char *mod_name); 107 + 108 + /** 109 + * umc_driver_register - register a UMC capabiltity driver. 110 + * @umc_drv: pointer to the driver. 111 + */ 112 + static inline int __must_check umc_driver_register(struct umc_driver *umc_drv) 113 + { 114 + return __umc_driver_register(umc_drv, THIS_MODULE, KBUILD_MODNAME); 115 + } 116 + void umc_driver_unregister(struct umc_driver *umc_drv); 117 + 118 + /* 119 + * Utility function you can use to match (umc_driver->match) against a 120 + * null-terminated array of 'struct pci_device_id' in 121 + * umc_driver->match_data. 122 + */ 123 + int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc); 124 + 125 + /** 126 + * umc_parent_pci_dev - return the UMC's parent PCI device or NULL if none 127 + * @umc_dev: UMC device whose parent PCI device we are looking for 128 + * 129 + * DIRTY!!! DON'T RELY ON THIS 130 + * 131 + * FIXME: This is as dirty as it gets, but we need some way to check 132 + * the correct type of umc_dev->parent (so that for example, we can 133 + * cast to pci_dev). Casting to pci_dev is necesary because at some 134 + * point we need to request resources from the device. Mapping is 135 + * easily over come (ioremap and stuff are bus agnostic), but hooking 136 + * up to some error handlers (such as pci error handlers) might need 137 + * this. 138 + * 139 + * THIS might (probably will) be removed in the future, so don't count 140 + * on it. 141 + */ 142 + static inline struct pci_dev *umc_parent_pci_dev(struct umc_dev *umc_dev) 143 + { 144 + struct pci_dev *pci_dev = NULL; 145 + if (umc_dev->dev.parent->bus == &pci_bus_type) 146 + pci_dev = to_pci_dev(umc_dev->dev.parent); 147 + return pci_dev; 148 + } 149 + 150 + /** 151 + * umc_dev_get() - reference a UMC device. 152 + * @umc_dev: Pointer to UMC device. 153 + * 154 + * NOTE: we are assuming in this whole scheme that the parent device 155 + * is referenced at _probe() time and unreferenced at _remove() 156 + * time by the parent's subsystem. 157 + */ 158 + static inline struct umc_dev *umc_dev_get(struct umc_dev *umc_dev) 159 + { 160 + get_device(&umc_dev->dev); 161 + return umc_dev; 162 + } 163 + 164 + /** 165 + * umc_dev_put() - unreference a UMC device. 166 + * @umc_dev: Pointer to UMC device. 167 + */ 168 + static inline void umc_dev_put(struct umc_dev *umc_dev) 169 + { 170 + put_device(&umc_dev->dev); 171 + } 172 + 173 + /** 174 + * umc_set_drvdata - set UMC device's driver data. 175 + * @umc_dev: Pointer to UMC device. 176 + * @data: Data to set. 177 + */ 178 + static inline void umc_set_drvdata(struct umc_dev *umc_dev, void *data) 179 + { 180 + dev_set_drvdata(&umc_dev->dev, data); 181 + } 182 + 183 + /** 184 + * umc_get_drvdata - recover UMC device's driver data. 185 + * @umc_dev: Pointer to UMC device. 186 + */ 187 + static inline void *umc_get_drvdata(struct umc_dev *umc_dev) 188 + { 189 + return dev_get_drvdata(&umc_dev->dev); 190 + } 191 + 192 + int umc_controller_reset(struct umc_dev *umc); 193 + 194 + #endif /* #ifndef _LINUX_UWB_UMC_H_ */