X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/72899afc5d2a33576094a80d1eba797e293c975a..21a88941686b3626aa7b98660b1e1fa88cb064ec:/hw/usb-hub.c diff --git a/hw/usb-hub.c b/hw/usb-hub.c index 4d4cb167f4..1aee7fe5b4 100644 --- a/hw/usb-hub.c +++ b/hw/usb-hub.c @@ -2,7 +2,7 @@ * QEMU USB HUB emulation * * Copyright (c) 2005 Fabrice Bellard - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -21,7 +21,8 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "vl.h" +#include "qemu-common.h" +#include "usb.h" //#define DEBUG @@ -112,13 +113,13 @@ static const uint8_t qemu_hub_config_descriptor[] = { 0x01, /* u8 bNumInterfaces; (1) */ 0x01, /* u8 bConfigurationValue; */ 0x00, /* u8 iConfiguration; */ - 0xc0, /* u8 bmAttributes; + 0xe0, /* u8 bmAttributes; Bit 7: must be set, 6: Self-powered, 5: Remote wakeup, 4..0: resvd */ 0x00, /* u8 MaxPower; */ - + /* USB 1.1: * USB 2.0, single TT organization (mandatory): * one interface, protocol 0 @@ -140,7 +141,7 @@ static const uint8_t qemu_hub_config_descriptor[] = { 0x00, /* u8 if_bInterfaceSubClass; */ 0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ 0x00, /* u8 if_iInterface; */ - + /* one endpoint (status change endpoint) */ 0x07, /* u8 ep_bLength; */ 0x05, /* u8 ep_bDescriptorType; Endpoint */ @@ -152,26 +153,26 @@ static const uint8_t qemu_hub_config_descriptor[] = { static const uint8_t qemu_hub_hub_descriptor[] = { - 0x09, /* u8 bLength; */ + 0x00, /* u8 bLength; patched in later */ 0x29, /* u8 bDescriptorType; Hub-descriptor */ 0x00, /* u8 bNbrPorts; (patched later) */ 0x0a, /* u16 wHubCharacteristics; */ 0x00, /* (per-port OC, no power switching) */ 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* u8 bHubContrCurrent; 0 mA */ - 0x00, /* u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* u8 PortPwrCtrlMask; *** 7 ports max *** */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ + + /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; static void usb_hub_attach(USBPort *port1, USBDevice *dev) { USBHubState *s = port1->opaque; USBHubPort *port = &s->ports[port1->index]; - + if (dev) { if (port->port.dev) usb_attach(port1, NULL); - + port->wPortStatus |= PORT_STAT_CONNECTION; port->wPortChange |= PORT_STAT_C_CONNECTION; if (dev->speed == USB_SPEED_LOW) @@ -179,6 +180,8 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) else port->wPortStatus &= ~PORT_STAT_LOW_SPEED; port->port.dev = dev; + /* send the attach message */ + usb_send_msg(dev, USB_MSG_ATTACH); } else { dev = port->port.dev; if (dev) { @@ -188,6 +191,8 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev) port->wPortStatus &= ~PORT_STAT_ENABLE; port->wPortChange |= PORT_STAT_C_ENABLE; } + /* send the detach message */ + usb_send_msg(dev, USB_MSG_DETACH); port->port.dev = NULL; } } @@ -219,6 +224,12 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, } ret = 0; break; + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: + if (value == 0 && index != 0x81) { /* clear ep halt */ + goto fail; + } + ret = 0; + break; case DeviceOutRequest | USB_REQ_SET_FEATURE: if (value == USB_DEVICE_REMOTE_WAKEUP) { dev->remote_wakeup = 1; @@ -234,13 +245,18 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, case DeviceRequest | USB_REQ_GET_DESCRIPTOR: switch(value >> 8) { case USB_DT_DEVICE: - memcpy(data, qemu_hub_dev_descriptor, + memcpy(data, qemu_hub_dev_descriptor, sizeof(qemu_hub_dev_descriptor)); ret = sizeof(qemu_hub_dev_descriptor); break; case USB_DT_CONFIG: - memcpy(data, qemu_hub_config_descriptor, + memcpy(data, qemu_hub_config_descriptor, sizeof(qemu_hub_config_descriptor)); + + /* status change endpoint size based on number + * of ports */ + data[22] = (s->nb_ports + 1 + 7) / 8; + ret = sizeof(qemu_hub_config_descriptor); break; case USB_DT_STRING: @@ -332,11 +348,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, break; case PORT_RESET: if (dev) { - dev->handle_packet(dev, - USB_MSG_RESET, 0, 0, NULL, 0); + usb_send_msg(dev, USB_MSG_RESET); port->wPortChange |= PORT_STAT_C_RESET; /* set enable bit */ - port->wPortChange |= PORT_STAT_C_ENABLE; port->wPortStatus |= PORT_STAT_ENABLE; } break; @@ -386,11 +400,30 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, } break; case GetHubDescriptor: - memcpy(data, qemu_hub_hub_descriptor, - sizeof(qemu_hub_hub_descriptor)); - data[2] = s->nb_ports; - ret = sizeof(qemu_hub_hub_descriptor); - break; + { + unsigned int n, limit, var_hub_size = 0; + memcpy(data, qemu_hub_hub_descriptor, + sizeof(qemu_hub_hub_descriptor)); + data[2] = s->nb_ports; + + /* fill DeviceRemovable bits */ + limit = ((s->nb_ports + 1 + 7) / 8) + 7; + for (n = 7; n < limit; n++) { + data[n] = 0x00; + var_hub_size++; + } + + /* fill PortPwrCtrlMask bits */ + limit = limit + ((s->nb_ports + 7) / 8); + for (;n < limit; n++) { + data[n] = 0xff; + var_hub_size++; + } + + ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size; + data[0] = ret; + break; + } default: fail: ret = USB_RET_STALL; @@ -399,21 +432,23 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value, return ret; } -static int usb_hub_handle_data(USBDevice *dev, int pid, - uint8_t devep, uint8_t *data, int len) +static int usb_hub_handle_data(USBDevice *dev, USBPacket *p) { USBHubState *s = (USBHubState *)dev; int ret; - switch(pid) { + switch(p->pid) { case USB_TOKEN_IN: - if (devep == 1) { + if (p->devep == 1) { USBHubPort *port; unsigned int status; int i, n; n = (s->nb_ports + 1 + 7) / 8; - if (n > len) + if (p->len == 1) { /* FreeBSD workaround */ + n = 1; + } else if (n > p->len) { return USB_RET_BABBLE; + } status = 0; for(i = 0; i < s->nb_ports; i++) { port = &s->ports[i]; @@ -422,11 +457,11 @@ static int usb_hub_handle_data(USBDevice *dev, int pid, } if (status != 0) { for(i = 0; i < n; i++) { - data[i] = status >> (8 * i); + p->data[i] = status >> (8 * i); } ret = n; } else { - ret = 0; + ret = USB_RET_NAK; /* usb11 11.13.1 */ } } else { goto fail; @@ -441,9 +476,7 @@ static int usb_hub_handle_data(USBDevice *dev, int pid, return ret; } -static int usb_hub_broadcast_packet(USBHubState *s, int pid, - uint8_t devaddr, uint8_t devep, - uint8_t *data, int len) +static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p) { USBHubPort *port; USBDevice *dev; @@ -453,9 +486,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid, port = &s->ports[i]; dev = port->port.dev; if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { - ret = dev->handle_packet(dev, pid, - devaddr, devep, - data, len); + ret = dev->info->handle_packet(dev, p); if (ret != USB_RET_NODEV) { return ret; } @@ -464,9 +495,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, int pid, return USB_RET_NODEV; } -static int usb_hub_handle_packet(USBDevice *dev, int pid, - uint8_t devaddr, uint8_t devep, - uint8_t *data, int len) +static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p) { USBHubState *s = (USBHubState *)dev; @@ -475,44 +504,59 @@ static int usb_hub_handle_packet(USBDevice *dev, int pid, #endif if (dev->state == USB_STATE_DEFAULT && dev->addr != 0 && - devaddr != dev->addr && - (pid == USB_TOKEN_SETUP || - pid == USB_TOKEN_OUT || - pid == USB_TOKEN_IN)) { + p->devaddr != dev->addr && + (p->pid == USB_TOKEN_SETUP || + p->pid == USB_TOKEN_OUT || + p->pid == USB_TOKEN_IN)) { /* broadcast the packet to the devices */ - return usb_hub_broadcast_packet(s, pid, devaddr, devep, data, len); + return usb_hub_broadcast_packet(s, p); } - return usb_generic_handle_packet(dev, pid, devaddr, devep, data, len); + return usb_generic_handle_packet(dev, p); } -USBDevice *usb_hub_init(USBPort **usb_ports, int nb_ports) +static void usb_hub_handle_destroy(USBDevice *dev) { - USBHubState *s; - USBHubPort *port; + USBHubState *s = (USBHubState *)dev; int i; - if (nb_ports > MAX_PORTS) - return NULL; - s = qemu_mallocz(sizeof(USBHubState)); - if (!s) - return NULL; - s->dev.speed = USB_SPEED_FULL; - s->dev.handle_packet = usb_hub_handle_packet; + for (i = 0; i < s->nb_ports; i++) { + usb_unregister_port(usb_bus_from_device(dev), + &s->ports[i].port); + } +} - /* generic USB device init */ - s->dev.handle_reset = usb_hub_handle_reset; - s->dev.handle_control = usb_hub_handle_control; - s->dev.handle_data = usb_hub_handle_data; +static int usb_hub_initfn(USBDevice *dev) +{ + USBHubState *s = DO_UPCAST(USBHubState, dev, dev); + USBHubPort *port; + int i; - s->nb_ports = nb_ports; - for(i = 0; i < s->nb_ports; i++) { + s->dev.speed = USB_SPEED_FULL, + s->nb_ports = MAX_PORTS; /* FIXME: make configurable */ + for (i = 0; i < s->nb_ports; i++) { port = &s->ports[i]; + usb_register_port(usb_bus_from_device(dev), + &port->port, s, i, usb_hub_attach); port->wPortStatus = PORT_STAT_POWER; port->wPortChange = 0; - port->port.attach = usb_hub_attach; - port->port.opaque = s; - port->port.index = i; - usb_ports[i] = &port->port; } - return (USBDevice *)s; + return 0; +} + +static struct USBDeviceInfo hub_info = { + .product_desc = "QEMU USB Hub", + .qdev.name = "usb-hub", + .qdev.size = sizeof(USBHubState), + .init = usb_hub_initfn, + .handle_packet = usb_hub_handle_packet, + .handle_reset = usb_hub_handle_reset, + .handle_control = usb_hub_handle_control, + .handle_data = usb_hub_handle_data, + .handle_destroy = usb_hub_handle_destroy, +}; + +static void usb_hub_register_devices(void) +{ + usb_qdev_register(&hub_info); } +device_init(usb_hub_register_devices)