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