diff options
Diffstat (limited to 'tools/usb')
-rw-r--r-- | tools/usb/ffs-test.c | 112 |
1 files changed, 107 insertions, 5 deletions
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index 708d317b0f37..88d5e71be044 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <fcntl.h> | 29 | #include <fcntl.h> |
30 | #include <pthread.h> | 30 | #include <pthread.h> |
31 | #include <stdarg.h> | 31 | #include <stdarg.h> |
32 | #include <stdbool.h> | ||
32 | #include <stdio.h> | 33 | #include <stdio.h> |
33 | #include <stdlib.h> | 34 | #include <stdlib.h> |
34 | #include <string.h> | 35 | #include <string.h> |
@@ -172,6 +173,89 @@ static const struct { | |||
172 | }, | 173 | }, |
173 | }; | 174 | }; |
174 | 175 | ||
176 | static size_t descs_to_legacy(void **legacy, const void *descriptors_v2) | ||
177 | { | ||
178 | const unsigned char *descs_end, *descs_start; | ||
179 | __u32 length, fs_count = 0, hs_count = 0, count; | ||
180 | |||
181 | /* Read v2 header */ | ||
182 | { | ||
183 | const struct { | ||
184 | const struct usb_functionfs_descs_head_v2 header; | ||
185 | const __le32 counts[]; | ||
186 | } __attribute__((packed)) *const in = descriptors_v2; | ||
187 | const __le32 *counts = in->counts; | ||
188 | __u32 flags; | ||
189 | |||
190 | if (le32_to_cpu(in->header.magic) != | ||
191 | FUNCTIONFS_DESCRIPTORS_MAGIC_V2) | ||
192 | return 0; | ||
193 | length = le32_to_cpu(in->header.length); | ||
194 | if (length <= sizeof in->header) | ||
195 | return 0; | ||
196 | length -= sizeof in->header; | ||
197 | flags = le32_to_cpu(in->header.flags); | ||
198 | if (flags & ~(FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC | | ||
199 | FUNCTIONFS_HAS_SS_DESC)) | ||
200 | return 0; | ||
201 | |||
202 | #define GET_NEXT_COUNT_IF_FLAG(ret, flg) do { \ | ||
203 | if (!(flags & (flg))) \ | ||
204 | break; \ | ||
205 | if (length < 4) \ | ||
206 | return 0; \ | ||
207 | ret = le32_to_cpu(*counts); \ | ||
208 | length -= 4; \ | ||
209 | ++counts; \ | ||
210 | } while (0) | ||
211 | |||
212 | GET_NEXT_COUNT_IF_FLAG(fs_count, FUNCTIONFS_HAS_FS_DESC); | ||
213 | GET_NEXT_COUNT_IF_FLAG(hs_count, FUNCTIONFS_HAS_HS_DESC); | ||
214 | GET_NEXT_COUNT_IF_FLAG(count, FUNCTIONFS_HAS_SS_DESC); | ||
215 | |||
216 | count = fs_count + hs_count; | ||
217 | if (!count) | ||
218 | return 0; | ||
219 | descs_start = (const void *)counts; | ||
220 | |||
221 | #undef GET_NEXT_COUNT_IF_FLAG | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * Find the end of FS and HS USB descriptors. SS descriptors | ||
226 | * are ignored since legacy format does not support them. | ||
227 | */ | ||
228 | descs_end = descs_start; | ||
229 | do { | ||
230 | if (length < *descs_end) | ||
231 | return 0; | ||
232 | length -= *descs_end; | ||
233 | descs_end += *descs_end; | ||
234 | } while (--count); | ||
235 | |||
236 | /* Allocate legacy descriptors and copy the data. */ | ||
237 | { | ||
238 | #pragma GCC diagnostic push | ||
239 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" | ||
240 | struct { | ||
241 | struct usb_functionfs_descs_head header; | ||
242 | __u8 descriptors[]; | ||
243 | } __attribute__((packed)) *out; | ||
244 | #pragma GCC diagnostic pop | ||
245 | |||
246 | length = sizeof out->header + (descs_end - descs_start); | ||
247 | out = malloc(length); | ||
248 | out->header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC); | ||
249 | out->header.length = cpu_to_le32(length); | ||
250 | out->header.fs_count = cpu_to_le32(fs_count); | ||
251 | out->header.hs_count = cpu_to_le32(hs_count); | ||
252 | memcpy(out->descriptors, descs_start, descs_end - descs_start); | ||
253 | *legacy = out; | ||
254 | } | ||
255 | |||
256 | return length; | ||
257 | } | ||
258 | |||
175 | 259 | ||
176 | #define STR_INTERFACE_ "Source/Sink" | 260 | #define STR_INTERFACE_ "Source/Sink" |
177 | 261 | ||
@@ -491,12 +575,29 @@ ep0_consume(struct thread *ignore, const void *buf, size_t nbytes) | |||
491 | return nbytes; | 575 | return nbytes; |
492 | } | 576 | } |
493 | 577 | ||
494 | static void ep0_init(struct thread *t) | 578 | static void ep0_init(struct thread *t, bool legacy_descriptors) |
495 | { | 579 | { |
580 | void *legacy; | ||
496 | ssize_t ret; | 581 | ssize_t ret; |
582 | size_t len; | ||
583 | |||
584 | if (legacy_descriptors) { | ||
585 | info("%s: writing descriptors\n", t->filename); | ||
586 | goto legacy; | ||
587 | } | ||
497 | 588 | ||
498 | info("%s: writing descriptors\n", t->filename); | 589 | info("%s: writing descriptors (in v2 format)\n", t->filename); |
499 | ret = write(t->fd, &descriptors, sizeof descriptors); | 590 | ret = write(t->fd, &descriptors, sizeof descriptors); |
591 | |||
592 | if (ret < 0 && errno == EINVAL) { | ||
593 | warn("%s: new format rejected, trying legacy\n", t->filename); | ||
594 | legacy: | ||
595 | len = descs_to_legacy(&legacy, &descriptors); | ||
596 | if (len) { | ||
597 | ret = write(t->fd, legacy, len); | ||
598 | free(legacy); | ||
599 | } | ||
600 | } | ||
500 | die_on(ret < 0, "%s: write: descriptors", t->filename); | 601 | die_on(ret < 0, "%s: write: descriptors", t->filename); |
501 | 602 | ||
502 | info("%s: writing strings\n", t->filename); | 603 | info("%s: writing strings\n", t->filename); |
@@ -507,14 +608,15 @@ static void ep0_init(struct thread *t) | |||
507 | 608 | ||
508 | /******************** Main **************************************************/ | 609 | /******************** Main **************************************************/ |
509 | 610 | ||
510 | int main(void) | 611 | int main(int argc, char **argv) |
511 | { | 612 | { |
613 | bool legacy_descriptors; | ||
512 | unsigned i; | 614 | unsigned i; |
513 | 615 | ||
514 | /* XXX TODO: Argument parsing missing */ | 616 | legacy_descriptors = argc > 2 && !strcmp(argv[1], "-l"); |
515 | 617 | ||
516 | init_thread(threads); | 618 | init_thread(threads); |
517 | ep0_init(threads); | 619 | ep0_init(threads, legacy_descriptors); |
518 | 620 | ||
519 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) | 621 | for (i = 1; i < sizeof threads / sizeof *threads; ++i) |
520 | init_thread(threads + i); | 622 | init_thread(threads + i); |