Statistics
| Branch: | Revision:

root / hw / usb-desc.c @ 0dad6c35

History | View | Annotate | Download (15.1 kB)

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