|
|
1.1 ! root 1: /* ! 2: * QEMU USB HUB emulation ! 3: * ! 4: * Copyright (c) 2005 Fabrice Bellard ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: #include "qemu-common.h" ! 25: #include "trace.h" ! 26: #include "hw/usb.h" ! 27: #include "hw/usb/desc.h" ! 28: ! 29: #define NUM_PORTS 8 ! 30: ! 31: typedef struct USBHubPort { ! 32: USBPort port; ! 33: uint16_t wPortStatus; ! 34: uint16_t wPortChange; ! 35: } USBHubPort; ! 36: ! 37: typedef struct USBHubState { ! 38: USBDevice dev; ! 39: USBEndpoint *intr; ! 40: USBHubPort ports[NUM_PORTS]; ! 41: } USBHubState; ! 42: ! 43: #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) ! 44: #define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) ! 45: #define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) ! 46: #define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) ! 47: #define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) ! 48: #define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) ! 49: #define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) ! 50: ! 51: #define PORT_STAT_CONNECTION 0x0001 ! 52: #define PORT_STAT_ENABLE 0x0002 ! 53: #define PORT_STAT_SUSPEND 0x0004 ! 54: #define PORT_STAT_OVERCURRENT 0x0008 ! 55: #define PORT_STAT_RESET 0x0010 ! 56: #define PORT_STAT_POWER 0x0100 ! 57: #define PORT_STAT_LOW_SPEED 0x0200 ! 58: #define PORT_STAT_HIGH_SPEED 0x0400 ! 59: #define PORT_STAT_TEST 0x0800 ! 60: #define PORT_STAT_INDICATOR 0x1000 ! 61: ! 62: #define PORT_STAT_C_CONNECTION 0x0001 ! 63: #define PORT_STAT_C_ENABLE 0x0002 ! 64: #define PORT_STAT_C_SUSPEND 0x0004 ! 65: #define PORT_STAT_C_OVERCURRENT 0x0008 ! 66: #define PORT_STAT_C_RESET 0x0010 ! 67: ! 68: #define PORT_CONNECTION 0 ! 69: #define PORT_ENABLE 1 ! 70: #define PORT_SUSPEND 2 ! 71: #define PORT_OVERCURRENT 3 ! 72: #define PORT_RESET 4 ! 73: #define PORT_POWER 8 ! 74: #define PORT_LOWSPEED 9 ! 75: #define PORT_HIGHSPEED 10 ! 76: #define PORT_C_CONNECTION 16 ! 77: #define PORT_C_ENABLE 17 ! 78: #define PORT_C_SUSPEND 18 ! 79: #define PORT_C_OVERCURRENT 19 ! 80: #define PORT_C_RESET 20 ! 81: #define PORT_TEST 21 ! 82: #define PORT_INDICATOR 22 ! 83: ! 84: /* same as Linux kernel root hubs */ ! 85: ! 86: enum { ! 87: STR_MANUFACTURER = 1, ! 88: STR_PRODUCT, ! 89: STR_SERIALNUMBER, ! 90: }; ! 91: ! 92: static const USBDescStrings desc_strings = { ! 93: [STR_MANUFACTURER] = "QEMU " QEMU_VERSION, ! 94: [STR_PRODUCT] = "QEMU USB Hub", ! 95: [STR_SERIALNUMBER] = "314159", ! 96: }; ! 97: ! 98: static const USBDescIface desc_iface_hub = { ! 99: .bInterfaceNumber = 0, ! 100: .bNumEndpoints = 1, ! 101: .bInterfaceClass = USB_CLASS_HUB, ! 102: .eps = (USBDescEndpoint[]) { ! 103: { ! 104: .bEndpointAddress = USB_DIR_IN | 0x01, ! 105: .bmAttributes = USB_ENDPOINT_XFER_INT, ! 106: .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8, ! 107: .bInterval = 0xff, ! 108: }, ! 109: } ! 110: }; ! 111: ! 112: static const USBDescDevice desc_device_hub = { ! 113: .bcdUSB = 0x0110, ! 114: .bDeviceClass = USB_CLASS_HUB, ! 115: .bMaxPacketSize0 = 8, ! 116: .bNumConfigurations = 1, ! 117: .confs = (USBDescConfig[]) { ! 118: { ! 119: .bNumInterfaces = 1, ! 120: .bConfigurationValue = 1, ! 121: .bmAttributes = 0xe0, ! 122: .nif = 1, ! 123: .ifs = &desc_iface_hub, ! 124: }, ! 125: }, ! 126: }; ! 127: ! 128: static const USBDesc desc_hub = { ! 129: .id = { ! 130: .idVendor = 0x0409, ! 131: .idProduct = 0x55aa, ! 132: .bcdDevice = 0x0101, ! 133: .iManufacturer = STR_MANUFACTURER, ! 134: .iProduct = STR_PRODUCT, ! 135: .iSerialNumber = STR_SERIALNUMBER, ! 136: }, ! 137: .full = &desc_device_hub, ! 138: .str = desc_strings, ! 139: }; ! 140: ! 141: static const uint8_t qemu_hub_hub_descriptor[] = ! 142: { ! 143: 0x00, /* u8 bLength; patched in later */ ! 144: 0x29, /* u8 bDescriptorType; Hub-descriptor */ ! 145: 0x00, /* u8 bNbrPorts; (patched later) */ ! 146: 0x0a, /* u16 wHubCharacteristics; */ ! 147: 0x00, /* (per-port OC, no power switching) */ ! 148: 0x01, /* u8 bPwrOn2pwrGood; 2ms */ ! 149: 0x00 /* u8 bHubContrCurrent; 0 mA */ ! 150: ! 151: /* DeviceRemovable and PortPwrCtrlMask patched in later */ ! 152: }; ! 153: ! 154: static void usb_hub_attach(USBPort *port1) ! 155: { ! 156: USBHubState *s = port1->opaque; ! 157: USBHubPort *port = &s->ports[port1->index]; ! 158: ! 159: trace_usb_hub_attach(s->dev.addr, port1->index + 1); ! 160: port->wPortStatus |= PORT_STAT_CONNECTION; ! 161: port->wPortChange |= PORT_STAT_C_CONNECTION; ! 162: if (port->port.dev->speed == USB_SPEED_LOW) { ! 163: port->wPortStatus |= PORT_STAT_LOW_SPEED; ! 164: } else { ! 165: port->wPortStatus &= ~PORT_STAT_LOW_SPEED; ! 166: } ! 167: usb_wakeup(s->intr); ! 168: } ! 169: ! 170: static void usb_hub_detach(USBPort *port1) ! 171: { ! 172: USBHubState *s = port1->opaque; ! 173: USBHubPort *port = &s->ports[port1->index]; ! 174: ! 175: trace_usb_hub_detach(s->dev.addr, port1->index + 1); ! 176: usb_wakeup(s->intr); ! 177: ! 178: /* Let upstream know the device on this port is gone */ ! 179: s->dev.port->ops->child_detach(s->dev.port, port1->dev); ! 180: ! 181: port->wPortStatus &= ~PORT_STAT_CONNECTION; ! 182: port->wPortChange |= PORT_STAT_C_CONNECTION; ! 183: if (port->wPortStatus & PORT_STAT_ENABLE) { ! 184: port->wPortStatus &= ~PORT_STAT_ENABLE; ! 185: port->wPortChange |= PORT_STAT_C_ENABLE; ! 186: } ! 187: } ! 188: ! 189: static void usb_hub_child_detach(USBPort *port1, USBDevice *child) ! 190: { ! 191: USBHubState *s = port1->opaque; ! 192: ! 193: /* Pass along upstream */ ! 194: s->dev.port->ops->child_detach(s->dev.port, child); ! 195: } ! 196: ! 197: static void usb_hub_wakeup(USBPort *port1) ! 198: { ! 199: USBHubState *s = port1->opaque; ! 200: USBHubPort *port = &s->ports[port1->index]; ! 201: ! 202: if (port->wPortStatus & PORT_STAT_SUSPEND) { ! 203: port->wPortChange |= PORT_STAT_C_SUSPEND; ! 204: usb_wakeup(s->intr); ! 205: } ! 206: } ! 207: ! 208: static void usb_hub_complete(USBPort *port, USBPacket *packet) ! 209: { ! 210: USBHubState *s = port->opaque; ! 211: ! 212: /* ! 213: * Just pass it along upstream for now. ! 214: * ! 215: * If we ever implement usb 2.0 split transactions this will ! 216: * become a little more complicated ... ! 217: * ! 218: * Can't use usb_packet_complete() here because packet->owner is ! 219: * cleared already, go call the ->complete() callback directly ! 220: * instead. ! 221: */ ! 222: s->dev.port->ops->complete(s->dev.port, packet); ! 223: } ! 224: ! 225: static USBDevice *usb_hub_find_device(USBDevice *dev, uint8_t addr) ! 226: { ! 227: USBHubState *s = DO_UPCAST(USBHubState, dev, dev); ! 228: USBHubPort *port; ! 229: USBDevice *downstream; ! 230: int i; ! 231: ! 232: for (i = 0; i < NUM_PORTS; i++) { ! 233: port = &s->ports[i]; ! 234: if (!(port->wPortStatus & PORT_STAT_ENABLE)) { ! 235: continue; ! 236: } ! 237: downstream = usb_find_device(&port->port, addr); ! 238: if (downstream != NULL) { ! 239: return downstream; ! 240: } ! 241: } ! 242: return NULL; ! 243: } ! 244: ! 245: static void usb_hub_handle_reset(USBDevice *dev) ! 246: { ! 247: USBHubState *s = DO_UPCAST(USBHubState, dev, dev); ! 248: USBHubPort *port; ! 249: int i; ! 250: ! 251: trace_usb_hub_reset(s->dev.addr); ! 252: for (i = 0; i < NUM_PORTS; i++) { ! 253: port = s->ports + i; ! 254: port->wPortStatus = PORT_STAT_POWER; ! 255: port->wPortChange = 0; ! 256: if (port->port.dev && port->port.dev->attached) { ! 257: port->wPortStatus |= PORT_STAT_CONNECTION; ! 258: port->wPortChange |= PORT_STAT_C_CONNECTION; ! 259: if (port->port.dev->speed == USB_SPEED_LOW) { ! 260: port->wPortStatus |= PORT_STAT_LOW_SPEED; ! 261: } ! 262: } ! 263: } ! 264: } ! 265: ! 266: static const char *feature_name(int feature) ! 267: { ! 268: static const char *name[] = { ! 269: [PORT_CONNECTION] = "connection", ! 270: [PORT_ENABLE] = "enable", ! 271: [PORT_SUSPEND] = "suspend", ! 272: [PORT_OVERCURRENT] = "overcurrent", ! 273: [PORT_RESET] = "reset", ! 274: [PORT_POWER] = "power", ! 275: [PORT_LOWSPEED] = "lowspeed", ! 276: [PORT_HIGHSPEED] = "highspeed", ! 277: [PORT_C_CONNECTION] = "change connection", ! 278: [PORT_C_ENABLE] = "change enable", ! 279: [PORT_C_SUSPEND] = "change suspend", ! 280: [PORT_C_OVERCURRENT] = "change overcurrent", ! 281: [PORT_C_RESET] = "change reset", ! 282: [PORT_TEST] = "test", ! 283: [PORT_INDICATOR] = "indicator", ! 284: }; ! 285: if (feature < 0 || feature >= ARRAY_SIZE(name)) { ! 286: return "?"; ! 287: } ! 288: return name[feature] ?: "?"; ! 289: } ! 290: ! 291: static int usb_hub_handle_control(USBDevice *dev, USBPacket *p, ! 292: int request, int value, int index, int length, uint8_t *data) ! 293: { ! 294: USBHubState *s = (USBHubState *)dev; ! 295: int ret; ! 296: ! 297: trace_usb_hub_control(s->dev.addr, request, value, index, length); ! 298: ! 299: ret = usb_desc_handle_control(dev, p, request, value, index, length, data); ! 300: if (ret >= 0) { ! 301: return ret; ! 302: } ! 303: ! 304: switch(request) { ! 305: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: ! 306: if (value == 0 && index != 0x81) { /* clear ep halt */ ! 307: goto fail; ! 308: } ! 309: ret = 0; ! 310: break; ! 311: /* usb specific requests */ ! 312: case GetHubStatus: ! 313: data[0] = 0; ! 314: data[1] = 0; ! 315: data[2] = 0; ! 316: data[3] = 0; ! 317: ret = 4; ! 318: break; ! 319: case GetPortStatus: ! 320: { ! 321: unsigned int n = index - 1; ! 322: USBHubPort *port; ! 323: if (n >= NUM_PORTS) { ! 324: goto fail; ! 325: } ! 326: port = &s->ports[n]; ! 327: trace_usb_hub_get_port_status(s->dev.addr, index, ! 328: port->wPortStatus, ! 329: port->wPortChange); ! 330: data[0] = port->wPortStatus; ! 331: data[1] = port->wPortStatus >> 8; ! 332: data[2] = port->wPortChange; ! 333: data[3] = port->wPortChange >> 8; ! 334: ret = 4; ! 335: } ! 336: break; ! 337: case SetHubFeature: ! 338: case ClearHubFeature: ! 339: if (value == 0 || value == 1) { ! 340: } else { ! 341: goto fail; ! 342: } ! 343: ret = 0; ! 344: break; ! 345: case SetPortFeature: ! 346: { ! 347: unsigned int n = index - 1; ! 348: USBHubPort *port; ! 349: USBDevice *dev; ! 350: ! 351: trace_usb_hub_set_port_feature(s->dev.addr, index, ! 352: feature_name(value)); ! 353: ! 354: if (n >= NUM_PORTS) { ! 355: goto fail; ! 356: } ! 357: port = &s->ports[n]; ! 358: dev = port->port.dev; ! 359: switch(value) { ! 360: case PORT_SUSPEND: ! 361: port->wPortStatus |= PORT_STAT_SUSPEND; ! 362: break; ! 363: case PORT_RESET: ! 364: if (dev && dev->attached) { ! 365: usb_device_reset(dev); ! 366: port->wPortChange |= PORT_STAT_C_RESET; ! 367: /* set enable bit */ ! 368: port->wPortStatus |= PORT_STAT_ENABLE; ! 369: } ! 370: break; ! 371: case PORT_POWER: ! 372: break; ! 373: default: ! 374: goto fail; ! 375: } ! 376: ret = 0; ! 377: } ! 378: break; ! 379: case ClearPortFeature: ! 380: { ! 381: unsigned int n = index - 1; ! 382: USBHubPort *port; ! 383: ! 384: trace_usb_hub_clear_port_feature(s->dev.addr, index, ! 385: feature_name(value)); ! 386: ! 387: if (n >= NUM_PORTS) { ! 388: goto fail; ! 389: } ! 390: port = &s->ports[n]; ! 391: switch(value) { ! 392: case PORT_ENABLE: ! 393: port->wPortStatus &= ~PORT_STAT_ENABLE; ! 394: break; ! 395: case PORT_C_ENABLE: ! 396: port->wPortChange &= ~PORT_STAT_C_ENABLE; ! 397: break; ! 398: case PORT_SUSPEND: ! 399: port->wPortStatus &= ~PORT_STAT_SUSPEND; ! 400: break; ! 401: case PORT_C_SUSPEND: ! 402: port->wPortChange &= ~PORT_STAT_C_SUSPEND; ! 403: break; ! 404: case PORT_C_CONNECTION: ! 405: port->wPortChange &= ~PORT_STAT_C_CONNECTION; ! 406: break; ! 407: case PORT_C_OVERCURRENT: ! 408: port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; ! 409: break; ! 410: case PORT_C_RESET: ! 411: port->wPortChange &= ~PORT_STAT_C_RESET; ! 412: break; ! 413: default: ! 414: goto fail; ! 415: } ! 416: ret = 0; ! 417: } ! 418: break; ! 419: case GetHubDescriptor: ! 420: { ! 421: unsigned int n, limit, var_hub_size = 0; ! 422: memcpy(data, qemu_hub_hub_descriptor, ! 423: sizeof(qemu_hub_hub_descriptor)); ! 424: data[2] = NUM_PORTS; ! 425: ! 426: /* fill DeviceRemovable bits */ ! 427: limit = ((NUM_PORTS + 1 + 7) / 8) + 7; ! 428: for (n = 7; n < limit; n++) { ! 429: data[n] = 0x00; ! 430: var_hub_size++; ! 431: } ! 432: ! 433: /* fill PortPwrCtrlMask bits */ ! 434: limit = limit + ((NUM_PORTS + 7) / 8); ! 435: for (;n < limit; n++) { ! 436: data[n] = 0xff; ! 437: var_hub_size++; ! 438: } ! 439: ! 440: ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; ! 441: data[0] = ret; ! 442: break; ! 443: } ! 444: default: ! 445: fail: ! 446: ret = USB_RET_STALL; ! 447: break; ! 448: } ! 449: return ret; ! 450: } ! 451: ! 452: static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) ! 453: { ! 454: USBHubState *s = (USBHubState *)dev; ! 455: int ret; ! 456: ! 457: switch(p->pid) { ! 458: case USB_TOKEN_IN: ! 459: if (p->ep->nr == 1) { ! 460: USBHubPort *port; ! 461: unsigned int status; ! 462: uint8_t buf[4]; ! 463: int i, n; ! 464: n = (NUM_PORTS + 1 + 7) / 8; ! 465: if (p->iov.size == 1) { /* FreeBSD workaround */ ! 466: n = 1; ! 467: } else if (n > p->iov.size) { ! 468: return USB_RET_BABBLE; ! 469: } ! 470: status = 0; ! 471: for(i = 0; i < NUM_PORTS; i++) { ! 472: port = &s->ports[i]; ! 473: if (port->wPortChange) ! 474: status |= (1 << (i + 1)); ! 475: } ! 476: if (status != 0) { ! 477: for(i = 0; i < n; i++) { ! 478: buf[i] = status >> (8 * i); ! 479: } ! 480: usb_packet_copy(p, buf, n); ! 481: ret = n; ! 482: } else { ! 483: ret = USB_RET_NAK; /* usb11 11.13.1 */ ! 484: } ! 485: } else { ! 486: goto fail; ! 487: } ! 488: break; ! 489: case USB_TOKEN_OUT: ! 490: default: ! 491: fail: ! 492: ret = USB_RET_STALL; ! 493: break; ! 494: } ! 495: return ret; ! 496: } ! 497: ! 498: static void usb_hub_handle_destroy(USBDevice *dev) ! 499: { ! 500: USBHubState *s = (USBHubState *)dev; ! 501: int i; ! 502: ! 503: for (i = 0; i < NUM_PORTS; i++) { ! 504: usb_unregister_port(usb_bus_from_device(dev), ! 505: &s->ports[i].port); ! 506: } ! 507: } ! 508: ! 509: static USBPortOps usb_hub_port_ops = { ! 510: .attach = usb_hub_attach, ! 511: .detach = usb_hub_detach, ! 512: .child_detach = usb_hub_child_detach, ! 513: .wakeup = usb_hub_wakeup, ! 514: .complete = usb_hub_complete, ! 515: }; ! 516: ! 517: static int usb_hub_initfn(USBDevice *dev) ! 518: { ! 519: USBHubState *s = DO_UPCAST(USBHubState, dev, dev); ! 520: USBHubPort *port; ! 521: int i; ! 522: ! 523: usb_desc_create_serial(dev); ! 524: usb_desc_init(dev); ! 525: s->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); ! 526: for (i = 0; i < NUM_PORTS; i++) { ! 527: port = &s->ports[i]; ! 528: usb_register_port(usb_bus_from_device(dev), ! 529: &port->port, s, i, &usb_hub_port_ops, ! 530: USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL); ! 531: usb_port_location(&port->port, dev->port, i+1); ! 532: } ! 533: usb_hub_handle_reset(dev); ! 534: return 0; ! 535: } ! 536: ! 537: static const VMStateDescription vmstate_usb_hub_port = { ! 538: .name = "usb-hub-port", ! 539: .version_id = 1, ! 540: .minimum_version_id = 1, ! 541: .fields = (VMStateField []) { ! 542: VMSTATE_UINT16(wPortStatus, USBHubPort), ! 543: VMSTATE_UINT16(wPortChange, USBHubPort), ! 544: VMSTATE_END_OF_LIST() ! 545: } ! 546: }; ! 547: ! 548: static const VMStateDescription vmstate_usb_hub = { ! 549: .name = "usb-hub", ! 550: .version_id = 1, ! 551: .minimum_version_id = 1, ! 552: .fields = (VMStateField []) { ! 553: VMSTATE_USB_DEVICE(dev, USBHubState), ! 554: VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0, ! 555: vmstate_usb_hub_port, USBHubPort), ! 556: VMSTATE_END_OF_LIST() ! 557: } ! 558: }; ! 559: ! 560: static void usb_hub_class_initfn(ObjectClass *klass, void *data) ! 561: { ! 562: DeviceClass *dc = DEVICE_CLASS(klass); ! 563: USBDeviceClass *uc = USB_DEVICE_CLASS(klass); ! 564: ! 565: uc->init = usb_hub_initfn; ! 566: uc->product_desc = "QEMU USB Hub"; ! 567: uc->usb_desc = &desc_hub; ! 568: uc->find_device = usb_hub_find_device; ! 569: uc->handle_reset = usb_hub_handle_reset; ! 570: uc->handle_control = usb_hub_handle_control; ! 571: uc->handle_data = usb_hub_handle_data; ! 572: uc->handle_destroy = usb_hub_handle_destroy; ! 573: dc->fw_name = "hub"; ! 574: dc->vmsd = &vmstate_usb_hub; ! 575: } ! 576: ! 577: static TypeInfo hub_info = { ! 578: .name = "usb-hub", ! 579: .parent = TYPE_USB_DEVICE, ! 580: .instance_size = sizeof(USBHubState), ! 581: .class_init = usb_hub_class_initfn, ! 582: }; ! 583: ! 584: static void usb_hub_register_types(void) ! 585: { ! 586: type_register_static(&hub_info); ! 587: } ! 588: ! 589: type_init(usb_hub_register_types)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.