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

Configure Feed

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

at v2.6.32-rc4 450 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 "wusbhc.h" 73 74/* 75 * Reset a fake port 76 * 77 * Using a Reset Device IE is too heavyweight as it causes the device 78 * to enter the UnConnected state and leave the cluster, this can mean 79 * that when the device reconnects it is connected to a different fake 80 * port. 81 * 82 * Instead, reset authenticated devices with a SetAddress(0), followed 83 * by a SetAddresss(AuthAddr). 84 * 85 * For unauthenticated devices just pretend to reset but do nothing. 86 * If the device initialization continues to fail it will eventually 87 * time out after TrustTimeout and enter the UnConnected state. 88 * 89 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 90 * 91 * Supposedly we are the only thread accesing @wusbhc->port; in any 92 * case, maybe we should move the mutex locking from 93 * wusbhc_devconnect_auth() to here. 94 * 95 * @port_idx refers to the wusbhc's port index, not the USB port number 96 */ 97static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx) 98{ 99 int result = 0; 100 struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx); 101 struct wusb_dev *wusb_dev = port->wusb_dev; 102 103 if (wusb_dev == NULL) 104 return -ENOTCONN; 105 106 port->status |= USB_PORT_STAT_RESET; 107 port->change |= USB_PORT_STAT_C_RESET; 108 109 if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH) 110 result = 0; 111 else 112 result = wusb_dev_update_address(wusbhc, wusb_dev); 113 114 port->status &= ~USB_PORT_STAT_RESET; 115 port->status |= USB_PORT_STAT_ENABLE; 116 port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE; 117 118 return result; 119} 120 121/* 122 * Return the hub change status bitmap 123 * 124 * The bits in the change status bitmap are cleared when a 125 * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4]. 126 * 127 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 128 * 129 * WARNING!! This gets called from atomic context; we cannot get the 130 * mutex--the only race condition we can find is some bit 131 * changing just after we copy it, which shouldn't be too 132 * big of a problem [and we can't make it an spinlock 133 * because other parts need to take it and sleep] . 134 * 135 * @usb_hcd is refcounted, so it won't dissapear under us 136 * and before killing a host, the polling of the root hub 137 * would be stopped anyway. 138 */ 139int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf) 140{ 141 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 142 size_t cnt, size; 143 unsigned long *buf = (unsigned long *) _buf; 144 145 /* WE DON'T LOCK, see comment */ 146 size = wusbhc->ports_max + 1 /* hub bit */; 147 size = (size + 8 - 1) / 8; /* round to bytes */ 148 for (cnt = 0; cnt < wusbhc->ports_max; cnt++) 149 if (wusb_port_by_idx(wusbhc, cnt)->change) 150 set_bit(cnt + 1, buf); 151 else 152 clear_bit(cnt + 1, buf); 153 return size; 154} 155EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); 156 157/* 158 * Return the hub's desciptor 159 * 160 * NOTE: almost cut and paste from ehci-hub.c 161 * 162 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked 163 */ 164static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, 165 u16 wIndex, 166 struct usb_hub_descriptor *descr, 167 u16 wLength) 168{ 169 u16 temp = 1 + (wusbhc->ports_max / 8); 170 u8 length = 7 + 2 * temp; 171 172 if (wLength < length) 173 return -ENOSPC; 174 descr->bDescLength = 7 + 2 * temp; 175 descr->bDescriptorType = 0x29; /* HUB type */ 176 descr->bNbrPorts = wusbhc->ports_max; 177 descr->wHubCharacteristics = cpu_to_le16( 178 0x00 /* All ports power at once */ 179 | 0x00 /* not part of compound device */ 180 | 0x10 /* No overcurrent protection */ 181 | 0x00 /* 8 FS think time FIXME ?? */ 182 | 0x00); /* No port indicators */ 183 descr->bPwrOn2PwrGood = 0; 184 descr->bHubContrCurrent = 0; 185 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ 186 memset(&descr->bitmap[0], 0, temp); 187 memset(&descr->bitmap[temp], 0xff, temp); 188 return 0; 189} 190 191/* 192 * Clear a hub feature 193 * 194 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 195 * 196 * Nothing to do, so no locking needed ;) 197 */ 198static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) 199{ 200 int result; 201 202 switch (feature) { 203 case C_HUB_LOCAL_POWER: 204 /* FIXME: maybe plug bit 0 to the power input status, 205 * if any? 206 * see wusbhc_rh_get_hub_status() */ 207 case C_HUB_OVER_CURRENT: 208 result = 0; 209 break; 210 default: 211 result = -EPIPE; 212 } 213 return result; 214} 215 216/* 217 * Return hub status (it is always zero...) 218 * 219 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 220 * 221 * Nothing to do, so no locking needed ;) 222 */ 223static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, 224 u16 wLength) 225{ 226 /* FIXME: maybe plug bit 0 to the power input status (if any)? */ 227 *buf = 0; 228 return 0; 229} 230 231/* 232 * Set a port feature 233 * 234 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 235 */ 236static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, 237 u8 selector, u8 port_idx) 238{ 239 struct device *dev = wusbhc->dev; 240 241 if (port_idx > wusbhc->ports_max) 242 return -EINVAL; 243 244 switch (feature) { 245 /* According to USB2.0[11.24.2.13]p2, these features 246 * are not required to be implemented. */ 247 case USB_PORT_FEAT_C_OVER_CURRENT: 248 case USB_PORT_FEAT_C_ENABLE: 249 case USB_PORT_FEAT_C_SUSPEND: 250 case USB_PORT_FEAT_C_CONNECTION: 251 case USB_PORT_FEAT_C_RESET: 252 return 0; 253 case USB_PORT_FEAT_POWER: 254 /* No such thing, but we fake it works */ 255 mutex_lock(&wusbhc->mutex); 256 wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; 257 mutex_unlock(&wusbhc->mutex); 258 return 0; 259 case USB_PORT_FEAT_RESET: 260 return wusbhc_rh_port_reset(wusbhc, port_idx); 261 case USB_PORT_FEAT_ENABLE: 262 case USB_PORT_FEAT_SUSPEND: 263 dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", 264 port_idx, feature, selector); 265 return -ENOSYS; 266 default: 267 dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", 268 port_idx, feature, selector); 269 return -EPIPE; 270 } 271 272 return 0; 273} 274 275/* 276 * Clear a port feature... 277 * 278 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 279 */ 280static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, 281 u8 selector, u8 port_idx) 282{ 283 int result = 0; 284 struct device *dev = wusbhc->dev; 285 286 if (port_idx > wusbhc->ports_max) 287 return -EINVAL; 288 289 mutex_lock(&wusbhc->mutex); 290 switch (feature) { 291 case USB_PORT_FEAT_POWER: /* fake port always on */ 292 /* According to USB2.0[11.24.2.7.1.4], no need to implement? */ 293 case USB_PORT_FEAT_C_OVER_CURRENT: 294 break; 295 case USB_PORT_FEAT_C_RESET: 296 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; 297 break; 298 case USB_PORT_FEAT_C_CONNECTION: 299 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; 300 break; 301 case USB_PORT_FEAT_ENABLE: 302 __wusbhc_dev_disable(wusbhc, port_idx); 303 break; 304 case USB_PORT_FEAT_C_ENABLE: 305 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; 306 break; 307 case USB_PORT_FEAT_SUSPEND: 308 case USB_PORT_FEAT_C_SUSPEND: 309 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", 310 port_idx, feature, selector); 311 result = -ENOSYS; 312 break; 313 default: 314 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", 315 port_idx, feature, selector); 316 result = -EPIPE; 317 break; 318 } 319 mutex_unlock(&wusbhc->mutex); 320 321 return result; 322} 323 324/* 325 * Return the port's status 326 * 327 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 328 */ 329static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, 330 u32 *_buf, u16 wLength) 331{ 332 __le16 *buf = (__le16 *)_buf; 333 334 if (port_idx > wusbhc->ports_max) 335 return -EINVAL; 336 337 mutex_lock(&wusbhc->mutex); 338 buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); 339 buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); 340 mutex_unlock(&wusbhc->mutex); 341 342 return 0; 343} 344 345/* 346 * Entry point for Root Hub operations 347 * 348 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 349 */ 350int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, 351 u16 wIndex, char *buf, u16 wLength) 352{ 353 int result = -ENOSYS; 354 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 355 356 switch (reqntype) { 357 case GetHubDescriptor: 358 result = wusbhc_rh_get_hub_descr( 359 wusbhc, wValue, wIndex, 360 (struct usb_hub_descriptor *) buf, wLength); 361 break; 362 case ClearHubFeature: 363 result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); 364 break; 365 case GetHubStatus: 366 result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); 367 break; 368 369 case SetPortFeature: 370 result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, 371 (wIndex & 0xff) - 1); 372 break; 373 case ClearPortFeature: 374 result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, 375 (wIndex & 0xff) - 1); 376 break; 377 case GetPortStatus: 378 result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, 379 (u32 *)buf, wLength); 380 break; 381 382 case SetHubFeature: 383 default: 384 dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " 385 "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, 386 wValue, wIndex, buf, wLength); 387 /* dump_stack(); */ 388 result = -ENOSYS; 389 } 390 return result; 391} 392EXPORT_SYMBOL_GPL(wusbhc_rh_control); 393 394int wusbhc_rh_suspend(struct usb_hcd *usb_hcd) 395{ 396 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 397 dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, 398 usb_hcd, wusbhc); 399 /* dump_stack(); */ 400 return -ENOSYS; 401} 402EXPORT_SYMBOL_GPL(wusbhc_rh_suspend); 403 404int wusbhc_rh_resume(struct usb_hcd *usb_hcd) 405{ 406 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 407 dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__, 408 usb_hcd, wusbhc); 409 /* dump_stack(); */ 410 return -ENOSYS; 411} 412EXPORT_SYMBOL_GPL(wusbhc_rh_resume); 413 414int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) 415{ 416 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 417 dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", 418 __func__, usb_hcd, wusbhc, port_idx); 419 WARN_ON(1); 420 return -ENOSYS; 421} 422EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); 423 424static void wusb_port_init(struct wusb_port *port) 425{ 426 port->status |= USB_PORT_STAT_HIGH_SPEED; 427} 428 429/* 430 * Alloc fake port specific fields and status. 431 */ 432int wusbhc_rh_create(struct wusbhc *wusbhc) 433{ 434 int result = -ENOMEM; 435 size_t port_size, itr; 436 port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); 437 wusbhc->port = kzalloc(port_size, GFP_KERNEL); 438 if (wusbhc->port == NULL) 439 goto error_port_alloc; 440 for (itr = 0; itr < wusbhc->ports_max; itr++) 441 wusb_port_init(&wusbhc->port[itr]); 442 result = 0; 443error_port_alloc: 444 return result; 445} 446 447void wusbhc_rh_destroy(struct wusbhc *wusbhc) 448{ 449 kfree(wusbhc->port); 450}