xref: /openbmc/qemu/hw/usb/desc.c (revision e36a20d3)
1f1ae32a1SGerd Hoffmann #include "hw/usb.h"
2f1ae32a1SGerd Hoffmann #include "hw/usb/desc.h"
3f1ae32a1SGerd Hoffmann #include "trace.h"
4f1ae32a1SGerd Hoffmann 
5f1ae32a1SGerd Hoffmann /* ------------------------------------------------------------------ */
6f1ae32a1SGerd Hoffmann 
7f1ae32a1SGerd Hoffmann static uint8_t usb_lo(uint16_t val)
8f1ae32a1SGerd Hoffmann {
9f1ae32a1SGerd Hoffmann     return val & 0xff;
10f1ae32a1SGerd Hoffmann }
11f1ae32a1SGerd Hoffmann 
12f1ae32a1SGerd Hoffmann static uint8_t usb_hi(uint16_t val)
13f1ae32a1SGerd Hoffmann {
14f1ae32a1SGerd Hoffmann     return (val >> 8) & 0xff;
15f1ae32a1SGerd Hoffmann }
16f1ae32a1SGerd Hoffmann 
17f1ae32a1SGerd Hoffmann int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
18f1ae32a1SGerd Hoffmann                     uint8_t *dest, size_t len)
19f1ae32a1SGerd Hoffmann {
20f1ae32a1SGerd Hoffmann     uint8_t bLength = 0x12;
21d3f904eaSGerd Hoffmann     USBDescriptor *d = (void *)dest;
22f1ae32a1SGerd Hoffmann 
23f1ae32a1SGerd Hoffmann     if (len < bLength) {
24f1ae32a1SGerd Hoffmann         return -1;
25f1ae32a1SGerd Hoffmann     }
26f1ae32a1SGerd Hoffmann 
27d3f904eaSGerd Hoffmann     d->bLength                     = bLength;
28d3f904eaSGerd Hoffmann     d->bDescriptorType             = USB_DT_DEVICE;
29f1ae32a1SGerd Hoffmann 
30d3f904eaSGerd Hoffmann     d->u.device.bcdUSB_lo          = usb_lo(dev->bcdUSB);
31d3f904eaSGerd Hoffmann     d->u.device.bcdUSB_hi          = usb_hi(dev->bcdUSB);
32d3f904eaSGerd Hoffmann     d->u.device.bDeviceClass       = dev->bDeviceClass;
33d3f904eaSGerd Hoffmann     d->u.device.bDeviceSubClass    = dev->bDeviceSubClass;
34d3f904eaSGerd Hoffmann     d->u.device.bDeviceProtocol    = dev->bDeviceProtocol;
35d3f904eaSGerd Hoffmann     d->u.device.bMaxPacketSize0    = dev->bMaxPacketSize0;
36f1ae32a1SGerd Hoffmann 
37d3f904eaSGerd Hoffmann     d->u.device.idVendor_lo        = usb_lo(id->idVendor);
38d3f904eaSGerd Hoffmann     d->u.device.idVendor_hi        = usb_hi(id->idVendor);
39d3f904eaSGerd Hoffmann     d->u.device.idProduct_lo       = usb_lo(id->idProduct);
40d3f904eaSGerd Hoffmann     d->u.device.idProduct_hi       = usb_hi(id->idProduct);
41d3f904eaSGerd Hoffmann     d->u.device.bcdDevice_lo       = usb_lo(id->bcdDevice);
42d3f904eaSGerd Hoffmann     d->u.device.bcdDevice_hi       = usb_hi(id->bcdDevice);
43d3f904eaSGerd Hoffmann     d->u.device.iManufacturer      = id->iManufacturer;
44d3f904eaSGerd Hoffmann     d->u.device.iProduct           = id->iProduct;
45d3f904eaSGerd Hoffmann     d->u.device.iSerialNumber      = id->iSerialNumber;
46f1ae32a1SGerd Hoffmann 
47d3f904eaSGerd Hoffmann     d->u.device.bNumConfigurations = dev->bNumConfigurations;
48f1ae32a1SGerd Hoffmann 
49f1ae32a1SGerd Hoffmann     return bLength;
50f1ae32a1SGerd Hoffmann }
51f1ae32a1SGerd Hoffmann 
52f1ae32a1SGerd Hoffmann int usb_desc_device_qualifier(const USBDescDevice *dev,
53f1ae32a1SGerd Hoffmann                               uint8_t *dest, size_t len)
54f1ae32a1SGerd Hoffmann {
55f1ae32a1SGerd Hoffmann     uint8_t bLength = 0x0a;
563cfeee61SGerd Hoffmann     USBDescriptor *d = (void *)dest;
57f1ae32a1SGerd Hoffmann 
58f1ae32a1SGerd Hoffmann     if (len < bLength) {
59f1ae32a1SGerd Hoffmann         return -1;
60f1ae32a1SGerd Hoffmann     }
61f1ae32a1SGerd Hoffmann 
623cfeee61SGerd Hoffmann     d->bLength                               = bLength;
633cfeee61SGerd Hoffmann     d->bDescriptorType                       = USB_DT_DEVICE_QUALIFIER;
64f1ae32a1SGerd Hoffmann 
653cfeee61SGerd Hoffmann     d->u.device_qualifier.bcdUSB_lo          = usb_lo(dev->bcdUSB);
663cfeee61SGerd Hoffmann     d->u.device_qualifier.bcdUSB_hi          = usb_hi(dev->bcdUSB);
673cfeee61SGerd Hoffmann     d->u.device_qualifier.bDeviceClass       = dev->bDeviceClass;
683cfeee61SGerd Hoffmann     d->u.device_qualifier.bDeviceSubClass    = dev->bDeviceSubClass;
693cfeee61SGerd Hoffmann     d->u.device_qualifier.bDeviceProtocol    = dev->bDeviceProtocol;
703cfeee61SGerd Hoffmann     d->u.device_qualifier.bMaxPacketSize0    = dev->bMaxPacketSize0;
713cfeee61SGerd Hoffmann     d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
723cfeee61SGerd Hoffmann     d->u.device_qualifier.bReserved          = 0;
73f1ae32a1SGerd Hoffmann 
74f1ae32a1SGerd Hoffmann     return bLength;
75f1ae32a1SGerd Hoffmann }
76f1ae32a1SGerd Hoffmann 
77f1ae32a1SGerd Hoffmann int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
78f1ae32a1SGerd Hoffmann {
79f1ae32a1SGerd Hoffmann     uint8_t  bLength = 0x09;
80f1ae32a1SGerd Hoffmann     uint16_t wTotalLength = 0;
810a263db1SGerd Hoffmann     USBDescriptor *d = (void *)dest;
82f1ae32a1SGerd Hoffmann     int i, rc;
83f1ae32a1SGerd Hoffmann 
84f1ae32a1SGerd Hoffmann     if (len < bLength) {
85f1ae32a1SGerd Hoffmann         return -1;
86f1ae32a1SGerd Hoffmann     }
87f1ae32a1SGerd Hoffmann 
880a263db1SGerd Hoffmann     d->bLength                      = bLength;
890a263db1SGerd Hoffmann     d->bDescriptorType              = USB_DT_CONFIG;
900a263db1SGerd Hoffmann 
910a263db1SGerd Hoffmann     d->u.config.bNumInterfaces      = conf->bNumInterfaces;
920a263db1SGerd Hoffmann     d->u.config.bConfigurationValue = conf->bConfigurationValue;
930a263db1SGerd Hoffmann     d->u.config.iConfiguration      = conf->iConfiguration;
940a263db1SGerd Hoffmann     d->u.config.bmAttributes        = conf->bmAttributes;
950a263db1SGerd Hoffmann     d->u.config.bMaxPower           = conf->bMaxPower;
96f1ae32a1SGerd Hoffmann     wTotalLength += bLength;
97f1ae32a1SGerd Hoffmann 
98f1ae32a1SGerd Hoffmann     /* handle grouped interfaces if any */
99f1ae32a1SGerd Hoffmann     for (i = 0; i < conf->nif_groups; i++) {
100f1ae32a1SGerd Hoffmann         rc = usb_desc_iface_group(&(conf->if_groups[i]),
101f1ae32a1SGerd Hoffmann                                   dest + wTotalLength,
102f1ae32a1SGerd Hoffmann                                   len - wTotalLength);
103f1ae32a1SGerd Hoffmann         if (rc < 0) {
104f1ae32a1SGerd Hoffmann             return rc;
105f1ae32a1SGerd Hoffmann         }
106f1ae32a1SGerd Hoffmann         wTotalLength += rc;
107f1ae32a1SGerd Hoffmann     }
108f1ae32a1SGerd Hoffmann 
109f1ae32a1SGerd Hoffmann     /* handle normal (ungrouped / no IAD) interfaces if any */
110f1ae32a1SGerd Hoffmann     for (i = 0; i < conf->nif; i++) {
111f1ae32a1SGerd Hoffmann         rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
112f1ae32a1SGerd Hoffmann         if (rc < 0) {
113f1ae32a1SGerd Hoffmann             return rc;
114f1ae32a1SGerd Hoffmann         }
115f1ae32a1SGerd Hoffmann         wTotalLength += rc;
116f1ae32a1SGerd Hoffmann     }
117f1ae32a1SGerd Hoffmann 
1180a263db1SGerd Hoffmann     d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
1190a263db1SGerd Hoffmann     d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
120f1ae32a1SGerd Hoffmann     return wTotalLength;
121f1ae32a1SGerd Hoffmann }
122f1ae32a1SGerd Hoffmann 
123f1ae32a1SGerd Hoffmann int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
124f1ae32a1SGerd Hoffmann                          size_t len)
125f1ae32a1SGerd Hoffmann {
126f1ae32a1SGerd Hoffmann     int pos = 0;
127f1ae32a1SGerd Hoffmann     int i = 0;
128f1ae32a1SGerd Hoffmann 
129f1ae32a1SGerd Hoffmann     /* handle interface association descriptor */
130f1ae32a1SGerd Hoffmann     uint8_t bLength = 0x08;
131f1ae32a1SGerd Hoffmann 
132f1ae32a1SGerd Hoffmann     if (len < bLength) {
133f1ae32a1SGerd Hoffmann         return -1;
134f1ae32a1SGerd Hoffmann     }
135f1ae32a1SGerd Hoffmann 
136f1ae32a1SGerd Hoffmann     dest[0x00] = bLength;
137f1ae32a1SGerd Hoffmann     dest[0x01] = USB_DT_INTERFACE_ASSOC;
138f1ae32a1SGerd Hoffmann     dest[0x02] = iad->bFirstInterface;
139f1ae32a1SGerd Hoffmann     dest[0x03] = iad->bInterfaceCount;
140f1ae32a1SGerd Hoffmann     dest[0x04] = iad->bFunctionClass;
141f1ae32a1SGerd Hoffmann     dest[0x05] = iad->bFunctionSubClass;
142f1ae32a1SGerd Hoffmann     dest[0x06] = iad->bFunctionProtocol;
143f1ae32a1SGerd Hoffmann     dest[0x07] = iad->iFunction;
144f1ae32a1SGerd Hoffmann     pos += bLength;
145f1ae32a1SGerd Hoffmann 
146f1ae32a1SGerd Hoffmann     /* handle associated interfaces in this group */
147f1ae32a1SGerd Hoffmann     for (i = 0; i < iad->nif; i++) {
148f1ae32a1SGerd Hoffmann         int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
149f1ae32a1SGerd Hoffmann         if (rc < 0) {
150f1ae32a1SGerd Hoffmann             return rc;
151f1ae32a1SGerd Hoffmann         }
152f1ae32a1SGerd Hoffmann         pos += rc;
153f1ae32a1SGerd Hoffmann     }
154f1ae32a1SGerd Hoffmann 
155f1ae32a1SGerd Hoffmann     return pos;
156f1ae32a1SGerd Hoffmann }
157f1ae32a1SGerd Hoffmann 
158f1ae32a1SGerd Hoffmann int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
159f1ae32a1SGerd Hoffmann {
160f1ae32a1SGerd Hoffmann     uint8_t bLength = 0x09;
161f1ae32a1SGerd Hoffmann     int i, rc, pos = 0;
162feafd797SGerd Hoffmann     USBDescriptor *d = (void *)dest;
163f1ae32a1SGerd Hoffmann 
164f1ae32a1SGerd Hoffmann     if (len < bLength) {
165f1ae32a1SGerd Hoffmann         return -1;
166f1ae32a1SGerd Hoffmann     }
167f1ae32a1SGerd Hoffmann 
168feafd797SGerd Hoffmann     d->bLength                        = bLength;
169feafd797SGerd Hoffmann     d->bDescriptorType                = USB_DT_INTERFACE;
170feafd797SGerd Hoffmann 
171feafd797SGerd Hoffmann     d->u.interface.bInterfaceNumber   = iface->bInterfaceNumber;
172feafd797SGerd Hoffmann     d->u.interface.bAlternateSetting  = iface->bAlternateSetting;
173feafd797SGerd Hoffmann     d->u.interface.bNumEndpoints      = iface->bNumEndpoints;
174feafd797SGerd Hoffmann     d->u.interface.bInterfaceClass    = iface->bInterfaceClass;
175feafd797SGerd Hoffmann     d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass;
176feafd797SGerd Hoffmann     d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol;
177feafd797SGerd Hoffmann     d->u.interface.iInterface         = iface->iInterface;
178f1ae32a1SGerd Hoffmann     pos += bLength;
179f1ae32a1SGerd Hoffmann 
180f1ae32a1SGerd Hoffmann     for (i = 0; i < iface->ndesc; i++) {
181f1ae32a1SGerd Hoffmann         rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
182f1ae32a1SGerd Hoffmann         if (rc < 0) {
183f1ae32a1SGerd Hoffmann             return rc;
184f1ae32a1SGerd Hoffmann         }
185f1ae32a1SGerd Hoffmann         pos += rc;
186f1ae32a1SGerd Hoffmann     }
187f1ae32a1SGerd Hoffmann 
188f1ae32a1SGerd Hoffmann     for (i = 0; i < iface->bNumEndpoints; i++) {
189f1ae32a1SGerd Hoffmann         rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
190f1ae32a1SGerd Hoffmann         if (rc < 0) {
191f1ae32a1SGerd Hoffmann             return rc;
192f1ae32a1SGerd Hoffmann         }
193f1ae32a1SGerd Hoffmann         pos += rc;
194f1ae32a1SGerd Hoffmann     }
195f1ae32a1SGerd Hoffmann 
196f1ae32a1SGerd Hoffmann     return pos;
197f1ae32a1SGerd Hoffmann }
198f1ae32a1SGerd Hoffmann 
199f1ae32a1SGerd Hoffmann int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
200f1ae32a1SGerd Hoffmann {
201f1ae32a1SGerd Hoffmann     uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
202f1ae32a1SGerd Hoffmann     uint8_t extralen = ep->extra ? ep->extra[0] : 0;
203*e36a20d3SGerd Hoffmann     USBDescriptor *d = (void *)dest;
204f1ae32a1SGerd Hoffmann 
205f1ae32a1SGerd Hoffmann     if (len < bLength + extralen) {
206f1ae32a1SGerd Hoffmann         return -1;
207f1ae32a1SGerd Hoffmann     }
208f1ae32a1SGerd Hoffmann 
209*e36a20d3SGerd Hoffmann     d->bLength                      = bLength;
210*e36a20d3SGerd Hoffmann     d->bDescriptorType              = USB_DT_ENDPOINT;
211*e36a20d3SGerd Hoffmann 
212*e36a20d3SGerd Hoffmann     d->u.endpoint.bEndpointAddress  = ep->bEndpointAddress;
213*e36a20d3SGerd Hoffmann     d->u.endpoint.bmAttributes      = ep->bmAttributes;
214*e36a20d3SGerd Hoffmann     d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize);
215*e36a20d3SGerd Hoffmann     d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize);
216*e36a20d3SGerd Hoffmann     d->u.endpoint.bInterval         = ep->bInterval;
217f1ae32a1SGerd Hoffmann     if (ep->is_audio) {
218*e36a20d3SGerd Hoffmann         d->u.endpoint.bRefresh      = ep->bRefresh;
219*e36a20d3SGerd Hoffmann         d->u.endpoint.bSynchAddress = ep->bSynchAddress;
220f1ae32a1SGerd Hoffmann     }
221f1ae32a1SGerd Hoffmann     if (ep->extra) {
222f1ae32a1SGerd Hoffmann         memcpy(dest + bLength, ep->extra, extralen);
223f1ae32a1SGerd Hoffmann     }
224f1ae32a1SGerd Hoffmann 
225f1ae32a1SGerd Hoffmann     return bLength + extralen;
226f1ae32a1SGerd Hoffmann }
227f1ae32a1SGerd Hoffmann 
228f1ae32a1SGerd Hoffmann int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
229f1ae32a1SGerd Hoffmann {
230f1ae32a1SGerd Hoffmann     int bLength = desc->length ? desc->length : desc->data[0];
231f1ae32a1SGerd Hoffmann 
232f1ae32a1SGerd Hoffmann     if (len < bLength) {
233f1ae32a1SGerd Hoffmann         return -1;
234f1ae32a1SGerd Hoffmann     }
235f1ae32a1SGerd Hoffmann 
236f1ae32a1SGerd Hoffmann     memcpy(dest, desc->data, bLength);
237f1ae32a1SGerd Hoffmann     return bLength;
238f1ae32a1SGerd Hoffmann }
239f1ae32a1SGerd Hoffmann 
240f1ae32a1SGerd Hoffmann /* ------------------------------------------------------------------ */
241f1ae32a1SGerd Hoffmann 
242f1ae32a1SGerd Hoffmann static void usb_desc_ep_init(USBDevice *dev)
243f1ae32a1SGerd Hoffmann {
244f1ae32a1SGerd Hoffmann     const USBDescIface *iface;
245f1ae32a1SGerd Hoffmann     int i, e, pid, ep;
246f1ae32a1SGerd Hoffmann 
247f1ae32a1SGerd Hoffmann     usb_ep_init(dev);
248f1ae32a1SGerd Hoffmann     for (i = 0; i < dev->ninterfaces; i++) {
249f1ae32a1SGerd Hoffmann         iface = dev->ifaces[i];
250f1ae32a1SGerd Hoffmann         if (iface == NULL) {
251f1ae32a1SGerd Hoffmann             continue;
252f1ae32a1SGerd Hoffmann         }
253f1ae32a1SGerd Hoffmann         for (e = 0; e < iface->bNumEndpoints; e++) {
254f1ae32a1SGerd Hoffmann             pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
255f1ae32a1SGerd Hoffmann                 USB_TOKEN_IN : USB_TOKEN_OUT;
256f1ae32a1SGerd Hoffmann             ep = iface->eps[e].bEndpointAddress & 0x0f;
257f1ae32a1SGerd Hoffmann             usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
258f1ae32a1SGerd Hoffmann             usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
259f1ae32a1SGerd Hoffmann             usb_ep_set_max_packet_size(dev, pid, ep,
260f1ae32a1SGerd Hoffmann                                        iface->eps[e].wMaxPacketSize);
261f1ae32a1SGerd Hoffmann         }
262f1ae32a1SGerd Hoffmann     }
263f1ae32a1SGerd Hoffmann }
264f1ae32a1SGerd Hoffmann 
265f1ae32a1SGerd Hoffmann static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
266f1ae32a1SGerd Hoffmann                                                    int nif, int alt)
267f1ae32a1SGerd Hoffmann {
268f1ae32a1SGerd Hoffmann     const USBDescIface *iface;
269f1ae32a1SGerd Hoffmann     int g, i;
270f1ae32a1SGerd Hoffmann 
271f1ae32a1SGerd Hoffmann     if (!dev->config) {
272f1ae32a1SGerd Hoffmann         return NULL;
273f1ae32a1SGerd Hoffmann     }
274f1ae32a1SGerd Hoffmann     for (g = 0; g < dev->config->nif_groups; g++) {
275f1ae32a1SGerd Hoffmann         for (i = 0; i < dev->config->if_groups[g].nif; i++) {
276f1ae32a1SGerd Hoffmann             iface = &dev->config->if_groups[g].ifs[i];
277f1ae32a1SGerd Hoffmann             if (iface->bInterfaceNumber == nif &&
278f1ae32a1SGerd Hoffmann                 iface->bAlternateSetting == alt) {
279f1ae32a1SGerd Hoffmann                 return iface;
280f1ae32a1SGerd Hoffmann             }
281f1ae32a1SGerd Hoffmann         }
282f1ae32a1SGerd Hoffmann     }
283f1ae32a1SGerd Hoffmann     for (i = 0; i < dev->config->nif; i++) {
284f1ae32a1SGerd Hoffmann         iface = &dev->config->ifs[i];
285f1ae32a1SGerd Hoffmann         if (iface->bInterfaceNumber == nif &&
286f1ae32a1SGerd Hoffmann             iface->bAlternateSetting == alt) {
287f1ae32a1SGerd Hoffmann             return iface;
288f1ae32a1SGerd Hoffmann         }
289f1ae32a1SGerd Hoffmann     }
290f1ae32a1SGerd Hoffmann     return NULL;
291f1ae32a1SGerd Hoffmann }
292f1ae32a1SGerd Hoffmann 
293f1ae32a1SGerd Hoffmann static int usb_desc_set_interface(USBDevice *dev, int index, int value)
294f1ae32a1SGerd Hoffmann {
295f1ae32a1SGerd Hoffmann     const USBDescIface *iface;
296f1ae32a1SGerd Hoffmann     int old;
297f1ae32a1SGerd Hoffmann 
298f1ae32a1SGerd Hoffmann     iface = usb_desc_find_interface(dev, index, value);
299f1ae32a1SGerd Hoffmann     if (iface == NULL) {
300f1ae32a1SGerd Hoffmann         return -1;
301f1ae32a1SGerd Hoffmann     }
302f1ae32a1SGerd Hoffmann 
303f1ae32a1SGerd Hoffmann     old = dev->altsetting[index];
304f1ae32a1SGerd Hoffmann     dev->altsetting[index] = value;
305f1ae32a1SGerd Hoffmann     dev->ifaces[index] = iface;
306f1ae32a1SGerd Hoffmann     usb_desc_ep_init(dev);
307f1ae32a1SGerd Hoffmann 
308f1ae32a1SGerd Hoffmann     if (old != value) {
309f1ae32a1SGerd Hoffmann         usb_device_set_interface(dev, index, old, value);
310f1ae32a1SGerd Hoffmann     }
311f1ae32a1SGerd Hoffmann     return 0;
312f1ae32a1SGerd Hoffmann }
313f1ae32a1SGerd Hoffmann 
314f1ae32a1SGerd Hoffmann static int usb_desc_set_config(USBDevice *dev, int value)
315f1ae32a1SGerd Hoffmann {
316f1ae32a1SGerd Hoffmann     int i;
317f1ae32a1SGerd Hoffmann 
318f1ae32a1SGerd Hoffmann     if (value == 0) {
319f1ae32a1SGerd Hoffmann         dev->configuration = 0;
320f1ae32a1SGerd Hoffmann         dev->ninterfaces   = 0;
321f1ae32a1SGerd Hoffmann         dev->config = NULL;
322f1ae32a1SGerd Hoffmann     } else {
323f1ae32a1SGerd Hoffmann         for (i = 0; i < dev->device->bNumConfigurations; i++) {
324f1ae32a1SGerd Hoffmann             if (dev->device->confs[i].bConfigurationValue == value) {
325f1ae32a1SGerd Hoffmann                 dev->configuration = value;
326f1ae32a1SGerd Hoffmann                 dev->ninterfaces   = dev->device->confs[i].bNumInterfaces;
327f1ae32a1SGerd Hoffmann                 dev->config = dev->device->confs + i;
328f1ae32a1SGerd Hoffmann                 assert(dev->ninterfaces <= USB_MAX_INTERFACES);
329f1ae32a1SGerd Hoffmann             }
330f1ae32a1SGerd Hoffmann         }
331f1ae32a1SGerd Hoffmann         if (i < dev->device->bNumConfigurations) {
332f1ae32a1SGerd Hoffmann             return -1;
333f1ae32a1SGerd Hoffmann         }
334f1ae32a1SGerd Hoffmann     }
335f1ae32a1SGerd Hoffmann 
336f1ae32a1SGerd Hoffmann     for (i = 0; i < dev->ninterfaces; i++) {
337f1ae32a1SGerd Hoffmann         usb_desc_set_interface(dev, i, 0);
338f1ae32a1SGerd Hoffmann     }
339f1ae32a1SGerd Hoffmann     for (; i < USB_MAX_INTERFACES; i++) {
340f1ae32a1SGerd Hoffmann         dev->altsetting[i] = 0;
341f1ae32a1SGerd Hoffmann         dev->ifaces[i] = NULL;
342f1ae32a1SGerd Hoffmann     }
343f1ae32a1SGerd Hoffmann 
344f1ae32a1SGerd Hoffmann     return 0;
345f1ae32a1SGerd Hoffmann }
346f1ae32a1SGerd Hoffmann 
347f1ae32a1SGerd Hoffmann static void usb_desc_setdefaults(USBDevice *dev)
348f1ae32a1SGerd Hoffmann {
349f1ae32a1SGerd Hoffmann     const USBDesc *desc = usb_device_get_usb_desc(dev);
350f1ae32a1SGerd Hoffmann 
351f1ae32a1SGerd Hoffmann     assert(desc != NULL);
352f1ae32a1SGerd Hoffmann     switch (dev->speed) {
353f1ae32a1SGerd Hoffmann     case USB_SPEED_LOW:
354f1ae32a1SGerd Hoffmann     case USB_SPEED_FULL:
355f1ae32a1SGerd Hoffmann         dev->device = desc->full;
356f1ae32a1SGerd Hoffmann         break;
357f1ae32a1SGerd Hoffmann     case USB_SPEED_HIGH:
358f1ae32a1SGerd Hoffmann         dev->device = desc->high;
359f1ae32a1SGerd Hoffmann         break;
360f1ae32a1SGerd Hoffmann     }
361f1ae32a1SGerd Hoffmann     usb_desc_set_config(dev, 0);
362f1ae32a1SGerd Hoffmann }
363f1ae32a1SGerd Hoffmann 
364f1ae32a1SGerd Hoffmann void usb_desc_init(USBDevice *dev)
365f1ae32a1SGerd Hoffmann {
366f1ae32a1SGerd Hoffmann     const USBDesc *desc = usb_device_get_usb_desc(dev);
367f1ae32a1SGerd Hoffmann 
368f1ae32a1SGerd Hoffmann     assert(desc != NULL);
369f1ae32a1SGerd Hoffmann     dev->speed = USB_SPEED_FULL;
370f1ae32a1SGerd Hoffmann     dev->speedmask = 0;
371f1ae32a1SGerd Hoffmann     if (desc->full) {
372f1ae32a1SGerd Hoffmann         dev->speedmask |= USB_SPEED_MASK_FULL;
373f1ae32a1SGerd Hoffmann     }
374f1ae32a1SGerd Hoffmann     if (desc->high) {
375f1ae32a1SGerd Hoffmann         dev->speedmask |= USB_SPEED_MASK_HIGH;
376f1ae32a1SGerd Hoffmann     }
377f1ae32a1SGerd Hoffmann     usb_desc_setdefaults(dev);
378f1ae32a1SGerd Hoffmann }
379f1ae32a1SGerd Hoffmann 
380f1ae32a1SGerd Hoffmann void usb_desc_attach(USBDevice *dev)
381f1ae32a1SGerd Hoffmann {
382f1ae32a1SGerd Hoffmann     const USBDesc *desc = usb_device_get_usb_desc(dev);
383f1ae32a1SGerd Hoffmann 
384f1ae32a1SGerd Hoffmann     assert(desc != NULL);
385f1ae32a1SGerd Hoffmann     if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
386f1ae32a1SGerd Hoffmann         dev->speed = USB_SPEED_HIGH;
387f1ae32a1SGerd Hoffmann     } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
388f1ae32a1SGerd Hoffmann         dev->speed = USB_SPEED_FULL;
389f1ae32a1SGerd Hoffmann     } else {
390f1ae32a1SGerd Hoffmann         fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
391f1ae32a1SGerd Hoffmann                 usb_device_get_product_desc(dev));
392f1ae32a1SGerd Hoffmann         return;
393f1ae32a1SGerd Hoffmann     }
394f1ae32a1SGerd Hoffmann     usb_desc_setdefaults(dev);
395f1ae32a1SGerd Hoffmann }
396f1ae32a1SGerd Hoffmann 
397f1ae32a1SGerd Hoffmann void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
398f1ae32a1SGerd Hoffmann {
399f1ae32a1SGerd Hoffmann     USBDescString *s;
400f1ae32a1SGerd Hoffmann 
401f1ae32a1SGerd Hoffmann     QLIST_FOREACH(s, &dev->strings, next) {
402f1ae32a1SGerd Hoffmann         if (s->index == index) {
403f1ae32a1SGerd Hoffmann             break;
404f1ae32a1SGerd Hoffmann         }
405f1ae32a1SGerd Hoffmann     }
406f1ae32a1SGerd Hoffmann     if (s == NULL) {
407f1ae32a1SGerd Hoffmann         s = g_malloc0(sizeof(*s));
408f1ae32a1SGerd Hoffmann         s->index = index;
409f1ae32a1SGerd Hoffmann         QLIST_INSERT_HEAD(&dev->strings, s, next);
410f1ae32a1SGerd Hoffmann     }
411f1ae32a1SGerd Hoffmann     g_free(s->str);
412f1ae32a1SGerd Hoffmann     s->str = g_strdup(str);
413f1ae32a1SGerd Hoffmann }
414f1ae32a1SGerd Hoffmann 
415f1ae32a1SGerd Hoffmann const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
416f1ae32a1SGerd Hoffmann {
417f1ae32a1SGerd Hoffmann     USBDescString *s;
418f1ae32a1SGerd Hoffmann 
419f1ae32a1SGerd Hoffmann     QLIST_FOREACH(s, &dev->strings, next) {
420f1ae32a1SGerd Hoffmann         if (s->index == index) {
421f1ae32a1SGerd Hoffmann             return s->str;
422f1ae32a1SGerd Hoffmann         }
423f1ae32a1SGerd Hoffmann     }
424f1ae32a1SGerd Hoffmann     return NULL;
425f1ae32a1SGerd Hoffmann }
426f1ae32a1SGerd Hoffmann 
427f1ae32a1SGerd Hoffmann int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
428f1ae32a1SGerd Hoffmann {
429f1ae32a1SGerd Hoffmann     uint8_t bLength, pos, i;
430f1ae32a1SGerd Hoffmann     const char *str;
431f1ae32a1SGerd Hoffmann 
432f1ae32a1SGerd Hoffmann     if (len < 4) {
433f1ae32a1SGerd Hoffmann         return -1;
434f1ae32a1SGerd Hoffmann     }
435f1ae32a1SGerd Hoffmann 
436f1ae32a1SGerd Hoffmann     if (index == 0) {
437f1ae32a1SGerd Hoffmann         /* language ids */
438f1ae32a1SGerd Hoffmann         dest[0] = 4;
439f1ae32a1SGerd Hoffmann         dest[1] = USB_DT_STRING;
440f1ae32a1SGerd Hoffmann         dest[2] = 0x09;
441f1ae32a1SGerd Hoffmann         dest[3] = 0x04;
442f1ae32a1SGerd Hoffmann         return 4;
443f1ae32a1SGerd Hoffmann     }
444f1ae32a1SGerd Hoffmann 
445f1ae32a1SGerd Hoffmann     str = usb_desc_get_string(dev, index);
446f1ae32a1SGerd Hoffmann     if (str == NULL) {
447f1ae32a1SGerd Hoffmann         str = usb_device_get_usb_desc(dev)->str[index];
448f1ae32a1SGerd Hoffmann         if (str == NULL) {
449f1ae32a1SGerd Hoffmann             return 0;
450f1ae32a1SGerd Hoffmann         }
451f1ae32a1SGerd Hoffmann     }
452f1ae32a1SGerd Hoffmann 
453f1ae32a1SGerd Hoffmann     bLength = strlen(str) * 2 + 2;
454f1ae32a1SGerd Hoffmann     dest[0] = bLength;
455f1ae32a1SGerd Hoffmann     dest[1] = USB_DT_STRING;
456f1ae32a1SGerd Hoffmann     i = 0; pos = 2;
457f1ae32a1SGerd Hoffmann     while (pos+1 < bLength && pos+1 < len) {
458f1ae32a1SGerd Hoffmann         dest[pos++] = str[i++];
459f1ae32a1SGerd Hoffmann         dest[pos++] = 0;
460f1ae32a1SGerd Hoffmann     }
461f1ae32a1SGerd Hoffmann     return pos;
462f1ae32a1SGerd Hoffmann }
463f1ae32a1SGerd Hoffmann 
464f1ae32a1SGerd Hoffmann int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
465f1ae32a1SGerd Hoffmann {
466f1ae32a1SGerd Hoffmann     const USBDesc *desc = usb_device_get_usb_desc(dev);
467f1ae32a1SGerd Hoffmann     const USBDescDevice *other_dev;
468f1ae32a1SGerd Hoffmann     uint8_t buf[256];
469f1ae32a1SGerd Hoffmann     uint8_t type = value >> 8;
470f1ae32a1SGerd Hoffmann     uint8_t index = value & 0xff;
471f1ae32a1SGerd Hoffmann     int ret = -1;
472f1ae32a1SGerd Hoffmann 
473f1ae32a1SGerd Hoffmann     if (dev->speed == USB_SPEED_HIGH) {
474f1ae32a1SGerd Hoffmann         other_dev = usb_device_get_usb_desc(dev)->full;
475f1ae32a1SGerd Hoffmann     } else {
476f1ae32a1SGerd Hoffmann         other_dev = usb_device_get_usb_desc(dev)->high;
477f1ae32a1SGerd Hoffmann     }
478f1ae32a1SGerd Hoffmann 
479f1ae32a1SGerd Hoffmann     switch(type) {
480f1ae32a1SGerd Hoffmann     case USB_DT_DEVICE:
481f1ae32a1SGerd Hoffmann         ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
482f1ae32a1SGerd Hoffmann         trace_usb_desc_device(dev->addr, len, ret);
483f1ae32a1SGerd Hoffmann         break;
484f1ae32a1SGerd Hoffmann     case USB_DT_CONFIG:
485f1ae32a1SGerd Hoffmann         if (index < dev->device->bNumConfigurations) {
486f1ae32a1SGerd Hoffmann             ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
487f1ae32a1SGerd Hoffmann         }
488f1ae32a1SGerd Hoffmann         trace_usb_desc_config(dev->addr, index, len, ret);
489f1ae32a1SGerd Hoffmann         break;
490f1ae32a1SGerd Hoffmann     case USB_DT_STRING:
491f1ae32a1SGerd Hoffmann         ret = usb_desc_string(dev, index, buf, sizeof(buf));
492f1ae32a1SGerd Hoffmann         trace_usb_desc_string(dev->addr, index, len, ret);
493f1ae32a1SGerd Hoffmann         break;
494f1ae32a1SGerd Hoffmann 
495f1ae32a1SGerd Hoffmann     case USB_DT_DEVICE_QUALIFIER:
496f1ae32a1SGerd Hoffmann         if (other_dev != NULL) {
497f1ae32a1SGerd Hoffmann             ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
498f1ae32a1SGerd Hoffmann         }
499f1ae32a1SGerd Hoffmann         trace_usb_desc_device_qualifier(dev->addr, len, ret);
500f1ae32a1SGerd Hoffmann         break;
501f1ae32a1SGerd Hoffmann     case USB_DT_OTHER_SPEED_CONFIG:
502f1ae32a1SGerd Hoffmann         if (other_dev != NULL && index < other_dev->bNumConfigurations) {
503f1ae32a1SGerd Hoffmann             ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
504f1ae32a1SGerd Hoffmann             buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
505f1ae32a1SGerd Hoffmann         }
506f1ae32a1SGerd Hoffmann         trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
507f1ae32a1SGerd Hoffmann         break;
508f1ae32a1SGerd Hoffmann 
509f1ae32a1SGerd Hoffmann     case USB_DT_DEBUG:
510f1ae32a1SGerd Hoffmann         /* ignore silently */
511f1ae32a1SGerd Hoffmann         break;
512f1ae32a1SGerd Hoffmann 
513f1ae32a1SGerd Hoffmann     default:
514f1ae32a1SGerd Hoffmann         fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
515f1ae32a1SGerd Hoffmann                 dev->addr, type, len);
516f1ae32a1SGerd Hoffmann         break;
517f1ae32a1SGerd Hoffmann     }
518f1ae32a1SGerd Hoffmann 
519f1ae32a1SGerd Hoffmann     if (ret > 0) {
520f1ae32a1SGerd Hoffmann         if (ret > len) {
521f1ae32a1SGerd Hoffmann             ret = len;
522f1ae32a1SGerd Hoffmann         }
523f1ae32a1SGerd Hoffmann         memcpy(dest, buf, ret);
524f1ae32a1SGerd Hoffmann     }
525f1ae32a1SGerd Hoffmann     return ret;
526f1ae32a1SGerd Hoffmann }
527f1ae32a1SGerd Hoffmann 
528f1ae32a1SGerd Hoffmann int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
529f1ae32a1SGerd Hoffmann         int request, int value, int index, int length, uint8_t *data)
530f1ae32a1SGerd Hoffmann {
531f1ae32a1SGerd Hoffmann     const USBDesc *desc = usb_device_get_usb_desc(dev);
532f1ae32a1SGerd Hoffmann     int ret = -1;
533f1ae32a1SGerd Hoffmann 
534f1ae32a1SGerd Hoffmann     assert(desc != NULL);
535f1ae32a1SGerd Hoffmann     switch(request) {
536f1ae32a1SGerd Hoffmann     case DeviceOutRequest | USB_REQ_SET_ADDRESS:
537f1ae32a1SGerd Hoffmann         dev->addr = value;
538f1ae32a1SGerd Hoffmann         trace_usb_set_addr(dev->addr);
539f1ae32a1SGerd Hoffmann         ret = 0;
540f1ae32a1SGerd Hoffmann         break;
541f1ae32a1SGerd Hoffmann 
542f1ae32a1SGerd Hoffmann     case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
543f1ae32a1SGerd Hoffmann         ret = usb_desc_get_descriptor(dev, value, data, length);
544f1ae32a1SGerd Hoffmann         break;
545f1ae32a1SGerd Hoffmann 
546f1ae32a1SGerd Hoffmann     case DeviceRequest | USB_REQ_GET_CONFIGURATION:
547f1ae32a1SGerd Hoffmann         /*
548f1ae32a1SGerd Hoffmann          * 9.4.2: 0 should be returned if the device is unconfigured, otherwise
549f1ae32a1SGerd Hoffmann          * the non zero value of bConfigurationValue.
550f1ae32a1SGerd Hoffmann          */
551f1ae32a1SGerd Hoffmann         data[0] = dev->config ? dev->config->bConfigurationValue : 0;
552f1ae32a1SGerd Hoffmann         ret = 1;
553f1ae32a1SGerd Hoffmann         break;
554f1ae32a1SGerd Hoffmann     case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
555f1ae32a1SGerd Hoffmann         ret = usb_desc_set_config(dev, value);
556f1ae32a1SGerd Hoffmann         trace_usb_set_config(dev->addr, value, ret);
557f1ae32a1SGerd Hoffmann         break;
558f1ae32a1SGerd Hoffmann 
559f1ae32a1SGerd Hoffmann     case DeviceRequest | USB_REQ_GET_STATUS: {
560f1ae32a1SGerd Hoffmann         const USBDescConfig *config = dev->config ?
561f1ae32a1SGerd Hoffmann             dev->config : &dev->device->confs[0];
562f1ae32a1SGerd Hoffmann 
563f1ae32a1SGerd Hoffmann         data[0] = 0;
564f1ae32a1SGerd Hoffmann         /*
565f1ae32a1SGerd Hoffmann          * Default state: Device behavior when this request is received while
566f1ae32a1SGerd Hoffmann          *                the device is in the Default state is not specified.
567f1ae32a1SGerd Hoffmann          * We return the same value that a configured device would return if
568f1ae32a1SGerd Hoffmann          * it used the first configuration.
569f1ae32a1SGerd Hoffmann          */
570f1ae32a1SGerd Hoffmann         if (config->bmAttributes & 0x40) {
571f1ae32a1SGerd Hoffmann             data[0] |= 1 << USB_DEVICE_SELF_POWERED;
572f1ae32a1SGerd Hoffmann         }
573f1ae32a1SGerd Hoffmann         if (dev->remote_wakeup) {
574f1ae32a1SGerd Hoffmann             data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
575f1ae32a1SGerd Hoffmann         }
576f1ae32a1SGerd Hoffmann         data[1] = 0x00;
577f1ae32a1SGerd Hoffmann         ret = 2;
578f1ae32a1SGerd Hoffmann         break;
579f1ae32a1SGerd Hoffmann     }
580f1ae32a1SGerd Hoffmann     case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
581f1ae32a1SGerd Hoffmann         if (value == USB_DEVICE_REMOTE_WAKEUP) {
582f1ae32a1SGerd Hoffmann             dev->remote_wakeup = 0;
583f1ae32a1SGerd Hoffmann             ret = 0;
584f1ae32a1SGerd Hoffmann         }
585f1ae32a1SGerd Hoffmann         trace_usb_clear_device_feature(dev->addr, value, ret);
586f1ae32a1SGerd Hoffmann         break;
587f1ae32a1SGerd Hoffmann     case DeviceOutRequest | USB_REQ_SET_FEATURE:
588f1ae32a1SGerd Hoffmann         if (value == USB_DEVICE_REMOTE_WAKEUP) {
589f1ae32a1SGerd Hoffmann             dev->remote_wakeup = 1;
590f1ae32a1SGerd Hoffmann             ret = 0;
591f1ae32a1SGerd Hoffmann         }
592f1ae32a1SGerd Hoffmann         trace_usb_set_device_feature(dev->addr, value, ret);
593f1ae32a1SGerd Hoffmann         break;
594f1ae32a1SGerd Hoffmann 
595f1ae32a1SGerd Hoffmann     case InterfaceRequest | USB_REQ_GET_INTERFACE:
596f1ae32a1SGerd Hoffmann         if (index < 0 || index >= dev->ninterfaces) {
597f1ae32a1SGerd Hoffmann             break;
598f1ae32a1SGerd Hoffmann         }
599f1ae32a1SGerd Hoffmann         data[0] = dev->altsetting[index];
600f1ae32a1SGerd Hoffmann         ret = 1;
601f1ae32a1SGerd Hoffmann         break;
602f1ae32a1SGerd Hoffmann     case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
603f1ae32a1SGerd Hoffmann         ret = usb_desc_set_interface(dev, index, value);
604f1ae32a1SGerd Hoffmann         trace_usb_set_interface(dev->addr, index, value, ret);
605f1ae32a1SGerd Hoffmann         break;
606f1ae32a1SGerd Hoffmann 
607f1ae32a1SGerd Hoffmann     }
608f1ae32a1SGerd Hoffmann     return ret;
609f1ae32a1SGerd Hoffmann }
610