|
|
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 "vl.h" ! 25: ! 26: //#define DEBUG ! 27: ! 28: #define MAX_PORTS 8 ! 29: ! 30: typedef struct USBHubPort { ! 31: USBPort port; ! 32: uint16_t wPortStatus; ! 33: uint16_t wPortChange; ! 34: } USBHubPort; ! 35: ! 36: typedef struct USBHubState { ! 37: USBDevice dev; ! 38: int nb_ports; ! 39: USBHubPort ports[MAX_PORTS]; ! 40: } USBHubState; ! 41: ! 42: #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) ! 43: #define ClearPortFeature (0x2300 | USB_REQ_CLEAR_FEATURE) ! 44: #define GetHubDescriptor (0xa000 | USB_REQ_GET_DESCRIPTOR) ! 45: #define GetHubStatus (0xa000 | USB_REQ_GET_STATUS) ! 46: #define GetPortStatus (0xa300 | USB_REQ_GET_STATUS) ! 47: #define SetHubFeature (0x2000 | USB_REQ_SET_FEATURE) ! 48: #define SetPortFeature (0x2300 | USB_REQ_SET_FEATURE) ! 49: ! 50: #define PORT_STAT_CONNECTION 0x0001 ! 51: #define PORT_STAT_ENABLE 0x0002 ! 52: #define PORT_STAT_SUSPEND 0x0004 ! 53: #define PORT_STAT_OVERCURRENT 0x0008 ! 54: #define PORT_STAT_RESET 0x0010 ! 55: #define PORT_STAT_POWER 0x0100 ! 56: #define PORT_STAT_LOW_SPEED 0x0200 ! 57: #define PORT_STAT_HIGH_SPEED 0x0400 ! 58: #define PORT_STAT_TEST 0x0800 ! 59: #define PORT_STAT_INDICATOR 0x1000 ! 60: ! 61: #define PORT_STAT_C_CONNECTION 0x0001 ! 62: #define PORT_STAT_C_ENABLE 0x0002 ! 63: #define PORT_STAT_C_SUSPEND 0x0004 ! 64: #define PORT_STAT_C_OVERCURRENT 0x0008 ! 65: #define PORT_STAT_C_RESET 0x0010 ! 66: ! 67: #define PORT_CONNECTION 0 ! 68: #define PORT_ENABLE 1 ! 69: #define PORT_SUSPEND 2 ! 70: #define PORT_OVERCURRENT 3 ! 71: #define PORT_RESET 4 ! 72: #define PORT_POWER 8 ! 73: #define PORT_LOWSPEED 9 ! 74: #define PORT_HIGHSPEED 10 ! 75: #define PORT_C_CONNECTION 16 ! 76: #define PORT_C_ENABLE 17 ! 77: #define PORT_C_SUSPEND 18 ! 78: #define PORT_C_OVERCURRENT 19 ! 79: #define PORT_C_RESET 20 ! 80: #define PORT_TEST 21 ! 81: #define PORT_INDICATOR 22 ! 82: ! 83: /* same as Linux kernel root hubs */ ! 84: ! 85: static const uint8_t qemu_hub_dev_descriptor[] = { ! 86: 0x12, /* u8 bLength; */ ! 87: 0x01, /* u8 bDescriptorType; Device */ ! 88: 0x10, 0x01, /* u16 bcdUSB; v1.1 */ ! 89: ! 90: 0x09, /* u8 bDeviceClass; HUB_CLASSCODE */ ! 91: 0x00, /* u8 bDeviceSubClass; */ ! 92: 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */ ! 93: 0x08, /* u8 bMaxPacketSize0; 8 Bytes */ ! 94: ! 95: 0x00, 0x00, /* u16 idVendor; */ ! 96: 0x00, 0x00, /* u16 idProduct; */ ! 97: 0x01, 0x01, /* u16 bcdDevice */ ! 98: ! 99: 0x03, /* u8 iManufacturer; */ ! 100: 0x02, /* u8 iProduct; */ ! 101: 0x01, /* u8 iSerialNumber; */ ! 102: 0x01 /* u8 bNumConfigurations; */ ! 103: }; ! 104: ! 105: /* XXX: patch interrupt size */ ! 106: static const uint8_t qemu_hub_config_descriptor[] = { ! 107: ! 108: /* one configuration */ ! 109: 0x09, /* u8 bLength; */ ! 110: 0x02, /* u8 bDescriptorType; Configuration */ ! 111: 0x19, 0x00, /* u16 wTotalLength; */ ! 112: 0x01, /* u8 bNumInterfaces; (1) */ ! 113: 0x01, /* u8 bConfigurationValue; */ ! 114: 0x00, /* u8 iConfiguration; */ ! 115: 0xc0, /* u8 bmAttributes; ! 116: Bit 7: must be set, ! 117: 6: Self-powered, ! 118: 5: Remote wakeup, ! 119: 4..0: resvd */ ! 120: 0x00, /* u8 MaxPower; */ ! 121: ! 122: /* USB 1.1: ! 123: * USB 2.0, single TT organization (mandatory): ! 124: * one interface, protocol 0 ! 125: * ! 126: * USB 2.0, multiple TT organization (optional): ! 127: * two interfaces, protocols 1 (like single TT) ! 128: * and 2 (multiple TT mode) ... config is ! 129: * sometimes settable ! 130: * NOT IMPLEMENTED ! 131: */ ! 132: ! 133: /* one interface */ ! 134: 0x09, /* u8 if_bLength; */ ! 135: 0x04, /* u8 if_bDescriptorType; Interface */ ! 136: 0x00, /* u8 if_bInterfaceNumber; */ ! 137: 0x00, /* u8 if_bAlternateSetting; */ ! 138: 0x01, /* u8 if_bNumEndpoints; */ ! 139: 0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ ! 140: 0x00, /* u8 if_bInterfaceSubClass; */ ! 141: 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ ! 142: 0x00, /* u8 if_iInterface; */ ! 143: ! 144: /* one endpoint (status change endpoint) */ ! 145: 0x07, /* u8 ep_bLength; */ ! 146: 0x05, /* u8 ep_bDescriptorType; Endpoint */ ! 147: 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ ! 148: 0x03, /* u8 ep_bmAttributes; Interrupt */ ! 149: 0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ ! 150: 0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */ ! 151: }; ! 152: ! 153: static const uint8_t qemu_hub_hub_descriptor[] = ! 154: { ! 155: 0x09, /* u8 bLength; */ ! 156: 0x29, /* u8 bDescriptorType; Hub-descriptor */ ! 157: 0x00, /* u8 bNbrPorts; (patched later) */ ! 158: 0x0a, /* u16 wHubCharacteristics; */ ! 159: 0x00, /* (per-port OC, no power switching) */ ! 160: 0x01, /* u8 bPwrOn2pwrGood; 2ms */ ! 161: 0x00 /* u8 bHubContrCurrent; 0 mA */ ! 162: ! 163: /* DeviceRemovable and PortPwrCtrlMask patched in later */ ! 164: }; ! 165: ! 166: static void usb_hub_attach(USBPort *port1, USBDevice *dev) ! 167: { ! 168: USBHubState *s = port1->opaque; ! 169: USBHubPort *port = &s->ports[port1->index]; ! 170: ! 171: if (dev) { ! 172: if (port->port.dev) ! 173: usb_attach(port1, NULL); ! 174: ! 175: port->wPortStatus |= PORT_STAT_CONNECTION; ! 176: port->wPortChange |= PORT_STAT_C_CONNECTION; ! 177: if (dev->speed == USB_SPEED_LOW) ! 178: port->wPortStatus |= PORT_STAT_LOW_SPEED; ! 179: else ! 180: port->wPortStatus &= ~PORT_STAT_LOW_SPEED; ! 181: port->port.dev = dev; ! 182: } else { ! 183: dev = port->port.dev; ! 184: if (dev) { ! 185: port->wPortStatus &= ~PORT_STAT_CONNECTION; ! 186: port->wPortChange |= PORT_STAT_C_CONNECTION; ! 187: if (port->wPortStatus & PORT_STAT_ENABLE) { ! 188: port->wPortStatus &= ~PORT_STAT_ENABLE; ! 189: port->wPortChange |= PORT_STAT_C_ENABLE; ! 190: } ! 191: port->port.dev = NULL; ! 192: } ! 193: } ! 194: } ! 195: ! 196: static void usb_hub_handle_reset(USBDevice *dev) ! 197: { ! 198: /* XXX: do it */ ! 199: } ! 200: ! 201: static int usb_hub_handle_control(USBDevice *dev, int request, int value, ! 202: int index, int length, uint8_t *data) ! 203: { ! 204: USBHubState *s = (USBHubState *)dev; ! 205: int ret; ! 206: ! 207: switch(request) { ! 208: case DeviceRequest | USB_REQ_GET_STATUS: ! 209: data[0] = (1 << USB_DEVICE_SELF_POWERED) | ! 210: (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); ! 211: data[1] = 0x00; ! 212: ret = 2; ! 213: break; ! 214: case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: ! 215: if (value == USB_DEVICE_REMOTE_WAKEUP) { ! 216: dev->remote_wakeup = 0; ! 217: } else { ! 218: goto fail; ! 219: } ! 220: ret = 0; ! 221: break; ! 222: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: ! 223: if (value == 0 && index != 0x81) { /* clear ep halt */ ! 224: goto fail; ! 225: } ! 226: ret = 0; ! 227: break; ! 228: case DeviceOutRequest | USB_REQ_SET_FEATURE: ! 229: if (value == USB_DEVICE_REMOTE_WAKEUP) { ! 230: dev->remote_wakeup = 1; ! 231: } else { ! 232: goto fail; ! 233: } ! 234: ret = 0; ! 235: break; ! 236: case DeviceOutRequest | USB_REQ_SET_ADDRESS: ! 237: dev->addr = value; ! 238: ret = 0; ! 239: break; ! 240: case DeviceRequest | USB_REQ_GET_DESCRIPTOR: ! 241: switch(value >> 8) { ! 242: case USB_DT_DEVICE: ! 243: memcpy(data, qemu_hub_dev_descriptor, ! 244: sizeof(qemu_hub_dev_descriptor)); ! 245: ret = sizeof(qemu_hub_dev_descriptor); ! 246: break; ! 247: case USB_DT_CONFIG: ! 248: memcpy(data, qemu_hub_config_descriptor, ! 249: sizeof(qemu_hub_config_descriptor)); ! 250: ! 251: /* status change endpoint size based on number ! 252: * of ports */ ! 253: data[22] = (s->nb_ports + 1 + 7) / 8; ! 254: ! 255: ret = sizeof(qemu_hub_config_descriptor); ! 256: break; ! 257: case USB_DT_STRING: ! 258: switch(value & 0xff) { ! 259: case 0: ! 260: /* language ids */ ! 261: data[0] = 4; ! 262: data[1] = 3; ! 263: data[2] = 0x09; ! 264: data[3] = 0x04; ! 265: ret = 4; ! 266: break; ! 267: case 1: ! 268: /* serial number */ ! 269: ret = set_usb_string(data, "314159"); ! 270: break; ! 271: case 2: ! 272: /* product description */ ! 273: ret = set_usb_string(data, "QEMU USB Hub"); ! 274: break; ! 275: case 3: ! 276: /* vendor description */ ! 277: ret = set_usb_string(data, "QEMU " QEMU_VERSION); ! 278: break; ! 279: default: ! 280: goto fail; ! 281: } ! 282: break; ! 283: default: ! 284: goto fail; ! 285: } ! 286: break; ! 287: case DeviceRequest | USB_REQ_GET_CONFIGURATION: ! 288: data[0] = 1; ! 289: ret = 1; ! 290: break; ! 291: case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: ! 292: ret = 0; ! 293: break; ! 294: case DeviceRequest | USB_REQ_GET_INTERFACE: ! 295: data[0] = 0; ! 296: ret = 1; ! 297: break; ! 298: case DeviceOutRequest | USB_REQ_SET_INTERFACE: ! 299: ret = 0; ! 300: break; ! 301: /* usb specific requests */ ! 302: case GetHubStatus: ! 303: data[0] = 0; ! 304: data[1] = 0; ! 305: data[2] = 0; ! 306: data[3] = 0; ! 307: ret = 4; ! 308: break; ! 309: case GetPortStatus: ! 310: { ! 311: unsigned int n = index - 1; ! 312: USBHubPort *port; ! 313: if (n >= s->nb_ports) ! 314: goto fail; ! 315: port = &s->ports[n]; ! 316: data[0] = port->wPortStatus; ! 317: data[1] = port->wPortStatus >> 8; ! 318: data[2] = port->wPortChange; ! 319: data[3] = port->wPortChange >> 8; ! 320: ret = 4; ! 321: } ! 322: break; ! 323: case SetHubFeature: ! 324: case ClearHubFeature: ! 325: if (value == 0 || value == 1) { ! 326: } else { ! 327: goto fail; ! 328: } ! 329: ret = 0; ! 330: break; ! 331: case SetPortFeature: ! 332: { ! 333: unsigned int n = index - 1; ! 334: USBHubPort *port; ! 335: USBDevice *dev; ! 336: if (n >= s->nb_ports) ! 337: goto fail; ! 338: port = &s->ports[n]; ! 339: dev = port->port.dev; ! 340: switch(value) { ! 341: case PORT_SUSPEND: ! 342: port->wPortStatus |= PORT_STAT_SUSPEND; ! 343: break; ! 344: case PORT_RESET: ! 345: if (dev) { ! 346: dev->handle_packet(dev, ! 347: USB_MSG_RESET, 0, 0, NULL, 0); ! 348: port->wPortChange |= PORT_STAT_C_RESET; ! 349: /* set enable bit */ ! 350: port->wPortStatus |= PORT_STAT_ENABLE; ! 351: } ! 352: break; ! 353: case PORT_POWER: ! 354: break; ! 355: default: ! 356: goto fail; ! 357: } ! 358: ret = 0; ! 359: } ! 360: break; ! 361: case ClearPortFeature: ! 362: { ! 363: unsigned int n = index - 1; ! 364: USBHubPort *port; ! 365: USBDevice *dev; ! 366: if (n >= s->nb_ports) ! 367: goto fail; ! 368: port = &s->ports[n]; ! 369: dev = port->port.dev; ! 370: switch(value) { ! 371: case PORT_ENABLE: ! 372: port->wPortStatus &= ~PORT_STAT_ENABLE; ! 373: break; ! 374: case PORT_C_ENABLE: ! 375: port->wPortChange &= ~PORT_STAT_C_ENABLE; ! 376: break; ! 377: case PORT_SUSPEND: ! 378: port->wPortStatus &= ~PORT_STAT_SUSPEND; ! 379: break; ! 380: case PORT_C_SUSPEND: ! 381: port->wPortChange &= ~PORT_STAT_C_SUSPEND; ! 382: break; ! 383: case PORT_C_CONNECTION: ! 384: port->wPortChange &= ~PORT_STAT_C_CONNECTION; ! 385: break; ! 386: case PORT_C_OVERCURRENT: ! 387: port->wPortChange &= ~PORT_STAT_C_OVERCURRENT; ! 388: break; ! 389: case PORT_C_RESET: ! 390: port->wPortChange &= ~PORT_STAT_C_RESET; ! 391: break; ! 392: default: ! 393: goto fail; ! 394: } ! 395: ret = 0; ! 396: } ! 397: break; ! 398: case GetHubDescriptor: ! 399: { ! 400: unsigned int n, limit, var_hub_size = 0; ! 401: memcpy(data, qemu_hub_hub_descriptor, ! 402: sizeof(qemu_hub_hub_descriptor)); ! 403: data[2] = s->nb_ports; ! 404: ! 405: /* fill DeviceRemovable bits */ ! 406: limit = ((s->nb_ports + 1 + 7) / 8) + 7; ! 407: for (n = 7; n < limit; n++) { ! 408: data[n] = 0x00; ! 409: var_hub_size++; ! 410: } ! 411: ! 412: /* fill PortPwrCtrlMask bits */ ! 413: limit = limit + ((s->nb_ports + 7) / 8); ! 414: for (;n < limit; n++) { ! 415: data[n] = 0xff; ! 416: var_hub_size++; ! 417: } ! 418: ! 419: ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; ! 420: break; ! 421: } ! 422: default: ! 423: fail: ! 424: ret = USB_RET_STALL; ! 425: break; ! 426: } ! 427: return ret; ! 428: } ! 429: ! 430: static int usb_hub_handle_data(USBDevice *dev, int pid, ! 431: uint8_t devep, uint8_t *data, int len) ! 432: { ! 433: USBHubState *s = (USBHubState *)dev; ! 434: int ret; ! 435: ! 436: switch(pid) { ! 437: case USB_TOKEN_IN: ! 438: if (devep == 1) { ! 439: USBHubPort *port; ! 440: unsigned int status; ! 441: int i, n; ! 442: n = (s->nb_ports + 1 + 7) / 8; ! 443: if (len == 1) { /* FreeBSD workaround */ ! 444: n = 1; ! 445: } else if (n > len) { ! 446: return USB_RET_BABBLE; ! 447: } ! 448: status = 0; ! 449: for(i = 0; i < s->nb_ports; i++) { ! 450: port = &s->ports[i]; ! 451: if (port->wPortChange) ! 452: status |= (1 << (i + 1)); ! 453: } ! 454: if (status != 0) { ! 455: for(i = 0; i < n; i++) { ! 456: data[i] = status >> (8 * i); ! 457: } ! 458: ret = n; ! 459: } else { ! 460: ret = USB_RET_NAK; /* usb11 11.13.1 */ ! 461: } ! 462: } else { ! 463: goto fail; ! 464: } ! 465: break; ! 466: case USB_TOKEN_OUT: ! 467: default: ! 468: fail: ! 469: ret = USB_RET_STALL; ! 470: break; ! 471: } ! 472: return ret; ! 473: } ! 474: ! 475: static int usb_hub_broadcast_packet(USBHubState *s, int pid, ! 476: uint8_t devaddr, uint8_t devep, ! 477: uint8_t *data, int len) ! 478: { ! 479: USBHubPort *port; ! 480: USBDevice *dev; ! 481: int i, ret; ! 482: ! 483: for(i = 0; i < s->nb_ports; i++) { ! 484: port = &s->ports[i]; ! 485: dev = port->port.dev; ! 486: if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { ! 487: ret = dev->handle_packet(dev, pid, ! 488: devaddr, devep, ! 489: data, len); ! 490: if (ret != USB_RET_NODEV) { ! 491: return ret; ! 492: } ! 493: } ! 494: } ! 495: return USB_RET_NODEV; ! 496: } ! 497: ! 498: static int usb_hub_handle_packet(USBDevice *dev, int pid, ! 499: uint8_t devaddr, uint8_t devep, ! 500: uint8_t *data, int len) ! 501: { ! 502: USBHubState *s = (USBHubState *)dev; ! 503: ! 504: #if defined(DEBUG) && 0 ! 505: printf("usb_hub: pid=0x%x\n", pid); ! 506: #endif ! 507: if (dev->state == USB_STATE_DEFAULT && ! 508: dev->addr != 0 && ! 509: devaddr != dev->addr && ! 510: (pid == USB_TOKEN_SETUP || ! 511: pid == USB_TOKEN_OUT || ! 512: pid == USB_TOKEN_IN)) { ! 513: /* broadcast the packet to the devices */ ! 514: return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); ! 515: } ! 516: return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); ! 517: } ! 518: ! 519: USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) ! 520: { ! 521: USBHubState *s; ! 522: USBHubPort *port; ! 523: int i; ! 524: ! 525: if (nb_ports > MAX_PORTS) ! 526: return NULL; ! 527: s = qemu_mallocz(sizeof(USBHubState)); ! 528: if (!s) ! 529: return NULL; ! 530: s->dev.speed = USB_SPEED_FULL; ! 531: s->dev.handle_packet = usb_hub_handle_packet; ! 532: ! 533: /* generic USB device init */ ! 534: s->dev.handle_reset = usb_hub_handle_reset; ! 535: s->dev.handle_control = usb_hub_handle_control; ! 536: s->dev.handle_data = usb_hub_handle_data; ! 537: ! 538: s->nb_ports = nb_ports; ! 539: for(i = 0; i < s->nb_ports; i++) { ! 540: port = &s->ports[i]; ! 541: port->wPortStatus = PORT_STAT_POWER; ! 542: port->wPortChange = 0; ! 543: port->port.attach = usb_hub_attach; ! 544: port->port.opaque = s; ! 545: port->port.index = i; ! 546: usb_ports[i] = &port->port; ! 547: } ! 548: return (USBDevice *)s; ! 549: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.