···11+/*22+ * Bus for UWB Multi-interface Controller capabilities.33+ *44+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.55+ *66+ * This file is released under the GNU GPL v2.77+ */88+#include <linux/kernel.h>99+#include <linux/sysfs.h>1010+#include <linux/workqueue.h>1111+#include <linux/uwb/umc.h>1212+#include <linux/pci.h>1313+1414+static int umc_bus_unbind_helper(struct device *dev, void *data)1515+{1616+ struct device *parent = data;1717+1818+ if (dev->parent == parent && dev->driver)1919+ device_release_driver(dev);2020+ return 0;2121+}2222+2323+/**2424+ * umc_controller_reset - reset the whole UMC controller2525+ * @umc: the UMC device for the radio controller.2626+ *2727+ * Drivers will be unbound from all UMC devices belonging to the2828+ * controller and then the radio controller will be rebound. The2929+ * radio controller is expected to do a full hardware reset when it is3030+ * probed.3131+ *3232+ * If this is called while a probe() or remove() is in progress it3333+ * will return -EAGAIN and not perform the reset.3434+ */3535+int umc_controller_reset(struct umc_dev *umc)3636+{3737+ struct device *parent = umc->dev.parent;3838+ int ret;3939+4040+ if (down_trylock(&parent->sem))4141+ return -EAGAIN;4242+ bus_for_each_dev(&umc_bus_type, NULL, parent, umc_bus_unbind_helper);4343+ ret = device_attach(&umc->dev);4444+ if (ret == 1)4545+ ret = 0;4646+ up(&parent->sem);4747+4848+ return ret;4949+}5050+EXPORT_SYMBOL_GPL(umc_controller_reset);5151+5252+/**5353+ * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.5454+ * @umc_drv: umc driver with match_data pointing to a zero-terminated5555+ * table of pci_device_id's.5656+ * @umc: umc device whose parent is to be matched.5757+ */5858+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)5959+{6060+ const struct pci_device_id *id_table = umc_drv->match_data;6161+ struct pci_dev *pci;6262+6363+ if (umc->dev.parent->bus != &pci_bus_type)6464+ return 0;6565+6666+ pci = to_pci_dev(umc->dev.parent);6767+ return pci_match_id(id_table, pci) != NULL;6868+}6969+EXPORT_SYMBOL_GPL(umc_match_pci_id);7070+7171+static int umc_bus_rescan_helper(struct device *dev, void *data)7272+{7373+ int ret = 0;7474+7575+ if (!dev->driver)7676+ ret = device_attach(dev);7777+7878+ return ret < 0 ? ret : 0;7979+}8080+8181+static void umc_bus_rescan(void)8282+{8383+ int err;8484+8585+ /*8686+ * We can't use bus_rescan_devices() here as it deadlocks when8787+ * it tries to retake the dev->parent semaphore.8888+ */8989+ err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper);9090+ if (err < 0)9191+ printk(KERN_WARNING "%s: rescan of bus failed: %d\n",9292+ KBUILD_MODNAME, err);9393+}9494+9595+static int umc_bus_match(struct device *dev, struct device_driver *drv)9696+{9797+ struct umc_dev *umc = to_umc_dev(dev);9898+ struct umc_driver *umc_driver = to_umc_driver(drv);9999+100100+ if (umc->cap_id == umc_driver->cap_id) {101101+ if (umc_driver->match)102102+ return umc_driver->match(umc_driver, umc);103103+ else104104+ return 1;105105+ }106106+ return 0;107107+}108108+109109+static int umc_device_probe(struct device *dev)110110+{111111+ struct umc_dev *umc;112112+ struct umc_driver *umc_driver;113113+ int err;114114+115115+ umc_driver = to_umc_driver(dev->driver);116116+ umc = to_umc_dev(dev);117117+118118+ get_device(dev);119119+ err = umc_driver->probe(umc);120120+ if (err)121121+ put_device(dev);122122+ else123123+ umc_bus_rescan();124124+125125+ return err;126126+}127127+128128+static int umc_device_remove(struct device *dev)129129+{130130+ struct umc_dev *umc;131131+ struct umc_driver *umc_driver;132132+133133+ umc_driver = to_umc_driver(dev->driver);134134+ umc = to_umc_dev(dev);135135+136136+ umc_driver->remove(umc);137137+ put_device(dev);138138+ return 0;139139+}140140+141141+static int umc_device_suspend(struct device *dev, pm_message_t state)142142+{143143+ struct umc_dev *umc;144144+ struct umc_driver *umc_driver;145145+ int err = 0;146146+147147+ umc = to_umc_dev(dev);148148+149149+ if (dev->driver) {150150+ umc_driver = to_umc_driver(dev->driver);151151+ if (umc_driver->suspend)152152+ err = umc_driver->suspend(umc, state);153153+ }154154+ return err;155155+}156156+157157+static int umc_device_resume(struct device *dev)158158+{159159+ struct umc_dev *umc;160160+ struct umc_driver *umc_driver;161161+ int err = 0;162162+163163+ umc = to_umc_dev(dev);164164+165165+ if (dev->driver) {166166+ umc_driver = to_umc_driver(dev->driver);167167+ if (umc_driver->resume)168168+ err = umc_driver->resume(umc);169169+ }170170+ return err;171171+}172172+173173+static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)174174+{175175+ struct umc_dev *umc = to_umc_dev(dev);176176+177177+ return sprintf(buf, "0x%02x\n", umc->cap_id);178178+}179179+180180+static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)181181+{182182+ struct umc_dev *umc = to_umc_dev(dev);183183+184184+ return sprintf(buf, "0x%04x\n", umc->version);185185+}186186+187187+static struct device_attribute umc_dev_attrs[] = {188188+ __ATTR_RO(capability_id),189189+ __ATTR_RO(version),190190+ __ATTR_NULL,191191+};192192+193193+struct bus_type umc_bus_type = {194194+ .name = "umc",195195+ .match = umc_bus_match,196196+ .probe = umc_device_probe,197197+ .remove = umc_device_remove,198198+ .suspend = umc_device_suspend,199199+ .resume = umc_device_resume,200200+ .dev_attrs = umc_dev_attrs,201201+};202202+EXPORT_SYMBOL_GPL(umc_bus_type);203203+204204+static int __init umc_bus_init(void)205205+{206206+ return bus_register(&umc_bus_type);207207+}208208+module_init(umc_bus_init);209209+210210+static void __exit umc_bus_exit(void)211211+{212212+ bus_unregister(&umc_bus_type);213213+}214214+module_exit(umc_bus_exit);215215+216216+MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");217217+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");218218+MODULE_LICENSE("GPL");
+104
drivers/uwb/umc-dev.c
···11+/*22+ * UWB Multi-interface Controller device management.33+ *44+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.55+ *66+ * This file is released under the GNU GPL v2.77+ */88+#include <linux/kernel.h>99+#include <linux/uwb/umc.h>1010+#define D_LOCAL 01111+#include <linux/uwb/debug.h>1212+1313+static void umc_device_release(struct device *dev)1414+{1515+ struct umc_dev *umc = to_umc_dev(dev);1616+1717+ kfree(umc);1818+}1919+2020+/**2121+ * umc_device_create - allocate a child UMC device2222+ * @parent: parent of the new UMC device.2323+ * @n: index of the new device.2424+ *2525+ * The new UMC device will have a bus ID of the parent with '-n'2626+ * appended.2727+ */2828+struct umc_dev *umc_device_create(struct device *parent, int n)2929+{3030+ struct umc_dev *umc;3131+3232+ umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL);3333+ if (umc) {3434+ snprintf(umc->dev.bus_id, sizeof(umc->dev.bus_id), "%s-%d",3535+ parent->bus_id, n);3636+ umc->dev.parent = parent;3737+ umc->dev.bus = &umc_bus_type;3838+ umc->dev.release = umc_device_release;3939+4040+ umc->dev.dma_mask = parent->dma_mask;4141+ }4242+ return umc;4343+}4444+EXPORT_SYMBOL_GPL(umc_device_create);4545+4646+/**4747+ * umc_device_register - register a UMC device4848+ * @umc: pointer to the UMC device4949+ *5050+ * The memory resource for the UMC device is acquired and the device5151+ * registered with the system.5252+ */5353+int umc_device_register(struct umc_dev *umc)5454+{5555+ int err;5656+5757+ d_fnstart(3, &umc->dev, "(umc_dev %p)\n", umc);5858+5959+ err = request_resource(umc->resource.parent, &umc->resource);6060+ if (err < 0) {6161+ dev_err(&umc->dev, "can't allocate resource range "6262+ "%016Lx to %016Lx: %d\n",6363+ (unsigned long long)umc->resource.start,6464+ (unsigned long long)umc->resource.end,6565+ err);6666+ goto error_request_resource;6767+ }6868+6969+ err = device_register(&umc->dev);7070+ if (err < 0)7171+ goto error_device_register;7272+ d_fnend(3, &umc->dev, "(umc_dev %p) = 0\n", umc);7373+ return 0;7474+7575+error_device_register:7676+ release_resource(&umc->resource);7777+error_request_resource:7878+ d_fnend(3, &umc->dev, "(umc_dev %p) = %d\n", umc, err);7979+ return err;8080+}8181+EXPORT_SYMBOL_GPL(umc_device_register);8282+8383+/**8484+ * umc_device_unregister - unregister a UMC device8585+ * @umc: pointer to the UMC device8686+ *8787+ * First we unregister the device, make sure the driver can do it's8888+ * resource release thing and then we try to release any left over8989+ * resources. We take a ref to the device, to make sure it doesn't9090+ * dissapear under our feet.9191+ */9292+void umc_device_unregister(struct umc_dev *umc)9393+{9494+ struct device *dev;9595+ if (!umc)9696+ return;9797+ dev = get_device(&umc->dev);9898+ d_fnstart(3, dev, "(umc_dev %p)\n", umc);9999+ device_unregister(&umc->dev);100100+ release_resource(&umc->resource);101101+ d_fnend(3, dev, "(umc_dev %p) = void\n", umc);102102+ put_device(dev);103103+}104104+EXPORT_SYMBOL_GPL(umc_device_unregister);
+31
drivers/uwb/umc-drv.c
···11+/*22+ * UWB Multi-interface Controller driver management.33+ *44+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.55+ *66+ * This file is released under the GNU GPL v2.77+ */88+#include <linux/kernel.h>99+#include <linux/uwb/umc.h>1010+1111+int __umc_driver_register(struct umc_driver *umc_drv, struct module *module,1212+ const char *mod_name)1313+{1414+ umc_drv->driver.name = umc_drv->name;1515+ umc_drv->driver.owner = module;1616+ umc_drv->driver.mod_name = mod_name;1717+ umc_drv->driver.bus = &umc_bus_type;1818+1919+ return driver_register(&umc_drv->driver);2020+}2121+EXPORT_SYMBOL_GPL(__umc_driver_register);2222+2323+/**2424+ * umc_driver_register - unregister a UMC capabiltity driver.2525+ * @umc_drv: pointer to the driver.2626+ */2727+void umc_driver_unregister(struct umc_driver *umc_drv)2828+{2929+ driver_unregister(&umc_drv->driver);3030+}3131+EXPORT_SYMBOL_GPL(umc_driver_unregister);
+194
include/linux/uwb/umc.h
···11+/*22+ * UWB Multi-interface Controller support.33+ *44+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.55+ *66+ * This file is released under the GPLv277+ *88+ * UMC (UWB Multi-interface Controller) capabilities (e.g., radio99+ * controller, host controller) are presented as devices on the "umc"1010+ * bus.1111+ *1212+ * The radio controller is not strictly a UMC capability but it's1313+ * useful to present it as such.1414+ *1515+ * References:1616+ *1717+ * [WHCI] Wireless Host Controller Interface Specification for1818+ * Certified Wireless Universal Serial Bus, revision 0.95.1919+ *2020+ * How this works is kind of convoluted but simple. The whci.ko driver2121+ * loads when WHCI devices are detected. These WHCI devices expose2222+ * many devices in the same PCI function (they couldn't have reused2323+ * functions, no), so for each PCI function that exposes these many2424+ * devices, whci ceates a umc_dev [whci_probe() -> whci_add_cap()]2525+ * with umc_device_create() and adds it to the bus with2626+ * umc_device_register().2727+ *2828+ * umc_device_register() calls device_register() which will push the2929+ * bus management code to load your UMC driver's somehting_probe()3030+ * that you have registered for that capability code.3131+ *3232+ * Now when the WHCI device is removed, whci_remove() will go over3333+ * each umc_dev assigned to each of the PCI function's capabilities3434+ * and through whci_del_cap() call umc_device_unregister() each3535+ * created umc_dev. Of course, if you are bound to the device, your3636+ * driver's something_remove() will be called.3737+ */3838+3939+#ifndef _LINUX_UWB_UMC_H_4040+#define _LINUX_UWB_UMC_H_4141+4242+#include <linux/device.h>4343+#include <linux/pci.h>4444+4545+/*4646+ * UMC capability IDs.4747+ *4848+ * 0x00 is reserved so use it for the radio controller device.4949+ *5050+ * [WHCI] table 2-85151+ */5252+#define UMC_CAP_ID_WHCI_RC 0x00 /* radio controller */5353+#define UMC_CAP_ID_WHCI_WUSB_HC 0x01 /* WUSB host controller */5454+5555+/**5656+ * struct umc_dev - UMC capability device5757+ *5858+ * @version: version of the specification this capability conforms to.5959+ * @cap_id: capability ID.6060+ * @bar: PCI Bar (64 bit) where the resource lies6161+ * @resource: register space resource.6262+ * @irq: interrupt line.6363+ */6464+struct umc_dev {6565+ u16 version;6666+ u8 cap_id;6767+ u8 bar;6868+ struct resource resource;6969+ unsigned irq;7070+ struct device dev;7171+};7272+7373+#define to_umc_dev(d) container_of(d, struct umc_dev, dev)7474+7575+/**7676+ * struct umc_driver - UMC capability driver7777+ * @cap_id: supported capability ID.7878+ * @match: driver specific capability matching function.7979+ * @match_data: driver specific data for match() (e.g., a8080+ * table of pci_device_id's if umc_match_pci_id() is used).8181+ */8282+struct umc_driver {8383+ char *name;8484+ u8 cap_id;8585+ int (*match)(struct umc_driver *, struct umc_dev *);8686+ const void *match_data;8787+8888+ int (*probe)(struct umc_dev *);8989+ void (*remove)(struct umc_dev *);9090+ int (*suspend)(struct umc_dev *, pm_message_t state);9191+ int (*resume)(struct umc_dev *);9292+9393+ struct device_driver driver;9494+};9595+9696+#define to_umc_driver(d) container_of(d, struct umc_driver, driver)9797+9898+extern struct bus_type umc_bus_type;9999+100100+struct umc_dev *umc_device_create(struct device *parent, int n);101101+int __must_check umc_device_register(struct umc_dev *umc);102102+void umc_device_unregister(struct umc_dev *umc);103103+104104+int __must_check __umc_driver_register(struct umc_driver *umc_drv,105105+ struct module *mod,106106+ const char *mod_name);107107+108108+/**109109+ * umc_driver_register - register a UMC capabiltity driver.110110+ * @umc_drv: pointer to the driver.111111+ */112112+static inline int __must_check umc_driver_register(struct umc_driver *umc_drv)113113+{114114+ return __umc_driver_register(umc_drv, THIS_MODULE, KBUILD_MODNAME);115115+}116116+void umc_driver_unregister(struct umc_driver *umc_drv);117117+118118+/*119119+ * Utility function you can use to match (umc_driver->match) against a120120+ * null-terminated array of 'struct pci_device_id' in121121+ * umc_driver->match_data.122122+ */123123+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc);124124+125125+/**126126+ * umc_parent_pci_dev - return the UMC's parent PCI device or NULL if none127127+ * @umc_dev: UMC device whose parent PCI device we are looking for128128+ *129129+ * DIRTY!!! DON'T RELY ON THIS130130+ *131131+ * FIXME: This is as dirty as it gets, but we need some way to check132132+ * the correct type of umc_dev->parent (so that for example, we can133133+ * cast to pci_dev). Casting to pci_dev is necesary because at some134134+ * point we need to request resources from the device. Mapping is135135+ * easily over come (ioremap and stuff are bus agnostic), but hooking136136+ * up to some error handlers (such as pci error handlers) might need137137+ * this.138138+ *139139+ * THIS might (probably will) be removed in the future, so don't count140140+ * on it.141141+ */142142+static inline struct pci_dev *umc_parent_pci_dev(struct umc_dev *umc_dev)143143+{144144+ struct pci_dev *pci_dev = NULL;145145+ if (umc_dev->dev.parent->bus == &pci_bus_type)146146+ pci_dev = to_pci_dev(umc_dev->dev.parent);147147+ return pci_dev;148148+}149149+150150+/**151151+ * umc_dev_get() - reference a UMC device.152152+ * @umc_dev: Pointer to UMC device.153153+ *154154+ * NOTE: we are assuming in this whole scheme that the parent device155155+ * is referenced at _probe() time and unreferenced at _remove()156156+ * time by the parent's subsystem.157157+ */158158+static inline struct umc_dev *umc_dev_get(struct umc_dev *umc_dev)159159+{160160+ get_device(&umc_dev->dev);161161+ return umc_dev;162162+}163163+164164+/**165165+ * umc_dev_put() - unreference a UMC device.166166+ * @umc_dev: Pointer to UMC device.167167+ */168168+static inline void umc_dev_put(struct umc_dev *umc_dev)169169+{170170+ put_device(&umc_dev->dev);171171+}172172+173173+/**174174+ * umc_set_drvdata - set UMC device's driver data.175175+ * @umc_dev: Pointer to UMC device.176176+ * @data: Data to set.177177+ */178178+static inline void umc_set_drvdata(struct umc_dev *umc_dev, void *data)179179+{180180+ dev_set_drvdata(&umc_dev->dev, data);181181+}182182+183183+/**184184+ * umc_get_drvdata - recover UMC device's driver data.185185+ * @umc_dev: Pointer to UMC device.186186+ */187187+static inline void *umc_get_drvdata(struct umc_dev *umc_dev)188188+{189189+ return dev_get_drvdata(&umc_dev->dev);190190+}191191+192192+int umc_controller_reset(struct umc_dev *umc);193193+194194+#endif /* #ifndef _LINUX_UWB_UMC_H_ */