root / hw / usb-desc.c @ dcbd0b5c
History | View | Annotate | Download (11.4 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 | 37fb59d3 | Gerd Hoffmann | uint8_t bLength = 0x07;
|
196 | 37fb59d3 | Gerd Hoffmann | |
197 | 37fb59d3 | Gerd Hoffmann | if (len < bLength) {
|
198 | 37fb59d3 | Gerd Hoffmann | return -1; |
199 | 37fb59d3 | Gerd Hoffmann | } |
200 | 37fb59d3 | Gerd Hoffmann | |
201 | 37fb59d3 | Gerd Hoffmann | dest[0x00] = bLength;
|
202 | 37fb59d3 | Gerd Hoffmann | dest[0x01] = USB_DT_ENDPOINT;
|
203 | 37fb59d3 | Gerd Hoffmann | dest[0x02] = ep->bEndpointAddress;
|
204 | 37fb59d3 | Gerd Hoffmann | dest[0x03] = ep->bmAttributes;
|
205 | 37fb59d3 | Gerd Hoffmann | dest[0x04] = usb_lo(ep->wMaxPacketSize);
|
206 | 37fb59d3 | Gerd Hoffmann | dest[0x05] = usb_hi(ep->wMaxPacketSize);
|
207 | 37fb59d3 | Gerd Hoffmann | dest[0x06] = ep->bInterval;
|
208 | 37fb59d3 | Gerd Hoffmann | |
209 | 37fb59d3 | Gerd Hoffmann | return bLength;
|
210 | 37fb59d3 | Gerd Hoffmann | } |
211 | 37fb59d3 | Gerd Hoffmann | |
212 | 37fb59d3 | Gerd Hoffmann | int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len) |
213 | 37fb59d3 | Gerd Hoffmann | { |
214 | 37fb59d3 | Gerd Hoffmann | int bLength = desc->length ? desc->length : desc->data[0]; |
215 | 37fb59d3 | Gerd Hoffmann | |
216 | 37fb59d3 | Gerd Hoffmann | if (len < bLength) {
|
217 | 37fb59d3 | Gerd Hoffmann | return -1; |
218 | 37fb59d3 | Gerd Hoffmann | } |
219 | 37fb59d3 | Gerd Hoffmann | |
220 | 37fb59d3 | Gerd Hoffmann | memcpy(dest, desc->data, bLength); |
221 | 37fb59d3 | Gerd Hoffmann | return bLength;
|
222 | 37fb59d3 | Gerd Hoffmann | } |
223 | 37fb59d3 | Gerd Hoffmann | |
224 | 132a3f55 | Gerd Hoffmann | /* ------------------------------------------------------------------ */
|
225 | 132a3f55 | Gerd Hoffmann | |
226 | 32d41919 | Gerd Hoffmann | static void usb_desc_setdefaults(USBDevice *dev) |
227 | a980a065 | Gerd Hoffmann | { |
228 | a980a065 | Gerd Hoffmann | const USBDesc *desc = dev->info->usb_desc;
|
229 | a980a065 | Gerd Hoffmann | |
230 | a980a065 | Gerd Hoffmann | assert(desc != NULL);
|
231 | 32d41919 | Gerd Hoffmann | switch (dev->speed) {
|
232 | 32d41919 | Gerd Hoffmann | case USB_SPEED_LOW:
|
233 | 32d41919 | Gerd Hoffmann | case USB_SPEED_FULL:
|
234 | 32d41919 | Gerd Hoffmann | dev->device = desc->full; |
235 | 32d41919 | Gerd Hoffmann | break;
|
236 | 32d41919 | Gerd Hoffmann | case USB_SPEED_HIGH:
|
237 | 32d41919 | Gerd Hoffmann | dev->device = desc->high; |
238 | 32d41919 | Gerd Hoffmann | break;
|
239 | 32d41919 | Gerd Hoffmann | } |
240 | a980a065 | Gerd Hoffmann | dev->config = dev->device->confs; |
241 | a980a065 | Gerd Hoffmann | } |
242 | a980a065 | Gerd Hoffmann | |
243 | 32d41919 | Gerd Hoffmann | void usb_desc_init(USBDevice *dev)
|
244 | 32d41919 | Gerd Hoffmann | { |
245 | 32d41919 | Gerd Hoffmann | dev->speed = USB_SPEED_FULL; |
246 | 32d41919 | Gerd Hoffmann | usb_desc_setdefaults(dev); |
247 | 32d41919 | Gerd Hoffmann | } |
248 | 32d41919 | Gerd Hoffmann | |
249 | 32d41919 | Gerd Hoffmann | void usb_desc_attach(USBDevice *dev)
|
250 | 32d41919 | Gerd Hoffmann | { |
251 | 32d41919 | Gerd Hoffmann | const USBDesc *desc = dev->info->usb_desc;
|
252 | 32d41919 | Gerd Hoffmann | |
253 | 32d41919 | Gerd Hoffmann | assert(desc != NULL);
|
254 | 32d41919 | Gerd Hoffmann | if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
|
255 | 32d41919 | Gerd Hoffmann | dev->speed = USB_SPEED_HIGH; |
256 | 32d41919 | Gerd Hoffmann | } else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) { |
257 | 32d41919 | Gerd Hoffmann | dev->speed = USB_SPEED_FULL; |
258 | 32d41919 | Gerd Hoffmann | } else {
|
259 | 32d41919 | Gerd Hoffmann | fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
|
260 | 32d41919 | Gerd Hoffmann | dev->info->product_desc); |
261 | 32d41919 | Gerd Hoffmann | return;
|
262 | 32d41919 | Gerd Hoffmann | } |
263 | 32d41919 | Gerd Hoffmann | usb_desc_setdefaults(dev); |
264 | 32d41919 | Gerd Hoffmann | } |
265 | 32d41919 | Gerd Hoffmann | |
266 | 132a3f55 | Gerd Hoffmann | void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str) |
267 | 132a3f55 | Gerd Hoffmann | { |
268 | 132a3f55 | Gerd Hoffmann | USBDescString *s; |
269 | 132a3f55 | Gerd Hoffmann | |
270 | 132a3f55 | Gerd Hoffmann | QLIST_FOREACH(s, &dev->strings, next) { |
271 | 132a3f55 | Gerd Hoffmann | if (s->index == index) {
|
272 | 132a3f55 | Gerd Hoffmann | break;
|
273 | 132a3f55 | Gerd Hoffmann | } |
274 | 132a3f55 | Gerd Hoffmann | } |
275 | 132a3f55 | Gerd Hoffmann | if (s == NULL) { |
276 | 132a3f55 | Gerd Hoffmann | s = qemu_mallocz(sizeof(*s));
|
277 | 132a3f55 | Gerd Hoffmann | s->index = index; |
278 | 132a3f55 | Gerd Hoffmann | QLIST_INSERT_HEAD(&dev->strings, s, next); |
279 | 132a3f55 | Gerd Hoffmann | } |
280 | 132a3f55 | Gerd Hoffmann | qemu_free(s->str); |
281 | 132a3f55 | Gerd Hoffmann | s->str = qemu_strdup(str); |
282 | 132a3f55 | Gerd Hoffmann | } |
283 | 132a3f55 | Gerd Hoffmann | |
284 | 132a3f55 | Gerd Hoffmann | const char *usb_desc_get_string(USBDevice *dev, uint8_t index) |
285 | 132a3f55 | Gerd Hoffmann | { |
286 | 132a3f55 | Gerd Hoffmann | USBDescString *s; |
287 | 132a3f55 | Gerd Hoffmann | |
288 | 132a3f55 | Gerd Hoffmann | QLIST_FOREACH(s, &dev->strings, next) { |
289 | 132a3f55 | Gerd Hoffmann | if (s->index == index) {
|
290 | 132a3f55 | Gerd Hoffmann | return s->str;
|
291 | 132a3f55 | Gerd Hoffmann | } |
292 | 132a3f55 | Gerd Hoffmann | } |
293 | 132a3f55 | Gerd Hoffmann | return NULL; |
294 | 132a3f55 | Gerd Hoffmann | } |
295 | 132a3f55 | Gerd Hoffmann | |
296 | 132a3f55 | Gerd Hoffmann | int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len) |
297 | 37fb59d3 | Gerd Hoffmann | { |
298 | 37fb59d3 | Gerd Hoffmann | uint8_t bLength, pos, i; |
299 | 132a3f55 | Gerd Hoffmann | const char *str; |
300 | 37fb59d3 | Gerd Hoffmann | |
301 | 37fb59d3 | Gerd Hoffmann | if (len < 4) { |
302 | 37fb59d3 | Gerd Hoffmann | return -1; |
303 | 37fb59d3 | Gerd Hoffmann | } |
304 | 37fb59d3 | Gerd Hoffmann | |
305 | 37fb59d3 | Gerd Hoffmann | if (index == 0) { |
306 | 37fb59d3 | Gerd Hoffmann | /* language ids */
|
307 | 37fb59d3 | Gerd Hoffmann | dest[0] = 4; |
308 | 37fb59d3 | Gerd Hoffmann | dest[1] = USB_DT_STRING;
|
309 | 37fb59d3 | Gerd Hoffmann | dest[2] = 0x09; |
310 | 37fb59d3 | Gerd Hoffmann | dest[3] = 0x04; |
311 | 37fb59d3 | Gerd Hoffmann | return 4; |
312 | 37fb59d3 | Gerd Hoffmann | } |
313 | 37fb59d3 | Gerd Hoffmann | |
314 | 132a3f55 | Gerd Hoffmann | str = usb_desc_get_string(dev, index); |
315 | 132a3f55 | Gerd Hoffmann | if (str == NULL) { |
316 | 132a3f55 | Gerd Hoffmann | str = dev->info->usb_desc->str[index]; |
317 | 132a3f55 | Gerd Hoffmann | if (str == NULL) { |
318 | 132a3f55 | Gerd Hoffmann | return 0; |
319 | 132a3f55 | Gerd Hoffmann | } |
320 | 37fb59d3 | Gerd Hoffmann | } |
321 | 132a3f55 | Gerd Hoffmann | |
322 | 132a3f55 | Gerd Hoffmann | bLength = strlen(str) * 2 + 2; |
323 | 37fb59d3 | Gerd Hoffmann | dest[0] = bLength;
|
324 | 37fb59d3 | Gerd Hoffmann | dest[1] = USB_DT_STRING;
|
325 | 37fb59d3 | Gerd Hoffmann | i = 0; pos = 2; |
326 | 37fb59d3 | Gerd Hoffmann | while (pos+1 < bLength && pos+1 < len) { |
327 | 132a3f55 | Gerd Hoffmann | dest[pos++] = str[i++]; |
328 | 37fb59d3 | Gerd Hoffmann | dest[pos++] = 0;
|
329 | 37fb59d3 | Gerd Hoffmann | } |
330 | 37fb59d3 | Gerd Hoffmann | return pos;
|
331 | 37fb59d3 | Gerd Hoffmann | } |
332 | 37fb59d3 | Gerd Hoffmann | |
333 | 37fb59d3 | Gerd Hoffmann | int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len) |
334 | 37fb59d3 | Gerd Hoffmann | { |
335 | 37fb59d3 | Gerd Hoffmann | const USBDesc *desc = dev->info->usb_desc;
|
336 | 25620cba | Gerd Hoffmann | const USBDescDevice *other_dev;
|
337 | 37fb59d3 | Gerd Hoffmann | uint8_t buf[256];
|
338 | 37fb59d3 | Gerd Hoffmann | uint8_t type = value >> 8;
|
339 | 37fb59d3 | Gerd Hoffmann | uint8_t index = value & 0xff;
|
340 | 37fb59d3 | Gerd Hoffmann | int ret = -1; |
341 | 37fb59d3 | Gerd Hoffmann | |
342 | 25620cba | Gerd Hoffmann | if (dev->speed == USB_SPEED_HIGH) {
|
343 | 25620cba | Gerd Hoffmann | other_dev = dev->info->usb_desc->full; |
344 | 25620cba | Gerd Hoffmann | } else {
|
345 | 25620cba | Gerd Hoffmann | other_dev = dev->info->usb_desc->high; |
346 | 25620cba | Gerd Hoffmann | } |
347 | 25620cba | Gerd Hoffmann | |
348 | 37fb59d3 | Gerd Hoffmann | switch(type) {
|
349 | 37fb59d3 | Gerd Hoffmann | case USB_DT_DEVICE:
|
350 | a980a065 | Gerd Hoffmann | ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
|
351 | 37fb59d3 | Gerd Hoffmann | trace_usb_desc_device(dev->addr, len, ret); |
352 | 37fb59d3 | Gerd Hoffmann | break;
|
353 | 37fb59d3 | Gerd Hoffmann | case USB_DT_CONFIG:
|
354 | a980a065 | Gerd Hoffmann | if (index < dev->device->bNumConfigurations) {
|
355 | a980a065 | Gerd Hoffmann | ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
|
356 | 37fb59d3 | Gerd Hoffmann | } |
357 | 37fb59d3 | Gerd Hoffmann | trace_usb_desc_config(dev->addr, index, len, ret); |
358 | 37fb59d3 | Gerd Hoffmann | break;
|
359 | 37fb59d3 | Gerd Hoffmann | case USB_DT_STRING:
|
360 | 132a3f55 | Gerd Hoffmann | ret = usb_desc_string(dev, index, buf, sizeof(buf));
|
361 | 37fb59d3 | Gerd Hoffmann | trace_usb_desc_string(dev->addr, index, len, ret); |
362 | 37fb59d3 | Gerd Hoffmann | break;
|
363 | 25620cba | Gerd Hoffmann | |
364 | 25620cba | Gerd Hoffmann | case USB_DT_DEVICE_QUALIFIER:
|
365 | 25620cba | Gerd Hoffmann | if (other_dev != NULL) { |
366 | 25620cba | Gerd Hoffmann | ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
|
367 | 25620cba | Gerd Hoffmann | } |
368 | 25620cba | Gerd Hoffmann | trace_usb_desc_device_qualifier(dev->addr, len, ret); |
369 | 25620cba | Gerd Hoffmann | break;
|
370 | 25620cba | Gerd Hoffmann | case USB_DT_OTHER_SPEED_CONFIG:
|
371 | 25620cba | Gerd Hoffmann | if (other_dev != NULL && index < other_dev->bNumConfigurations) { |
372 | 25620cba | Gerd Hoffmann | ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
|
373 | 25620cba | Gerd Hoffmann | buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
|
374 | 25620cba | Gerd Hoffmann | } |
375 | 25620cba | Gerd Hoffmann | trace_usb_desc_other_speed_config(dev->addr, index, len, ret); |
376 | 25620cba | Gerd Hoffmann | break;
|
377 | 25620cba | Gerd Hoffmann | |
378 | 37fb59d3 | Gerd Hoffmann | default:
|
379 | 37fb59d3 | Gerd Hoffmann | fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
|
380 | 37fb59d3 | Gerd Hoffmann | dev->addr, type, len); |
381 | 37fb59d3 | Gerd Hoffmann | break;
|
382 | 37fb59d3 | Gerd Hoffmann | } |
383 | 37fb59d3 | Gerd Hoffmann | |
384 | 37fb59d3 | Gerd Hoffmann | if (ret > 0) { |
385 | 37fb59d3 | Gerd Hoffmann | if (ret > len) {
|
386 | 37fb59d3 | Gerd Hoffmann | ret = len; |
387 | 37fb59d3 | Gerd Hoffmann | } |
388 | 37fb59d3 | Gerd Hoffmann | memcpy(dest, buf, ret); |
389 | 37fb59d3 | Gerd Hoffmann | } |
390 | 37fb59d3 | Gerd Hoffmann | return ret;
|
391 | 37fb59d3 | Gerd Hoffmann | } |
392 | 37fb59d3 | Gerd Hoffmann | |
393 | 007fd62f | Hans de Goede | int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
394 | 007fd62f | Hans de Goede | int request, int value, int index, int length, uint8_t *data) |
395 | 37fb59d3 | Gerd Hoffmann | { |
396 | 37fb59d3 | Gerd Hoffmann | const USBDesc *desc = dev->info->usb_desc;
|
397 | a980a065 | Gerd Hoffmann | int i, ret = -1; |
398 | 37fb59d3 | Gerd Hoffmann | |
399 | 37fb59d3 | Gerd Hoffmann | assert(desc != NULL);
|
400 | 37fb59d3 | Gerd Hoffmann | switch(request) {
|
401 | 41c6abbd | Gerd Hoffmann | case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
402 | 41c6abbd | Gerd Hoffmann | dev->addr = value; |
403 | 41c6abbd | Gerd Hoffmann | trace_usb_set_addr(dev->addr); |
404 | 41c6abbd | Gerd Hoffmann | ret = 0;
|
405 | 41c6abbd | Gerd Hoffmann | break;
|
406 | 41c6abbd | Gerd Hoffmann | |
407 | 37fb59d3 | Gerd Hoffmann | case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
408 | 37fb59d3 | Gerd Hoffmann | ret = usb_desc_get_descriptor(dev, value, data, length); |
409 | 37fb59d3 | Gerd Hoffmann | break;
|
410 | a980a065 | Gerd Hoffmann | |
411 | a980a065 | Gerd Hoffmann | case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
412 | a980a065 | Gerd Hoffmann | data[0] = dev->config->bConfigurationValue;
|
413 | a980a065 | Gerd Hoffmann | ret = 1;
|
414 | a980a065 | Gerd Hoffmann | break;
|
415 | a980a065 | Gerd Hoffmann | case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
416 | a980a065 | Gerd Hoffmann | for (i = 0; i < dev->device->bNumConfigurations; i++) { |
417 | a980a065 | Gerd Hoffmann | if (dev->device->confs[i].bConfigurationValue == value) {
|
418 | a980a065 | Gerd Hoffmann | dev->config = dev->device->confs + i; |
419 | a980a065 | Gerd Hoffmann | ret = 0;
|
420 | a980a065 | Gerd Hoffmann | } |
421 | a980a065 | Gerd Hoffmann | } |
422 | a980a065 | Gerd Hoffmann | trace_usb_set_config(dev->addr, value, ret); |
423 | a980a065 | Gerd Hoffmann | break;
|
424 | ed5a83dd | Gerd Hoffmann | |
425 | ed5a83dd | Gerd Hoffmann | case DeviceRequest | USB_REQ_GET_STATUS:
|
426 | ed5a83dd | Gerd Hoffmann | data[0] = 0; |
427 | ed5a83dd | Gerd Hoffmann | if (dev->config->bmAttributes & 0x40) { |
428 | ed5a83dd | Gerd Hoffmann | data[0] |= 1 << USB_DEVICE_SELF_POWERED; |
429 | ed5a83dd | Gerd Hoffmann | } |
430 | ed5a83dd | Gerd Hoffmann | if (dev->remote_wakeup) {
|
431 | ed5a83dd | Gerd Hoffmann | data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP; |
432 | ed5a83dd | Gerd Hoffmann | } |
433 | ed5a83dd | Gerd Hoffmann | data[1] = 0x00; |
434 | ed5a83dd | Gerd Hoffmann | ret = 2;
|
435 | ed5a83dd | Gerd Hoffmann | break;
|
436 | ed5a83dd | Gerd Hoffmann | case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
437 | ed5a83dd | Gerd Hoffmann | if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
438 | ed5a83dd | Gerd Hoffmann | dev->remote_wakeup = 0;
|
439 | ed5a83dd | Gerd Hoffmann | ret = 0;
|
440 | ed5a83dd | Gerd Hoffmann | } |
441 | ed5a83dd | Gerd Hoffmann | trace_usb_clear_device_feature(dev->addr, value, ret); |
442 | ed5a83dd | Gerd Hoffmann | break;
|
443 | ed5a83dd | Gerd Hoffmann | case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
444 | ed5a83dd | Gerd Hoffmann | if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
445 | ed5a83dd | Gerd Hoffmann | dev->remote_wakeup = 1;
|
446 | ed5a83dd | Gerd Hoffmann | ret = 0;
|
447 | ed5a83dd | Gerd Hoffmann | } |
448 | ed5a83dd | Gerd Hoffmann | trace_usb_set_device_feature(dev->addr, value, ret); |
449 | ed5a83dd | Gerd Hoffmann | break;
|
450 | 37fb59d3 | Gerd Hoffmann | } |
451 | 37fb59d3 | Gerd Hoffmann | return ret;
|
452 | 37fb59d3 | Gerd Hoffmann | } |