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