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