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

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.28-rc2 218 lines 4.9 kB view raw
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 14static 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 */ 35int 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} 50EXPORT_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 */ 58int 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} 69EXPORT_SYMBOL_GPL(umc_match_pci_id); 70 71static 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 81static 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 95static 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 109static 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 128static 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 141static 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 157static 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 173static 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 180static 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 187static struct device_attribute umc_dev_attrs[] = { 188 __ATTR_RO(capability_id), 189 __ATTR_RO(version), 190 __ATTR_NULL, 191}; 192 193struct 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}; 202EXPORT_SYMBOL_GPL(umc_bus_type); 203 204static int __init umc_bus_init(void) 205{ 206 return bus_register(&umc_bus_type); 207} 208module_init(umc_bus_init); 209 210static void __exit umc_bus_exit(void) 211{ 212 bus_unregister(&umc_bus_type); 213} 214module_exit(umc_bus_exit); 215 216MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus"); 217MODULE_AUTHOR("Cambridge Silicon Radio Ltd."); 218MODULE_LICENSE("GPL");