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, bits_set = 0; 145 146 /* WE DON'T LOCK, see comment */ 147 /* round up to bytes. Hub bit is bit 0 so add 1. */ 148 size = DIV_ROUND_UP(wusbhc->ports_max + 1, 8); 149 150 /* clear the output buffer. */ 151 memset(_buf, 0, size); 152 /* set the bit for each changed port. */ 153 for (cnt = 0; cnt < wusbhc->ports_max; cnt++) { 154 155 if (wusb_port_by_idx(wusbhc, cnt)->change) { 156 const int bitpos = cnt+1; 157 158 _buf[bitpos/8] |= (1 << (bitpos % 8)); 159 bits_set++; 160 } 161 } 162 163 return bits_set ? size : 0; 164} 165EXPORT_SYMBOL_GPL(wusbhc_rh_status_data); 166 167/* 168 * Return the hub's descriptor 169 * 170 * NOTE: almost cut and paste from ehci-hub.c 171 * 172 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked 173 */ 174static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue, 175 u16 wIndex, 176 struct usb_hub_descriptor *descr, 177 u16 wLength) 178{ 179 u16 temp = 1 + (wusbhc->ports_max / 8); 180 u8 length = 7 + 2 * temp; 181 182 if (wLength < length) 183 return -ENOSPC; 184 descr->bDescLength = 7 + 2 * temp; 185 descr->bDescriptorType = USB_DT_HUB; /* HUB type */ 186 descr->bNbrPorts = wusbhc->ports_max; 187 descr->wHubCharacteristics = cpu_to_le16( 188 HUB_CHAR_COMMON_LPSM /* All ports power at once */ 189 | 0x00 /* not part of compound device */ 190 | HUB_CHAR_NO_OCPM /* No overcurrent protection */ 191 | 0x00 /* 8 FS think time FIXME ?? */ 192 | 0x00); /* No port indicators */ 193 descr->bPwrOn2PwrGood = 0; 194 descr->bHubContrCurrent = 0; 195 /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ 196 memset(&descr->u.hs.DeviceRemovable[0], 0, temp); 197 memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp); 198 return 0; 199} 200 201/* 202 * Clear a hub feature 203 * 204 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 205 * 206 * Nothing to do, so no locking needed ;) 207 */ 208static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature) 209{ 210 int result; 211 212 switch (feature) { 213 case C_HUB_LOCAL_POWER: 214 /* FIXME: maybe plug bit 0 to the power input status, 215 * if any? 216 * see wusbhc_rh_get_hub_status() */ 217 case C_HUB_OVER_CURRENT: 218 result = 0; 219 break; 220 default: 221 result = -EPIPE; 222 } 223 return result; 224} 225 226/* 227 * Return hub status (it is always zero...) 228 * 229 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 230 * 231 * Nothing to do, so no locking needed ;) 232 */ 233static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf, 234 u16 wLength) 235{ 236 /* FIXME: maybe plug bit 0 to the power input status (if any)? */ 237 *buf = 0; 238 return 0; 239} 240 241/* 242 * Set a port feature 243 * 244 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 245 */ 246static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature, 247 u8 selector, u8 port_idx) 248{ 249 struct device *dev = wusbhc->dev; 250 251 if (port_idx > wusbhc->ports_max) 252 return -EINVAL; 253 254 switch (feature) { 255 /* According to USB2.0[11.24.2.13]p2, these features 256 * are not required to be implemented. */ 257 case USB_PORT_FEAT_C_OVER_CURRENT: 258 case USB_PORT_FEAT_C_ENABLE: 259 case USB_PORT_FEAT_C_SUSPEND: 260 case USB_PORT_FEAT_C_CONNECTION: 261 case USB_PORT_FEAT_C_RESET: 262 return 0; 263 case USB_PORT_FEAT_POWER: 264 /* No such thing, but we fake it works */ 265 mutex_lock(&wusbhc->mutex); 266 wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER; 267 mutex_unlock(&wusbhc->mutex); 268 return 0; 269 case USB_PORT_FEAT_RESET: 270 return wusbhc_rh_port_reset(wusbhc, port_idx); 271 case USB_PORT_FEAT_ENABLE: 272 case USB_PORT_FEAT_SUSPEND: 273 dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n", 274 port_idx, feature, selector); 275 return -ENOSYS; 276 default: 277 dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n", 278 port_idx, feature, selector); 279 return -EPIPE; 280 } 281 282 return 0; 283} 284 285/* 286 * Clear a port feature... 287 * 288 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 289 */ 290static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature, 291 u8 selector, u8 port_idx) 292{ 293 int result = 0; 294 struct device *dev = wusbhc->dev; 295 296 if (port_idx > wusbhc->ports_max) 297 return -EINVAL; 298 299 mutex_lock(&wusbhc->mutex); 300 switch (feature) { 301 case USB_PORT_FEAT_POWER: /* fake port always on */ 302 /* According to USB2.0[11.24.2.7.1.4], no need to implement? */ 303 case USB_PORT_FEAT_C_OVER_CURRENT: 304 break; 305 case USB_PORT_FEAT_C_RESET: 306 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET; 307 break; 308 case USB_PORT_FEAT_C_CONNECTION: 309 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION; 310 break; 311 case USB_PORT_FEAT_ENABLE: 312 __wusbhc_dev_disable(wusbhc, port_idx); 313 break; 314 case USB_PORT_FEAT_C_ENABLE: 315 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE; 316 break; 317 case USB_PORT_FEAT_SUSPEND: 318 case USB_PORT_FEAT_C_SUSPEND: 319 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n", 320 port_idx, feature, selector); 321 result = -ENOSYS; 322 break; 323 default: 324 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n", 325 port_idx, feature, selector); 326 result = -EPIPE; 327 break; 328 } 329 mutex_unlock(&wusbhc->mutex); 330 331 return result; 332} 333 334/* 335 * Return the port's status 336 * 337 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 338 */ 339static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx, 340 u32 *_buf, u16 wLength) 341{ 342 __le16 *buf = (__le16 *)_buf; 343 344 if (port_idx > wusbhc->ports_max) 345 return -EINVAL; 346 347 mutex_lock(&wusbhc->mutex); 348 buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status); 349 buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change); 350 mutex_unlock(&wusbhc->mutex); 351 352 return 0; 353} 354 355/* 356 * Entry point for Root Hub operations 357 * 358 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked. 359 */ 360int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue, 361 u16 wIndex, char *buf, u16 wLength) 362{ 363 int result = -ENOSYS; 364 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 365 366 switch (reqntype) { 367 case GetHubDescriptor: 368 result = wusbhc_rh_get_hub_descr( 369 wusbhc, wValue, wIndex, 370 (struct usb_hub_descriptor *) buf, wLength); 371 break; 372 case ClearHubFeature: 373 result = wusbhc_rh_clear_hub_feat(wusbhc, wValue); 374 break; 375 case GetHubStatus: 376 result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength); 377 break; 378 379 case SetPortFeature: 380 result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8, 381 (wIndex & 0xff) - 1); 382 break; 383 case ClearPortFeature: 384 result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8, 385 (wIndex & 0xff) - 1); 386 break; 387 case GetPortStatus: 388 result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1, 389 (u32 *)buf, wLength); 390 break; 391 392 case SetHubFeature: 393 default: 394 dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) " 395 "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype, 396 wValue, wIndex, buf, wLength); 397 /* dump_stack(); */ 398 result = -ENOSYS; 399 } 400 return result; 401} 402EXPORT_SYMBOL_GPL(wusbhc_rh_control); 403 404int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx) 405{ 406 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); 407 dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n", 408 __func__, usb_hcd, wusbhc, port_idx); 409 WARN_ON(1); 410 return -ENOSYS; 411} 412EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset); 413 414static void wusb_port_init(struct wusb_port *port) 415{ 416 port->status |= USB_PORT_STAT_HIGH_SPEED; 417} 418 419/* 420 * Alloc fake port specific fields and status. 421 */ 422int wusbhc_rh_create(struct wusbhc *wusbhc) 423{ 424 int result = -ENOMEM; 425 size_t port_size, itr; 426 port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]); 427 wusbhc->port = kzalloc(port_size, GFP_KERNEL); 428 if (wusbhc->port == NULL) 429 goto error_port_alloc; 430 for (itr = 0; itr < wusbhc->ports_max; itr++) 431 wusb_port_init(&wusbhc->port[itr]); 432 result = 0; 433error_port_alloc: 434 return result; 435} 436 437void wusbhc_rh_destroy(struct wusbhc *wusbhc) 438{ 439 kfree(wusbhc->port); 440} 441