1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <stddef.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <thread>
10 
11 #include <fbl/auto_call.h>
12 #include <usbhost/usbhost.h>
13 #include <unittest/unittest.h>
14 #include <zircon/device/usb-peripheral-test.h>
15 #include <zircon/device/usb-peripheral.h>
16 
17 namespace {
18 
19 struct usb_device* dev = nullptr;
20 struct usb_endpoint_descriptor* bulk_out_ep = nullptr;
21 struct usb_endpoint_descriptor* bulk_in_ep = nullptr;
22 struct usb_endpoint_descriptor* intr_ep = nullptr;
23 
24 constexpr size_t BUFFER_SIZE = 4096;
25 
26 // Data to send to the device
27 uint8_t send_buf[BUFFER_SIZE] = {};
28 // Buffer for receiving data from the device
29 int receive_buf[BUFFER_SIZE] = {};
30 
31 constexpr int TIMEOUT = 1000; // 1 seecond
32 
33 
34 // Fill send_buf with random values.
randomize()35 void randomize() {
36     // Generate some random data.
37     for (size_t i = 0; i < sizeof(send_buf); i++) {
38         send_buf[i] = static_cast<uint8_t>(random());
39     }
40 }
41 
42 // Tests control and interrupt transfers with specified transfer size.
control_interrupt_test(size_t transfer_size)43 bool control_interrupt_test(size_t transfer_size) {
44     BEGIN_TEST;
45 
46     randomize();
47 
48     // Send data to device via OUT control request.
49     int ret = usb_device_control_transfer(dev,
50                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
51                             USB_PERIPHERAL_TEST_SET_DATA, 0, 0, send_buf, transfer_size, TIMEOUT);
52     EXPECT_EQ(ret, transfer_size);
53 
54     // Receive data back from device via IN control request.
55     ret = usb_device_control_transfer(dev,
56                             USB_DIR_IN | USB_TYPE_VENDOR | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
57                             USB_PERIPHERAL_TEST_GET_DATA, 0, 0, receive_buf, transfer_size,
58                             TIMEOUT);
59     EXPECT_EQ(ret, transfer_size);
60 
61     // Sent and received data should match.
62     EXPECT_EQ(memcmp(send_buf, receive_buf, transfer_size), 0);
63 
64     // Create a thread to wait for interrupt request.
65     auto thread_func = [](struct usb_request** req) -> void {
66         *req = usb_request_wait(dev, TIMEOUT);
67     };
68     struct usb_request* complete_req = nullptr;
69     std::thread wait_thread (thread_func, &complete_req);
70 
71     // Queue read for interrupt request
72     auto* req = usb_request_new(dev, intr_ep);
73     EXPECT_NE(req, nullptr);
74     req->buffer = receive_buf;
75     req->buffer_length = static_cast<int>(transfer_size);
76     ret = usb_request_queue(req);
77     EXPECT_EQ(ret, 0);
78 
79     // Ask the device to send us an interrupt request containing the data we sent earlier.
80     ret = usb_device_control_transfer(dev,
81                             USB_DIR_OUT | USB_TYPE_VENDOR | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
82                             USB_PERIPHERAL_TEST_SEND_INTERUPT, 0, 0, nullptr, 0, TIMEOUT);
83     EXPECT_EQ(ret, 0);
84 
85     wait_thread.join();
86 
87     EXPECT_EQ(complete_req, req);
88 
89     // Sent data should match payload of interrupt request.
90     EXPECT_EQ(memcmp(send_buf, receive_buf, transfer_size), 0);
91 
92     usb_request_free(req);
93 
94     END_TEST;
95 }
96 
97 // Test control and interrupt requests with 8 byte transfer size.
control_interrupt_test_8()98 bool control_interrupt_test_8() {
99     return control_interrupt_test(8);
100 }
101 
102 // Test control and interrupt requests with 64 byte transfer size.
control_interrupt_test_64()103 bool control_interrupt_test_64() {
104     return control_interrupt_test(64);
105 }
106 
107 // Test control and interrupt requests with 100 byte transfer size.
control_interrupt_test_100()108 bool control_interrupt_test_100() {
109     return control_interrupt_test(100);
110 }
111 
112 // Test control and interrupt requests with 256 byte transfer size.
control_interrupt_test_256()113 bool control_interrupt_test_256() {
114     return control_interrupt_test(256);
115 }
116 
117 // Test control and interrupt requests with 1000 byte transfer size.
control_interrupt_test_1000()118 bool control_interrupt_test_1000() {
119     return control_interrupt_test(1000);
120 }
121 
122 // Tests bulk OUT and IN transfers.
123 // Send BUFFER_SIZE bytes to device, read back and compare.
bulk_test()124 bool bulk_test() {
125     BEGIN_TEST;
126 
127     auto* send_req = usb_request_new(dev, bulk_out_ep);
128     EXPECT_NE(send_req, nullptr);
129     send_req->buffer = send_buf;
130     send_req->buffer_length = static_cast<int>(BUFFER_SIZE);
131 
132     auto* receive_req = usb_request_new(dev, bulk_in_ep);
133     EXPECT_NE(send_req, nullptr);
134     receive_req->buffer = receive_buf;
135     receive_req->buffer_length = static_cast<int>(BUFFER_SIZE);
136 
137     for (int i = 0; i < 10; i++) {
138         randomize();
139 
140         // Create a thread to wait for request completions.
141         auto thread_func = [](struct usb_request** reqs) -> void {
142             *reqs++ = usb_request_wait(dev, TIMEOUT);
143             *reqs = usb_request_wait(dev, TIMEOUT);
144         };
145         struct usb_request* complete_reqs[2] = {};
146         std::thread wait_thread (thread_func, complete_reqs);
147 
148         // Queue requests in both directions
149         int ret = usb_request_queue(receive_req);
150         EXPECT_EQ(ret, 0);
151         ret = usb_request_queue(send_req);
152         EXPECT_EQ(ret, 0);
153 
154         wait_thread.join();
155 
156         EXPECT_NE(complete_reqs[0], nullptr);
157         EXPECT_NE(complete_reqs[1], nullptr);
158 
159         // Sent and received data should match.
160         EXPECT_EQ(memcmp(send_buf, receive_buf, BUFFER_SIZE), 0);
161     }
162 
163     usb_request_free(send_req);
164     usb_request_free(receive_req);
165 
166     END_TEST;
167 }
168 
169 // usb_host_load() will call this for all connected USB devices.
usb_device_added(const char * dev_name,void * client_data)170 int usb_device_added(const char *dev_name, void *client_data) {
171     usb_descriptor_iter iter;
172     struct usb_descriptor_header* header;
173     struct usb_interface_descriptor* intf = nullptr;
174     int ret;
175 
176     dev = usb_device_open(dev_name);
177     if (!dev) {
178         fprintf(stderr, "usb_device_open failed for %s\n", dev_name);
179         return 0;
180     }
181 
182     uint16_t vid = usb_device_get_vendor_id(dev);
183     uint16_t pid = usb_device_get_product_id(dev);
184 
185     if (vid != GOOGLE_USB_VID || pid != GOOGLE_USB_PERIPHERAL_TEST_PID) {
186         // Device doesn't match, so keep looking.
187         usb_device_close(dev);
188         dev = nullptr;
189         return 0;
190     }
191 
192     usb_descriptor_iter_init(dev, &iter);
193 
194     while ((header = usb_descriptor_iter_next(&iter)) != nullptr) {
195         if (header->bDescriptorType == USB_DT_INTERFACE) {
196             intf = reinterpret_cast<struct usb_interface_descriptor*>(header);
197         } else if (header->bDescriptorType == USB_DT_ENDPOINT) {
198             auto* ep = reinterpret_cast<struct usb_endpoint_descriptor*>(header);
199             if (usb_endpoint_type(ep) == USB_ENDPOINT_XFER_BULK) {
200                 if (usb_endpoint_dir_in(ep)) {
201                     bulk_in_ep = ep;
202                 } else {
203                     bulk_out_ep = ep;
204                 }
205             } else if (usb_endpoint_type(ep) == USB_ENDPOINT_XFER_INT) {
206                 intr_ep = ep;
207             }
208         }
209     }
210 
211     if (!intf || !bulk_out_ep || !bulk_in_ep || !intr_ep) {
212         fprintf(stderr, "could not find all our endpoints\n");
213         goto fail;
214     }
215 
216     ret = usb_device_claim_interface(dev, intf->bInterfaceNumber);
217     if (ret < 0) {
218         fprintf(stderr, "usb_device_claim_interface failed\n");
219         goto fail;
220     }
221 
222     // Device found, exit from usb_host_load().
223     return 1;
224 
225 fail:
226     usb_device_close(dev);
227     dev = nullptr;
228     // Test done, exit from usb_host_load().
229     return 1;
230 }
231 
usb_device_removed(const char * dev_name,void * client_data)232 int usb_device_removed(const char *dev_name, void *client_data) {
233     return 0;
234 }
235 
usb_discovery_done(void * client_data)236 int usb_discovery_done(void *client_data) {
237     return 0;
238 }
239 
240 } // anonymous namespace
241 
242 BEGIN_TEST_CASE(usb_peripheral_tests)
243 RUN_TEST(control_interrupt_test_8);
244 RUN_TEST(control_interrupt_test_64);
245 RUN_TEST(control_interrupt_test_100);
246 RUN_TEST(control_interrupt_test_256);
247 RUN_TEST(control_interrupt_test_1000);
248 RUN_TEST(bulk_test);
END_TEST_CASE(usb_peripheral_tests)249 END_TEST_CASE(usb_peripheral_tests)
250 
251 int main(int argc, char** argv) {
252     struct usb_host_context* context = usb_host_init();
253     if (!context) {
254         fprintf(stderr, "usb_host_context failed\n");
255         return -1;
256     }
257     auto cleanup = fbl::MakeAutoCall([&]() {
258         usb_host_cleanup(context);
259     });
260 
261     auto ret = usb_host_load(context, usb_device_added, usb_device_removed, usb_discovery_done,
262                              nullptr);
263     if (ret < 0) {
264         fprintf(stderr, "usb_host_load failed!\n");
265         goto fail;
266     }
267     if (!dev) {
268         fprintf(stderr, "No device found, skipping tests.\n");
269         goto fail;
270     }
271 
272     ret = unittest_run_all_tests(argc, argv) ? 0 : -1;
273 
274 fail:
275     if (dev) {
276         usb_device_close(dev);
277     }
278 
279     return ret;
280 }
281