1*86b64dcbSAndroid Build Coastguard Worker /*
2*86b64dcbSAndroid Build Coastguard Worker * libusb multi-thread test program
3*86b64dcbSAndroid Build Coastguard Worker * Copyright 2022-2023 Tormod Volden
4*86b64dcbSAndroid Build Coastguard Worker *
5*86b64dcbSAndroid Build Coastguard Worker * This program is free software; you can redistribute it and/or
6*86b64dcbSAndroid Build Coastguard Worker * modify it under the terms of the GNU Lesser General Public
7*86b64dcbSAndroid Build Coastguard Worker * License as published by the Free Software Foundation; either
8*86b64dcbSAndroid Build Coastguard Worker * version 2.1 of the License, or (at your option) any later version.
9*86b64dcbSAndroid Build Coastguard Worker *
10*86b64dcbSAndroid Build Coastguard Worker * This program is distributed in the hope that it will be useful,
11*86b64dcbSAndroid Build Coastguard Worker * but WITHOUT ANY WARRANTY; without even the implied warranty of
12*86b64dcbSAndroid Build Coastguard Worker * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13*86b64dcbSAndroid Build Coastguard Worker * Lesser General Public License for more details.
14*86b64dcbSAndroid Build Coastguard Worker *
15*86b64dcbSAndroid Build Coastguard Worker * You should have received a copy of the GNU Lesser General Public
16*86b64dcbSAndroid Build Coastguard Worker * License along with this library; if not, write to the Free Software
17*86b64dcbSAndroid Build Coastguard Worker * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*86b64dcbSAndroid Build Coastguard Worker */
19*86b64dcbSAndroid Build Coastguard Worker
20*86b64dcbSAndroid Build Coastguard Worker #include <config.h>
21*86b64dcbSAndroid Build Coastguard Worker
22*86b64dcbSAndroid Build Coastguard Worker #include <libusb.h>
23*86b64dcbSAndroid Build Coastguard Worker #include <stdio.h>
24*86b64dcbSAndroid Build Coastguard Worker #include <stdbool.h>
25*86b64dcbSAndroid Build Coastguard Worker
26*86b64dcbSAndroid Build Coastguard Worker #if defined(PLATFORM_POSIX)
27*86b64dcbSAndroid Build Coastguard Worker
28*86b64dcbSAndroid Build Coastguard Worker #include <pthread.h>
29*86b64dcbSAndroid Build Coastguard Worker typedef pthread_t thread_t;
30*86b64dcbSAndroid Build Coastguard Worker typedef void * thread_return_t;
31*86b64dcbSAndroid Build Coastguard Worker #define THREAD_RETURN_VALUE NULL
32*86b64dcbSAndroid Build Coastguard Worker #define THREAD_CALL_TYPE
33*86b64dcbSAndroid Build Coastguard Worker
thread_create(thread_t * thread,thread_return_t (* thread_entry)(void * arg),void * arg)34*86b64dcbSAndroid Build Coastguard Worker static inline int thread_create(thread_t *thread,
35*86b64dcbSAndroid Build Coastguard Worker thread_return_t (*thread_entry)(void *arg), void *arg)
36*86b64dcbSAndroid Build Coastguard Worker {
37*86b64dcbSAndroid Build Coastguard Worker return pthread_create(thread, NULL, thread_entry, arg) == 0 ? 0 : -1;
38*86b64dcbSAndroid Build Coastguard Worker }
39*86b64dcbSAndroid Build Coastguard Worker
thread_join(thread_t thread)40*86b64dcbSAndroid Build Coastguard Worker static inline void thread_join(thread_t thread)
41*86b64dcbSAndroid Build Coastguard Worker {
42*86b64dcbSAndroid Build Coastguard Worker (void)pthread_join(thread, NULL);
43*86b64dcbSAndroid Build Coastguard Worker }
44*86b64dcbSAndroid Build Coastguard Worker
45*86b64dcbSAndroid Build Coastguard Worker #include <stdatomic.h>
46*86b64dcbSAndroid Build Coastguard Worker
47*86b64dcbSAndroid Build Coastguard Worker #elif defined(PLATFORM_WINDOWS)
48*86b64dcbSAndroid Build Coastguard Worker
49*86b64dcbSAndroid Build Coastguard Worker typedef HANDLE thread_t;
50*86b64dcbSAndroid Build Coastguard Worker #define THREAD_RETURN_VALUE 0
51*86b64dcbSAndroid Build Coastguard Worker #define THREAD_CALL_TYPE __stdcall
52*86b64dcbSAndroid Build Coastguard Worker
53*86b64dcbSAndroid Build Coastguard Worker #if defined(__CYGWIN__)
54*86b64dcbSAndroid Build Coastguard Worker typedef DWORD thread_return_t;
55*86b64dcbSAndroid Build Coastguard Worker #else
56*86b64dcbSAndroid Build Coastguard Worker #include <process.h>
57*86b64dcbSAndroid Build Coastguard Worker typedef unsigned thread_return_t;
58*86b64dcbSAndroid Build Coastguard Worker #endif
59*86b64dcbSAndroid Build Coastguard Worker
thread_create(thread_t * thread,thread_return_t (__stdcall * thread_entry)(void * arg),void * arg)60*86b64dcbSAndroid Build Coastguard Worker static inline int thread_create(thread_t *thread,
61*86b64dcbSAndroid Build Coastguard Worker thread_return_t (__stdcall *thread_entry)(void *arg), void *arg)
62*86b64dcbSAndroid Build Coastguard Worker {
63*86b64dcbSAndroid Build Coastguard Worker #if defined(__CYGWIN__)
64*86b64dcbSAndroid Build Coastguard Worker *thread = CreateThread(NULL, 0, thread_entry, arg, 0, NULL);
65*86b64dcbSAndroid Build Coastguard Worker #else
66*86b64dcbSAndroid Build Coastguard Worker *thread = (HANDLE)_beginthreadex(NULL, 0, thread_entry, arg, 0, NULL);
67*86b64dcbSAndroid Build Coastguard Worker #endif
68*86b64dcbSAndroid Build Coastguard Worker return *thread != NULL ? 0 : -1;
69*86b64dcbSAndroid Build Coastguard Worker }
70*86b64dcbSAndroid Build Coastguard Worker
thread_join(thread_t thread)71*86b64dcbSAndroid Build Coastguard Worker static inline void thread_join(thread_t thread)
72*86b64dcbSAndroid Build Coastguard Worker {
73*86b64dcbSAndroid Build Coastguard Worker (void)WaitForSingleObject(thread, INFINITE);
74*86b64dcbSAndroid Build Coastguard Worker (void)CloseHandle(thread);
75*86b64dcbSAndroid Build Coastguard Worker }
76*86b64dcbSAndroid Build Coastguard Worker
77*86b64dcbSAndroid Build Coastguard Worker typedef volatile LONG atomic_bool;
78*86b64dcbSAndroid Build Coastguard Worker
79*86b64dcbSAndroid Build Coastguard Worker #define atomic_exchange InterlockedExchange
80*86b64dcbSAndroid Build Coastguard Worker #endif /* PLATFORM_WINDOWS */
81*86b64dcbSAndroid Build Coastguard Worker
82*86b64dcbSAndroid Build Coastguard Worker /* Test that creates and destroys contexts repeatedly */
83*86b64dcbSAndroid Build Coastguard Worker
84*86b64dcbSAndroid Build Coastguard Worker #define NTHREADS 8
85*86b64dcbSAndroid Build Coastguard Worker #define ITERS 64
86*86b64dcbSAndroid Build Coastguard Worker #define MAX_DEVCOUNT 128
87*86b64dcbSAndroid Build Coastguard Worker
88*86b64dcbSAndroid Build Coastguard Worker struct thread_info {
89*86b64dcbSAndroid Build Coastguard Worker int number;
90*86b64dcbSAndroid Build Coastguard Worker int enumerate;
91*86b64dcbSAndroid Build Coastguard Worker ssize_t devcount;
92*86b64dcbSAndroid Build Coastguard Worker int err;
93*86b64dcbSAndroid Build Coastguard Worker int iteration;
94*86b64dcbSAndroid Build Coastguard Worker } tinfo[NTHREADS];
95*86b64dcbSAndroid Build Coastguard Worker
96*86b64dcbSAndroid Build Coastguard Worker atomic_bool no_access[MAX_DEVCOUNT];
97*86b64dcbSAndroid Build Coastguard Worker
98*86b64dcbSAndroid Build Coastguard Worker /* Function called by backend during device initialization to convert
99*86b64dcbSAndroid Build Coastguard Worker * multi-byte fields in the device descriptor to host-endian format.
100*86b64dcbSAndroid Build Coastguard Worker * Copied from libusbi.h as we want test to be realistic and not depend on internals.
101*86b64dcbSAndroid Build Coastguard Worker */
usbi_localize_device_descriptor(struct libusb_device_descriptor * desc)102*86b64dcbSAndroid Build Coastguard Worker static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc)
103*86b64dcbSAndroid Build Coastguard Worker {
104*86b64dcbSAndroid Build Coastguard Worker desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
105*86b64dcbSAndroid Build Coastguard Worker desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
106*86b64dcbSAndroid Build Coastguard Worker desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
107*86b64dcbSAndroid Build Coastguard Worker desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
108*86b64dcbSAndroid Build Coastguard Worker }
109*86b64dcbSAndroid Build Coastguard Worker
init_and_exit(void * arg)110*86b64dcbSAndroid Build Coastguard Worker static thread_return_t THREAD_CALL_TYPE init_and_exit(void * arg)
111*86b64dcbSAndroid Build Coastguard Worker {
112*86b64dcbSAndroid Build Coastguard Worker struct thread_info *ti = (struct thread_info *) arg;
113*86b64dcbSAndroid Build Coastguard Worker
114*86b64dcbSAndroid Build Coastguard Worker for (ti->iteration = 0; ti->iteration < ITERS && !ti->err; ti->iteration++) {
115*86b64dcbSAndroid Build Coastguard Worker libusb_context *ctx = NULL;
116*86b64dcbSAndroid Build Coastguard Worker
117*86b64dcbSAndroid Build Coastguard Worker ti->err = libusb_init_context(&ctx, /*options=*/NULL, /*num_options=*/0);
118*86b64dcbSAndroid Build Coastguard Worker if (ti->err != 0) {
119*86b64dcbSAndroid Build Coastguard Worker break;
120*86b64dcbSAndroid Build Coastguard Worker }
121*86b64dcbSAndroid Build Coastguard Worker if (ti->enumerate) {
122*86b64dcbSAndroid Build Coastguard Worker libusb_device **devs;
123*86b64dcbSAndroid Build Coastguard Worker ti->devcount = libusb_get_device_list(ctx, &devs);
124*86b64dcbSAndroid Build Coastguard Worker if (ti->devcount < 0) {
125*86b64dcbSAndroid Build Coastguard Worker ti->err = (int)ti->devcount;
126*86b64dcbSAndroid Build Coastguard Worker break;
127*86b64dcbSAndroid Build Coastguard Worker }
128*86b64dcbSAndroid Build Coastguard Worker for (int i = 0; i < ti->devcount && ti->err == 0; i++) {
129*86b64dcbSAndroid Build Coastguard Worker libusb_device *dev = devs[i];
130*86b64dcbSAndroid Build Coastguard Worker struct libusb_device_descriptor desc;
131*86b64dcbSAndroid Build Coastguard Worker ti->err = libusb_get_device_descriptor(dev, &desc);
132*86b64dcbSAndroid Build Coastguard Worker if (ti->err != 0) {
133*86b64dcbSAndroid Build Coastguard Worker break;
134*86b64dcbSAndroid Build Coastguard Worker }
135*86b64dcbSAndroid Build Coastguard Worker if (no_access[i]) {
136*86b64dcbSAndroid Build Coastguard Worker continue;
137*86b64dcbSAndroid Build Coastguard Worker }
138*86b64dcbSAndroid Build Coastguard Worker libusb_device_handle *dev_handle;
139*86b64dcbSAndroid Build Coastguard Worker int open_err = libusb_open(dev, &dev_handle);
140*86b64dcbSAndroid Build Coastguard Worker if (open_err == LIBUSB_ERROR_ACCESS
141*86b64dcbSAndroid Build Coastguard Worker #if defined(PLATFORM_WINDOWS)
142*86b64dcbSAndroid Build Coastguard Worker || open_err == LIBUSB_ERROR_NOT_SUPPORTED
143*86b64dcbSAndroid Build Coastguard Worker || open_err == LIBUSB_ERROR_NOT_FOUND
144*86b64dcbSAndroid Build Coastguard Worker #endif
145*86b64dcbSAndroid Build Coastguard Worker ) {
146*86b64dcbSAndroid Build Coastguard Worker /* Use atomic swap to ensure we print warning only once across all threads.
147*86b64dcbSAndroid Build Coastguard Worker This is a warning and not a hard error because it should be fine to run tests
148*86b64dcbSAndroid Build Coastguard Worker even if we don't have access to some devices. */
149*86b64dcbSAndroid Build Coastguard Worker if (!atomic_exchange(&no_access[i], true)) {
150*86b64dcbSAndroid Build Coastguard Worker fprintf(stderr, "No access to device %04x:%04x, skipping transfer tests.\n", desc.idVendor, desc.idProduct);
151*86b64dcbSAndroid Build Coastguard Worker }
152*86b64dcbSAndroid Build Coastguard Worker continue;
153*86b64dcbSAndroid Build Coastguard Worker }
154*86b64dcbSAndroid Build Coastguard Worker if (open_err != 0) {
155*86b64dcbSAndroid Build Coastguard Worker ti->err = open_err;
156*86b64dcbSAndroid Build Coastguard Worker break;
157*86b64dcbSAndroid Build Coastguard Worker }
158*86b64dcbSAndroid Build Coastguard Worker /* Request raw descriptor via control transfer.
159*86b64dcbSAndroid Build Coastguard Worker This tests opening, transferring and closing from multiple threads in parallel. */
160*86b64dcbSAndroid Build Coastguard Worker struct libusb_device_descriptor raw_desc;
161*86b64dcbSAndroid Build Coastguard Worker int raw_desc_len = libusb_get_descriptor(dev_handle, LIBUSB_DT_DEVICE, 0, (unsigned char *)&raw_desc, sizeof(raw_desc));
162*86b64dcbSAndroid Build Coastguard Worker if (raw_desc_len < 0) {
163*86b64dcbSAndroid Build Coastguard Worker ti->err = raw_desc_len;
164*86b64dcbSAndroid Build Coastguard Worker goto close;
165*86b64dcbSAndroid Build Coastguard Worker }
166*86b64dcbSAndroid Build Coastguard Worker if (raw_desc_len != sizeof(raw_desc)) {
167*86b64dcbSAndroid Build Coastguard Worker fprintf(stderr, "Thread %d: device %d: unexpected raw descriptor length %d\n",
168*86b64dcbSAndroid Build Coastguard Worker ti->number, i, raw_desc_len);
169*86b64dcbSAndroid Build Coastguard Worker ti->err = LIBUSB_ERROR_OTHER;
170*86b64dcbSAndroid Build Coastguard Worker goto close;
171*86b64dcbSAndroid Build Coastguard Worker }
172*86b64dcbSAndroid Build Coastguard Worker usbi_localize_device_descriptor(&raw_desc);
173*86b64dcbSAndroid Build Coastguard Worker #define ASSERT_EQ(field) if (raw_desc.field != desc.field) { \
174*86b64dcbSAndroid Build Coastguard Worker fprintf(stderr, "Thread %d: device %d: mismatch in field " #field ": %d != %d\n", \
175*86b64dcbSAndroid Build Coastguard Worker ti->number, i, raw_desc.field, desc.field); \
176*86b64dcbSAndroid Build Coastguard Worker ti->err = LIBUSB_ERROR_OTHER; \
177*86b64dcbSAndroid Build Coastguard Worker goto close; \
178*86b64dcbSAndroid Build Coastguard Worker }
179*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bLength);
180*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bDescriptorType);
181*86b64dcbSAndroid Build Coastguard Worker #if !defined(PLATFORM_WINDOWS)
182*86b64dcbSAndroid Build Coastguard Worker /* these are hardcoded by the winusbx HID backend */
183*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bcdUSB);
184*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bDeviceClass);
185*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bDeviceSubClass);
186*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bDeviceProtocol);
187*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bMaxPacketSize0);
188*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bcdDevice);
189*86b64dcbSAndroid Build Coastguard Worker #endif
190*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(idVendor);
191*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(idProduct);
192*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(iManufacturer);
193*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(iProduct);
194*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(iSerialNumber);
195*86b64dcbSAndroid Build Coastguard Worker ASSERT_EQ(bNumConfigurations);
196*86b64dcbSAndroid Build Coastguard Worker close:
197*86b64dcbSAndroid Build Coastguard Worker libusb_close(dev_handle);
198*86b64dcbSAndroid Build Coastguard Worker }
199*86b64dcbSAndroid Build Coastguard Worker libusb_free_device_list(devs, 1);
200*86b64dcbSAndroid Build Coastguard Worker }
201*86b64dcbSAndroid Build Coastguard Worker
202*86b64dcbSAndroid Build Coastguard Worker libusb_exit(ctx);
203*86b64dcbSAndroid Build Coastguard Worker }
204*86b64dcbSAndroid Build Coastguard Worker return (thread_return_t) THREAD_RETURN_VALUE;
205*86b64dcbSAndroid Build Coastguard Worker }
206*86b64dcbSAndroid Build Coastguard Worker
test_multi_init(int enumerate)207*86b64dcbSAndroid Build Coastguard Worker static int test_multi_init(int enumerate)
208*86b64dcbSAndroid Build Coastguard Worker {
209*86b64dcbSAndroid Build Coastguard Worker thread_t threadId[NTHREADS];
210*86b64dcbSAndroid Build Coastguard Worker int errs = 0;
211*86b64dcbSAndroid Build Coastguard Worker int t, i;
212*86b64dcbSAndroid Build Coastguard Worker ssize_t last_devcount = 0;
213*86b64dcbSAndroid Build Coastguard Worker int devcount_mismatch = 0;
214*86b64dcbSAndroid Build Coastguard Worker int access_failures = 0;
215*86b64dcbSAndroid Build Coastguard Worker
216*86b64dcbSAndroid Build Coastguard Worker printf("Starting %d threads\n", NTHREADS);
217*86b64dcbSAndroid Build Coastguard Worker for (t = 0; t < NTHREADS; t++) {
218*86b64dcbSAndroid Build Coastguard Worker tinfo[t].err = 0;
219*86b64dcbSAndroid Build Coastguard Worker tinfo[t].number = t;
220*86b64dcbSAndroid Build Coastguard Worker tinfo[t].enumerate = enumerate;
221*86b64dcbSAndroid Build Coastguard Worker thread_create(&threadId[t], &init_and_exit, (void *) &tinfo[t]);
222*86b64dcbSAndroid Build Coastguard Worker }
223*86b64dcbSAndroid Build Coastguard Worker
224*86b64dcbSAndroid Build Coastguard Worker for (t = 0; t < NTHREADS; t++) {
225*86b64dcbSAndroid Build Coastguard Worker thread_join(threadId[t]);
226*86b64dcbSAndroid Build Coastguard Worker if (tinfo[t].err) {
227*86b64dcbSAndroid Build Coastguard Worker errs++;
228*86b64dcbSAndroid Build Coastguard Worker fprintf(stderr,
229*86b64dcbSAndroid Build Coastguard Worker "Thread %d failed (iteration %d): %s\n",
230*86b64dcbSAndroid Build Coastguard Worker tinfo[t].number,
231*86b64dcbSAndroid Build Coastguard Worker tinfo[t].iteration,
232*86b64dcbSAndroid Build Coastguard Worker libusb_error_name(tinfo[t].err));
233*86b64dcbSAndroid Build Coastguard Worker } else if (enumerate) {
234*86b64dcbSAndroid Build Coastguard Worker if (t > 0 && tinfo[t].devcount != last_devcount) {
235*86b64dcbSAndroid Build Coastguard Worker devcount_mismatch++;
236*86b64dcbSAndroid Build Coastguard Worker printf("Device count mismatch: Thread %d discovered %ld devices instead of %ld\n",
237*86b64dcbSAndroid Build Coastguard Worker tinfo[t].number,
238*86b64dcbSAndroid Build Coastguard Worker (long int) tinfo[t].devcount,
239*86b64dcbSAndroid Build Coastguard Worker (long int) last_devcount);
240*86b64dcbSAndroid Build Coastguard Worker }
241*86b64dcbSAndroid Build Coastguard Worker last_devcount = tinfo[t].devcount;
242*86b64dcbSAndroid Build Coastguard Worker }
243*86b64dcbSAndroid Build Coastguard Worker }
244*86b64dcbSAndroid Build Coastguard Worker
245*86b64dcbSAndroid Build Coastguard Worker for (i = 0; i < MAX_DEVCOUNT; i++)
246*86b64dcbSAndroid Build Coastguard Worker if (no_access[i])
247*86b64dcbSAndroid Build Coastguard Worker access_failures++;
248*86b64dcbSAndroid Build Coastguard Worker
249*86b64dcbSAndroid Build Coastguard Worker if (enumerate && !devcount_mismatch)
250*86b64dcbSAndroid Build Coastguard Worker printf("All threads discovered %ld devices (%i not opened)\n",
251*86b64dcbSAndroid Build Coastguard Worker (long int) last_devcount, access_failures);
252*86b64dcbSAndroid Build Coastguard Worker
253*86b64dcbSAndroid Build Coastguard Worker return errs + devcount_mismatch;
254*86b64dcbSAndroid Build Coastguard Worker }
255*86b64dcbSAndroid Build Coastguard Worker
main(void)256*86b64dcbSAndroid Build Coastguard Worker int main(void)
257*86b64dcbSAndroid Build Coastguard Worker {
258*86b64dcbSAndroid Build Coastguard Worker int errs = 0;
259*86b64dcbSAndroid Build Coastguard Worker
260*86b64dcbSAndroid Build Coastguard Worker printf("Running multithreaded init/exit test...\n");
261*86b64dcbSAndroid Build Coastguard Worker errs += test_multi_init(0);
262*86b64dcbSAndroid Build Coastguard Worker printf("Running multithreaded init/exit test with enumeration...\n");
263*86b64dcbSAndroid Build Coastguard Worker errs += test_multi_init(1);
264*86b64dcbSAndroid Build Coastguard Worker printf("All done, %d errors\n", errs);
265*86b64dcbSAndroid Build Coastguard Worker
266*86b64dcbSAndroid Build Coastguard Worker return errs != 0;
267*86b64dcbSAndroid Build Coastguard Worker }
268