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