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.39-rc4 451 lines 13 kB view raw
1/* 2 * Wireless USB Host Controller 3 * Root Hub operations 4 * 5 * 6 * Copyright (C) 2005-2006 Intel Corporation 7 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License version 11 * 2 as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21 * 02110-1301, USA. 22 * 23 * 24 * We fake a root hub that has fake ports (as many as simultaneous 25 * devices the Wireless USB Host Controller can deal with). For each 26 * port we keep an state in @wusbhc->port[index] identical to the one 27 * specified in the USB2.0[ch11] spec and some extra device 28 * information that complements the one in 'struct usb_device' (as 29 * this lacs a hcpriv pointer). 30 * 31 * Note this is common to WHCI and HWA host controllers. 32 * 33 * Through here we enable most of the state changes that the USB stack 34 * will use to connect or disconnect devices. We need to do some 35 * forced adaptation of Wireless USB device states vs. wired: 36 * 37 * USB: WUSB: 38 * 39 * Port Powered-off port slot n/a 40 * Powered-on port slot available 41 * Disconnected port slot available 42 * Connected port slot assigned device 43 * device sent DN_Connect 44 * device was authenticated 45 * Enabled device is authenticated, transitioned 46 * from unauth -> auth -> default address 47 * -> enabled 48 * Reset disconnect 49 * Disable disconnect 50 * 51 * This maps the standard USB port states with the WUSB device states 52 * so we can fake ports without having to modify the USB stack. 53 * 54 * FIXME: this process will change in the future 55 * 56 * 57 * ENTRY POINTS 58 * 59 * Our entry points into here are, as in hcd.c, the USB stack root hub 60 * ops defined in the usb_hcd struct: 61 * 62 * wusbhc_rh_status_data() Provide hub and port status data bitmap 63 * 64 * wusbhc_rh_control() Execution of all the major requests 65 * you can do to a hub (Set|Clear 66 * features, get descriptors, status, etc). 67 * 68 * wusbhc_rh_[suspend|resume]() That 69 * 70 * wusbhc_rh_start_port_reset() ??? unimplemented 71 */ 72#include <linux/slab.h> 73#include "wusbhc.h" 74 75/* 76 * Reset a fake port 77 * 78 * Using a Reset Device IE is too heavyweight as it causes the device 79 * to enter the UnConnected state and leave the cluster, this can mean 80 * that when the device reconnects it is connected to a different fake 81 * port. 82 * 83 * Instead, reset authenticated devices with a SetAddress(0), followed 84 * by a SetAddresss(AuthAddr). 85 * 86 * For unauthenticated devices just pretend to reset but do nothing. 87 * If the device initialization continues to fail it will eventually 88 * time out after TrustTimeout and enter the UnConnected state. 89 * 90 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 91 * 92 * Supposedly we are the only thread accesing @wusbhc->port; in any 93 * case, maybe we should move the mutex locking from 94 * wusbhc_devconnect_auth() to here. 95 * 96 * @port_idx refers to the wusbhc's port index, not the USB port number 97 */ 98static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) 99{ 100 int result = 0; 101 struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); 102 struct wusb_dev *wusb_dev = port->wusb_dev; 103 104 if (wusb_dev == NULL) 105 return -ENOTCONN; 106 107 port->status |= USB_PORT_STAT_RESET; 108 port->change |= USB_PORT_STAT_C_RESET; 109 110 if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH) 111 result = 0; 112 else 113 result = wusb_dev_update_address(wusbhc, wusb_dev); 114 115 port->status &= ~USB_PORT_STAT_RESET; 116 port->status |= USB_PORT_STAT_ENABLE; 117 port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; 118 119 return result; 120} 121 122/* 123 * Return the hub change status bitmap 124 * 125 * The bits in the change status bitmap are cleared when a 126 * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4]. 127 * 128 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 129 * 130 * WARNING!! This gets called from atomic context; we cannot get the 131 * mutex--the only race condition we can find is some bit 132 * changing just after we copy it, which shouldn't be too 133 * big of a problem [and we can't make it an spinlock 134 * because other parts need to take it and sleep] . 135 * 136 * @usb_hcd is refcounted, so it won't disappear under us 137 * and before killing a host, the polling of the root hub 138 * would be stopped anyway. 139 */ 140int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) 141{ 142 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 143 size_t cnt, size; 144 unsigned long *buf = (unsigned long *) _buf; 145 146 /* WE DON'T LOCK, see comment */ 147 size = wusbhc->ports_max + 1 /* hub bit */; 148 size = (size + 8 - 1) / 8; /* round to bytes */ 149 for (cnt = 0; cnt < wusbhc->ports_max; cnt++) 150 if (wusb_port_by_idx(wusbhc, cnt)->change) 151 set_bit(cnt + 1, buf); 152 else 153 clear_bit(cnt + 1, buf); 154 return size; 155} 156EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); 157 158/* 159 * Return the hub's descriptor 160 * 161 * NOTE: almost cut and paste from ehci-hub.c 162 * 163 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked 164 */ 165static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, 166 u16 wIndex, 167 struct usb_hub_descriptor *descr, 168 u16 wLength) 169{ 170 u16 temp = 1 + (wusbhc->ports_max / 8); 171 u8 length = 7 + 2 * temp; 172 173 if (wLength < length) 174 return -ENOSPC; 175 descr->bDescLength = 7 + 2 * temp; 176 descr->bDescriptorType = 0x29; /* HUB type */ 177 descr->bNbrPorts = wusbhc->ports_max; 178 descr->wHubCharacteristics = cpu_to_le16( 179 0x00 /* All ports power at once */ 180 | 0x00 /* not part of compound device */ 181 | 0x10 /* No overcurrent protection */ 182 | 0x00 /* 8 FS think time FIXME ?? */ 183 | 0x00); /* No port indicators */ 184 descr->bPwrOn2PwrGood = 0; 185 descr->bHubContrCurrent = 0; 186 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ 187 memset(&descr->u.hs.DeviceRemovable[0], 0, temp); 188 memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp); 189 return 0; 190} 191 192/* 193 * Clear a hub feature 194 * 195 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 196 * 197 * Nothing to do, so no locking needed ;) 198 */ 199static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) 200{ 201 int result; 202 203 switch (feature) { 204 case C_HUB_LOCAL_POWER: 205 /* FIXME: maybe plug bit 0 to the power input status, 206 * if any? 207 * see wusbhc_rh_get_hub_status() */ 208 case C_HUB_OVER_CURRENT: 209 result = 0; 210 break; 211 default: 212 result = -EPIPE; 213 } 214 return result; 215} 216 217/* 218 * Return hub status (it is always zero...) 219 * 220 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 221 * 222 * Nothing to do, so no locking needed ;) 223 */ 224static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, 225 u16 wLength) 226{ 227 /* FIXME: maybe plug bit 0 to the power input status (if any)? */ 228 *buf = 0; 229 return 0; 230} 231 232/* 233 * Set a port feature 234 * 235 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 236 */ 237static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, 238 u8 selector, u8 port_idx) 239{ 240 struct device *dev = wusbhc->dev; 241 242 if (port_idx > wusbhc->ports_max) 243 return -EINVAL; 244 245 switch (feature) { 246 /* According to USB2.0[11.24.2.13]p2, these features 247 * are not required to be implemented. */ 248 case USB_PORT_FEAT_C_OVER_CURRENT: 249 case USB_PORT_FEAT_C_ENABLE: 250 case USB_PORT_FEAT_C_SUSPEND: 251 case USB_PORT_FEAT_C_CONNECTION: 252 case USB_PORT_FEAT_C_RESET: 253 return 0; 254 case USB_PORT_FEAT_POWER: 255 /* No such thing, but we fake it works */ 256 mutex_lock(&wusbhc->mutex); 257 wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; 258 mutex_unlock(&wusbhc->mutex); 259 return 0; 260 case USB_PORT_FEAT_RESET: 261 return wusbhc_rh_port_reset(wusbhc, port_idx); 262 case USB_PORT_FEAT_ENABLE: 263 case USB_PORT_FEAT_SUSPEND: 264 dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", 265 port_idx, feature, selector); 266 return -ENOSYS; 267 default: 268 dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", 269 port_idx, feature, selector); 270 return -EPIPE; 271 } 272 273 return 0; 274} 275 276/* 277 * Clear a port feature... 278 * 279 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 280 */ 281static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, 282 u8 selector, u8 port_idx) 283{ 284 int result = 0; 285 struct device *dev = wusbhc->dev; 286 287 if (port_idx > wusbhc->ports_max) 288 return -EINVAL; 289 290 mutex_lock(&wusbhc->mutex); 291 switch (feature) { 292 case USB_PORT_FEAT_POWER: /* fake port always on */ 293 /* According to USB2.0[11.24.2.7.1.4], no need to implement? */ 294 case USB_PORT_FEAT_C_OVER_CURRENT: 295 break; 296 case USB_PORT_FEAT_C_RESET: 297 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; 298 break; 299 case USB_PORT_FEAT_C_CONNECTION: 300 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; 301 break; 302 case USB_PORT_FEAT_ENABLE: 303 __wusbhc_dev_disable(wusbhc, port_idx); 304 break; 305 case USB_PORT_FEAT_C_ENABLE: 306 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; 307 break; 308 case USB_PORT_FEAT_SUSPEND: 309 case USB_PORT_FEAT_C_SUSPEND: 310 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", 311 port_idx, feature, selector); 312 result = -ENOSYS; 313 break; 314 default: 315 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", 316 port_idx, feature, selector); 317 result = -EPIPE; 318 break; 319 } 320 mutex_unlock(&wusbhc->mutex); 321 322 return result; 323} 324 325/* 326 * Return the port's status 327 * 328 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 329 */ 330static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, 331 u32 *_buf, u16 wLength) 332{ 333 __le16 *buf = (__le16 *)_buf; 334 335 if (port_idx > wusbhc->ports_max) 336 return -EINVAL; 337 338 mutex_lock(&wusbhc->mutex); 339 buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); 340 buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); 341 mutex_unlock(&wusbhc->mutex); 342 343 return 0; 344} 345 346/* 347 * Entry point for Root Hub operations 348 * 349 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 350 */ 351int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, 352 u16 wIndex, char *buf, u16 wLength) 353{ 354 int result = -ENOSYS; 355 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 356 357 switch (reqntype) { 358 case GetHubDescriptor: 359 result = wusbhc_rh_get_hub_descr( 360 wusbhc, wValue, wIndex, 361 (struct usb_hub_descriptor *) buf, wLength); 362 break; 363 case ClearHubFeature: 364 result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); 365 break; 366 case GetHubStatus: 367 result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); 368 break; 369 370 case SetPortFeature: 371 result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, 372 (wIndex & 0xff) - 1); 373 break; 374 case ClearPortFeature: 375 result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, 376 (wIndex & 0xff) - 1); 377 break; 378 case GetPortStatus: 379 result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, 380 (u32 *)buf, wLength); 381 break; 382 383 case SetHubFeature: 384 default: 385 dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " 386 "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, 387 wValue, wIndex, buf, wLength); 388 /* dump_stack(); */ 389 result = -ENOSYS; 390 } 391 return result; 392} 393EXPORT_SYMBOL_GPL(wusbhc_rh_control); 394 395int wusbhc_rh_suspend(struct usb_hcd *usb_hcd) 396{ 397 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 398 dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, 399 usb_hcd, wusbhc); 400 /* dump_stack(); */ 401 return -ENOSYS; 402} 403EXPORT_SYMBOL_GPL(wusbhc_rh_suspend); 404 405int wusbhc_rh_resume(struct usb_hcd *usb_hcd) 406{ 407 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 408 dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, 409 usb_hcd, wusbhc); 410 /* dump_stack(); */ 411 return -ENOSYS; 412} 413EXPORT_SYMBOL_GPL(wusbhc_rh_resume); 414 415int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) 416{ 417 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 418 dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", 419 __func__, usb_hcd, wusbhc, port_idx); 420 WARN_ON(1); 421 return -ENOSYS; 422} 423EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); 424 425static void wusb_port_init(struct wusb_port *port) 426{ 427 port->status |= USB_PORT_STAT_HIGH_SPEED; 428} 429 430/* 431 * Alloc fake port specific fields and status. 432 */ 433int wusbhc_rh_create(struct wusbhc *wusbhc) 434{ 435 int result = -ENOMEM; 436 size_t port_size, itr; 437 port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); 438 wusbhc->port = kzalloc(port_size, GFP_KERNEL); 439 if (wusbhc->port == NULL) 440 goto error_port_alloc; 441 for (itr = 0; itr < wusbhc->ports_max; itr++) 442 wusb_port_init(&wusbhc->port[itr]); 443 result = 0; 444error_port_alloc: 445 return result; 446} 447 448void wusbhc_rh_destroy(struct wusbhc *wusbhc) 449{ 450 kfree(wusbhc->port); 451}