aboutsummaryrefslogtreecommitdiff
path: root/hw/usb-desc.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/usb-desc.c')
-rw-r--r--hw/usb-desc.c143
1 files changed, 132 insertions, 11 deletions
diff --git a/hw/usb-desc.c b/hw/usb-desc.c
index ae2d384bb3..b3eb97bc6b 100644
--- a/hw/usb-desc.c
+++ b/hw/usb-desc.c
@@ -192,9 +192,10 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
{
- uint8_t bLength = 0x07;
+ uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
+ uint8_t extralen = ep->extra ? ep->extra[0] : 0;
- if (len < bLength) {
+ if (len < bLength + extralen) {
return -1;
}
@@ -205,8 +206,15 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
dest[0x04] = usb_lo(ep->wMaxPacketSize);
dest[0x05] = usb_hi(ep->wMaxPacketSize);
dest[0x06] = ep->bInterval;
+ if (ep->is_audio) {
+ dest[0x07] = ep->bRefresh;
+ dest[0x08] = ep->bSynchAddress;
+ }
+ if (ep->extra) {
+ memcpy(dest + bLength, ep->extra, extralen);
+ }
- return bLength;
+ return bLength + extralen;
}
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
@@ -223,6 +231,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
/* ------------------------------------------------------------------ */
+static void usb_desc_ep_init(USBDevice *dev)
+{
+ const USBDescIface *iface;
+ int i, e, pid, ep;
+
+ usb_ep_init(dev);
+ for (i = 0; i < dev->ninterfaces; i++) {
+ iface = dev->ifaces[i];
+ if (iface == NULL) {
+ continue;
+ }
+ for (e = 0; e < iface->bNumEndpoints; e++) {
+ pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
+ USB_TOKEN_IN : USB_TOKEN_OUT;
+ ep = iface->eps[e].bEndpointAddress & 0x0f;
+ usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
+ usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
+ usb_ep_set_max_packet_size(dev, pid, ep,
+ iface->eps[e].wMaxPacketSize);
+ }
+ }
+}
+
+static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
+ int nif, int alt)
+{
+ const USBDescIface *iface;
+ int g, i;
+
+ if (!dev->config) {
+ return NULL;
+ }
+ for (g = 0; g < dev->config->nif_groups; g++) {
+ for (i = 0; i < dev->config->if_groups[g].nif; i++) {
+ iface = &dev->config->if_groups[g].ifs[i];
+ if (iface->bInterfaceNumber == nif &&
+ iface->bAlternateSetting == alt) {
+ return iface;
+ }
+ }
+ }
+ for (i = 0; i < dev->config->nif; i++) {
+ iface = &dev->config->ifs[i];
+ if (iface->bInterfaceNumber == nif &&
+ iface->bAlternateSetting == alt) {
+ return iface;
+ }
+ }
+ return NULL;
+}
+
+static int usb_desc_set_interface(USBDevice *dev, int index, int value)
+{
+ const USBDescIface *iface;
+ int old;
+
+ iface = usb_desc_find_interface(dev, index, value);
+ if (iface == NULL) {
+ return -1;
+ }
+
+ old = dev->altsetting[index];
+ dev->altsetting[index] = value;
+ dev->ifaces[index] = iface;
+ usb_desc_ep_init(dev);
+
+ if (dev->info->set_interface && old != value) {
+ dev->info->set_interface(dev, index, old, value);
+ }
+ return 0;
+}
+
+static int usb_desc_set_config(USBDevice *dev, int value)
+{
+ int i;
+
+ if (value == 0) {
+ dev->configuration = 0;
+ dev->ninterfaces = 0;
+ dev->config = NULL;
+ } else {
+ for (i = 0; i < dev->device->bNumConfigurations; i++) {
+ if (dev->device->confs[i].bConfigurationValue == value) {
+ dev->configuration = value;
+ dev->ninterfaces = dev->device->confs[i].bNumInterfaces;
+ dev->config = dev->device->confs + i;
+ assert(dev->ninterfaces <= USB_MAX_INTERFACES);
+ }
+ }
+ if (i < dev->device->bNumConfigurations) {
+ return -1;
+ }
+ }
+
+ for (i = 0; i < dev->ninterfaces; i++) {
+ usb_desc_set_interface(dev, i, 0);
+ }
+ for (; i < USB_MAX_INTERFACES; i++) {
+ dev->altsetting[i] = 0;
+ dev->ifaces[i] = NULL;
+ }
+
+ return 0;
+}
+
static void usb_desc_setdefaults(USBDevice *dev)
{
const USBDesc *desc = dev->info->usb_desc;
@@ -237,7 +350,7 @@ static void usb_desc_setdefaults(USBDevice *dev)
dev->device = desc->high;
break;
}
- dev->config = dev->device->confs;
+ usb_desc_set_config(dev, 0);
}
void usb_desc_init(USBDevice *dev)
@@ -408,7 +521,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
const USBDesc *desc = dev->info->usb_desc;
- int i, ret = -1;
+ int ret = -1;
assert(desc != NULL);
switch(request) {
@@ -427,12 +540,7 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
- for (i = 0; i < dev->device->bNumConfigurations; i++) {
- if (dev->device->confs[i].bConfigurationValue == value) {
- dev->config = dev->device->confs + i;
- ret = 0;
- }
- }
+ ret = usb_desc_set_config(dev, value);
trace_usb_set_config(dev->addr, value, ret);
break;
@@ -461,6 +569,19 @@ int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
}
trace_usb_set_device_feature(dev->addr, value, ret);
break;
+
+ case InterfaceRequest | USB_REQ_GET_INTERFACE:
+ if (index < 0 || index >= dev->ninterfaces) {
+ break;
+ }
+ data[0] = dev->altsetting[index];
+ ret = 1;
+ break;
+ case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+ ret = usb_desc_set_interface(dev, index, value);
+ trace_usb_set_interface(dev->addr, index, value, ret);
+ break;
+
}
return ret;
}