| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <getopt.h> |
| #include <lib/fit/defer.h> |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/time.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include <algorithm> |
| #include <numeric> |
| #include <span> |
| #include <thread> |
| |
| #include <fbl/algorithm.h> |
| #include <usb/peripheral-test.h> |
| #include <usb/peripheral.h> |
| #include <usbhost/usbhost.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| struct usb_device* dev = nullptr; |
| struct usb_endpoint_descriptor* bulk_out_ep = nullptr; |
| struct usb_endpoint_descriptor* bulk_in_ep = nullptr; |
| struct usb_endpoint_descriptor* intr_out_ep = nullptr; |
| struct usb_endpoint_descriptor* intr_in_ep = nullptr; |
| |
| static uint8_t test_interface; |
| constexpr uint64_t kUsbTimeoutMilliseconds = 1000; // 1 second |
| constexpr uint64_t kSecondsToNanoseconds = 1'000'000'000; |
| constexpr uint64_t kMicrosecondsToNanoseconds = 1000; |
| constexpr uint64_t kNanosecondsToMilliseconds = 1'000'000; |
| constexpr uint64_t kBulkRequestSize = 512; |
| constexpr uint64_t kInterruptRequestSize = 64; |
| |
| constexpr char kUsageMsg[] = R"( |
| [OPTIONS] |
| --help -help [-h] Prints this message. |
| --time -time [-t] Configurable time for tests to run. |
| --bytes -bytes [-b] Configurable transfer bytes for data transfer. |
| --bulk-iterations -bulk-iterations [-i] Configurable iteration for bulk transfer. |
| --buffersize -buffersize [-s] Configurable buffer size. Default buffer size set to 4096 bytes. |
| --retest -retest [-r] Configurable retest transfer type. Specify one of |
| {CONTROL, INTERRUPT, BULK} to retest transfer type. |
| --retest-iterations -retest-iterations [-n] Configurable number of times the transfer type is tested. |
| )"; |
| |
| class UsbPeripheralConfigurableTests : public ::zxtest::Test { |
| public: |
| // User configurable time (in milliseconds) for USB peripheral tests. |
| inline static int64_t time_ms_ = 100; |
| |
| // User configurable transfer byte size. |
| inline static size_t bytes_ = 64; |
| |
| // User configurable iterations for bulk transfer test. |
| inline static int bulk_iterations_ = 2; |
| |
| // User configurable buffer size. |
| inline static uint64_t buffer_size_ = 4096; |
| |
| // User configurable to repeat test. |
| enum class TransferOptions { CONTROL, INTERRUPT, BULK, NONE }; |
| inline static TransferOptions retest_config_ = TransferOptions::BULK; |
| inline static int retest_iterations_ = 1; |
| static zx_status_t SetRetestConfigFromString(const std::string& str); |
| |
| // Help option. |
| inline static bool help_ = false; |
| }; |
| |
| zx_status_t UsbPeripheralConfigurableTests::SetRetestConfigFromString(const std::string& str) { |
| if (str == "CONTROL") { |
| retest_config_ = UsbPeripheralConfigurableTests::TransferOptions::CONTROL; |
| return ZX_OK; |
| } else if (str == "INTERRUPT") { |
| retest_config_ = UsbPeripheralConfigurableTests::TransferOptions::INTERRUPT; |
| return ZX_OK; |
| } else if (str == "BULK") { |
| retest_config_ = UsbPeripheralConfigurableTests::TransferOptions::BULK; |
| return ZX_OK; |
| } else { |
| retest_config_ = UsbPeripheralConfigurableTests::TransferOptions::NONE; |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| |
| class Timer { |
| public: |
| void Start() { start_ = GetCurrentTimeNanoseconds(); } |
| int64_t Stop() { |
| stop_ = GetTime(); |
| return stop_; |
| } |
| // Gets the amount of milliseconds since |start_|. |
| int64_t GetElapsedTimeMilliseconds() const { |
| return (GetCurrentTimeNanoseconds() - start_) / kNanosecondsToMilliseconds; |
| } |
| |
| private: |
| int64_t start_; |
| int64_t stop_; |
| static int64_t GetCurrentTimeNanoseconds() { |
| struct timeval tv; |
| if (gettimeofday(&tv, nullptr) < 0) { |
| return 0; |
| } |
| return tv.tv_sec * kSecondsToNanoseconds + tv.tv_usec * kMicrosecondsToNanoseconds; |
| } |
| int64_t GetTime() const { |
| return (GetCurrentTimeNanoseconds() - start_) / kNanosecondsToMilliseconds; |
| } |
| }; |
| |
| void pattern_buffer(cpp20::span<uint8_t> container, uint8_t distinct_buffer_number) { |
| // Generate a known pattern for the buffer. |
| uint64_t container_size = container.size(); |
| for (size_t i = 0; i < container_size - 1; i += 2) { |
| container[i] = 0x1; |
| container[i + 1] = distinct_buffer_number - 1; |
| } |
| // Setting buffer borders |
| container.front() = distinct_buffer_number; |
| container.back() = distinct_buffer_number; |
| } |
| |
| // Tests control and interrupt transfers with specified transfer size. |
| void control_interrupt_test(size_t transfer_size) { |
| uint8_t distinct_buffer_number = 3; |
| std::unique_ptr<uint8_t[]> send_buffer = std::make_unique<uint8_t[]>(transfer_size); |
| std::unique_ptr<uint8_t[]> receive_buffer = std::make_unique<uint8_t[]>(transfer_size); |
| |
| pattern_buffer({send_buffer.get(), transfer_size}, distinct_buffer_number); |
| |
| Timer timer; |
| timer.Start(); |
| |
| // Send data to device via OUT control request. |
| int ret = usb_device_control_transfer( |
| dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_PERIPHERAL_TEST_SET_DATA, 0, |
| test_interface, send_buffer.get(), static_cast<int>(transfer_size), kUsbTimeoutMilliseconds); |
| ASSERT_EQ(ret, static_cast<int>(transfer_size)); |
| |
| // Receive data back from device via IN control request. |
| ret = usb_device_control_transfer(dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, |
| USB_PERIPHERAL_TEST_GET_DATA, 0, test_interface, |
| receive_buffer.get(), static_cast<int>(transfer_size), |
| kUsbTimeoutMilliseconds); |
| ASSERT_EQ(ret, static_cast<int>(transfer_size)); |
| |
| // Sent and received data should match. |
| EXPECT_EQ(memcmp(send_buffer.get(), receive_buffer.get(), transfer_size), 0); |
| |
| // Create a thread to wait for interrupt request. |
| auto thread_func = [](struct usb_request** req) -> void { |
| *req = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| }; |
| |
| struct usb_request* complete_req = nullptr; |
| std::thread wait_thread(thread_func, &complete_req); |
| |
| // Queue read for interrupt request |
| auto* req = usb_request_new(dev, intr_in_ep); |
| EXPECT_NE(req, nullptr); |
| req->buffer = receive_buffer.get(); |
| req->buffer_length = static_cast<int>(transfer_size); |
| ret = usb_request_queue(req); |
| EXPECT_EQ(ret, 0); |
| |
| // Ask the device to send us an interrupt request containing the data we sent earlier. |
| ret = usb_device_control_transfer(dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, |
| USB_PERIPHERAL_TEST_SEND_INTERUPT, 0, test_interface, nullptr, |
| 0, kUsbTimeoutMilliseconds); |
| ASSERT_EQ(ret, 0); |
| |
| wait_thread.join(); |
| |
| ASSERT_EQ(complete_req, req); |
| EXPECT_EQ(static_cast<size_t>(req->actual_length), transfer_size); |
| |
| // Sent data should match payload of interrupt request. |
| EXPECT_EQ(memcmp(send_buffer.get(), receive_buffer.get(), transfer_size), 0); |
| |
| usb_request_free(req); |
| |
| const int64_t elapsed_time = timer.Stop(); |
| printf("[ ] Transferred %zu bytes/%lums\n", transfer_size, elapsed_time); |
| } |
| |
| // Tests bulk transfers with specified transfer size. |
| void bulk_test(uint64_t buffer_size, size_t bulk_iterations) { |
| buffer_size = fbl::round_up(buffer_size, kBulkRequestSize); |
| uint8_t distinct_buffer_number = 3; |
| std::unique_ptr<uint8_t[]> send_buffer = std::make_unique<uint8_t[]>(buffer_size); |
| std::unique_ptr<uint8_t[]> receive_buffer = std::make_unique<uint8_t[]>(buffer_size); |
| |
| // Initialize the buffer |
| memset(send_buffer.get(), 9, buffer_size); |
| memset(receive_buffer.get(), 9, buffer_size); |
| |
| auto* send_req = usb_request_new(dev, bulk_out_ep); |
| EXPECT_NE(send_req, nullptr); |
| send_req->buffer = send_buffer.get(); |
| send_req->buffer_length = static_cast<int>(buffer_size); |
| |
| auto* receive_req = usb_request_new(dev, bulk_in_ep); |
| EXPECT_NE(receive_req, nullptr); |
| receive_req->buffer = receive_buffer.get(); |
| receive_req->buffer_length = static_cast<int>(buffer_size); |
| |
| for (size_t i = 0; i < bulk_iterations; i++) { |
| pattern_buffer({send_buffer.get(), buffer_size}, distinct_buffer_number); |
| |
| // Create a thread to wait for request completions. |
| auto thread_func = [](struct usb_request** reqs) -> void { |
| *reqs++ = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| *reqs = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| }; |
| |
| struct usb_request* complete_reqs[2] = {}; |
| std::thread wait_thread(thread_func, complete_reqs); |
| |
| // Queue requests in both directions |
| int ret = usb_request_queue(send_req); |
| EXPECT_EQ(ret, 0); |
| ret = usb_request_queue(receive_req); |
| EXPECT_EQ(ret, 0); |
| |
| wait_thread.join(); |
| |
| EXPECT_NE(complete_reqs[0], nullptr); |
| EXPECT_NE(complete_reqs[1], nullptr); |
| |
| // Sent and received data should match. |
| EXPECT_EQ(memcmp(send_buffer.get(), receive_buffer.get(), buffer_size), 0); |
| |
| // Changing the number for the next buffer and bulk iteration. Helps verify that each transfer |
| // is different. |
| if (distinct_buffer_number > 9) |
| distinct_buffer_number = 3; |
| else |
| distinct_buffer_number++; |
| } |
| |
| usb_request_free(send_req); |
| usb_request_free(receive_req); |
| } |
| |
| void interrupt_test(size_t transfer_bytes) { |
| transfer_bytes = fbl::round_up(transfer_bytes, kInterruptRequestSize); |
| uint8_t distinct_buffer_number = 3; |
| std::unique_ptr<uint8_t[]> send_buffer = std::make_unique<uint8_t[]>(transfer_bytes); |
| std::unique_ptr<uint8_t[]> receive_buffer = std::make_unique<uint8_t[]>(transfer_bytes); |
| |
| // Initialize the buffer |
| memset(send_buffer.get(), 9, transfer_bytes); |
| memset(receive_buffer.get(), 9, transfer_bytes); |
| |
| auto* send_req = usb_request_new(dev, intr_out_ep); |
| ASSERT_NE(send_req, nullptr); |
| send_req->buffer = send_buffer.get(); |
| send_req->buffer_length = static_cast<int>(transfer_bytes); |
| |
| auto* receive_req = usb_request_new(dev, intr_in_ep); |
| ASSERT_NE(receive_req, nullptr); |
| receive_req->buffer = receive_buffer.get(); |
| receive_req->buffer_length = static_cast<int>(transfer_bytes); |
| |
| pattern_buffer({send_buffer.get(), transfer_bytes}, distinct_buffer_number); |
| |
| // Create a thread to wait for request completions. |
| auto thread_func = [](struct usb_request** reqs) -> void { |
| *reqs++ = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| *reqs = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| }; |
| |
| struct usb_request* complete_reqs[2] = {}; |
| std::thread wait_thread(thread_func, complete_reqs); |
| |
| // Queue requests in both directions |
| int res = usb_request_queue(send_req); |
| EXPECT_EQ(res, 0); |
| res = usb_request_queue(receive_req); |
| EXPECT_EQ(res, 0); |
| |
| wait_thread.join(); |
| |
| EXPECT_NE(complete_reqs[0], nullptr); |
| EXPECT_NE(complete_reqs[1], nullptr); |
| |
| // Sent and received data should match. |
| EXPECT_EQ(memcmp(send_buffer.get(), receive_buffer.get(), transfer_bytes), 0); |
| |
| usb_request_free(send_req); |
| usb_request_free(receive_req); |
| } |
| |
| // ====================== User configurable tests =================================== |
| // Tests end to end interrupt transfer. |
| TEST_F(UsbPeripheralConfigurableTests, interrupt_function) { |
| ASSERT_NO_FATAL_FAILURE(interrupt_test(bytes_)); |
| } |
| |
| // Tests bulk transfer for long periods of time. Time is user determined. |
| TEST_F(UsbPeripheralConfigurableTests, stress_test_configurable) { |
| int times_ran = 0; |
| std::vector<int64_t> recorded_times; |
| Timer timer_loop; |
| Timer timer_recording; |
| timer_loop.Start(); |
| |
| while (timer_loop.GetElapsedTimeMilliseconds() < time_ms_) { |
| timer_recording.Start(); |
| ASSERT_NO_FATAL_FAILURE(bulk_test(buffer_size_, bulk_iterations_)); |
| recorded_times.push_back(timer_recording.Stop()); |
| times_ran++; |
| } |
| |
| int64_t elapsed_time = timer_loop.Stop(); |
| uint64_t total_transfer_bytes = times_ran * buffer_size_ * bulk_iterations_; |
| printf("[ ] Bulk test ran %d times in %lums\n", times_ran, elapsed_time); |
| printf("[ ] Total bytes transferred: %lu\n", total_transfer_bytes); |
| |
| if (!recorded_times.empty()) { |
| uint64_t average = |
| std::reduce(recorded_times.begin(), recorded_times.end()) / recorded_times.size(); |
| printf("[ ] - Average transfer: %lu/%ldms per bulk test call\n", |
| buffer_size_ * bulk_iterations_, average); |
| } |
| EXPECT_FALSE(recorded_times.empty()); |
| } |
| |
| // Tests the repetition of a transfer type test. |
| TEST_F(UsbPeripheralConfigurableTests, retest_transfer_type_test) { |
| std::vector<int64_t> recorded_times; |
| Timer timer; |
| switch (retest_config_) { |
| case TransferOptions::CONTROL: |
| for (int i = 0; i < retest_iterations_; i++) { |
| timer.Start(); |
| ASSERT_NO_FATAL_FAILURE(control_interrupt_test(bytes_)); |
| recorded_times.push_back(timer.Stop()); |
| } |
| break; |
| case TransferOptions::INTERRUPT: |
| for (int i = 0; i < retest_iterations_; i++) { |
| timer.Start(); |
| ASSERT_NO_FATAL_FAILURE(interrupt_test(bytes_)); |
| recorded_times.push_back(timer.Stop()); |
| } |
| break; |
| case TransferOptions::BULK: |
| for (int i = 0; i < retest_iterations_; i++) { |
| timer.Start(); |
| ASSERT_NO_FATAL_FAILURE(bulk_test(buffer_size_, bulk_iterations_)); |
| recorded_times.push_back(timer.Stop()); |
| } |
| break; |
| default: |
| fprintf(stderr, "[ ] Transfer type does not exist.\n"); |
| break; |
| } |
| |
| if (!recorded_times.empty()) { |
| uint64_t average = |
| std::reduce(recorded_times.begin(), recorded_times.end()) / recorded_times.size(); |
| printf("[ ] - Average time for %d iterations is: %ldms\n", retest_iterations_, |
| average); |
| } |
| EXPECT_FALSE(recorded_times.empty()); |
| } |
| |
| // Tests bulk OUT and IN transfers from user configurable data. |
| TEST_F(UsbPeripheralConfigurableTests, bulk_test) { |
| ASSERT_NO_FATAL_FAILURE(bulk_test(buffer_size_, bulk_iterations_)); |
| } |
| |
| // Test control and interrupt requests from the user configurable data |
| TEST_F(UsbPeripheralConfigurableTests, control_interrupt_test_configurable) { |
| ASSERT_NO_FATAL_FAILURE(control_interrupt_test(bytes_)); |
| } |
| |
| // Tests the buffer size of Bulk transfers. |
| TEST_F(UsbPeripheralConfigurableTests, size_transfer_bytes) { |
| // To run this test, first it checks if the control transfer is working. If it succeeds, it goes |
| // on to test the size transfer. |
| std::unique_ptr<uint8_t[]> send_buf = std::make_unique<uint8_t[]>(bytes_); |
| std::unique_ptr<uint8_t[]> receive_buf = std::make_unique<uint8_t[]>(bytes_); |
| |
| memset(send_buf.get(), 9, bytes_); |
| |
| int res = usb_device_control_transfer( |
| dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_PERIPHERAL_TEST_SET_DATA, 0, |
| test_interface, send_buf.get(), static_cast<int>(bytes_), kUsbTimeoutMilliseconds); |
| ASSERT_EQ(res, static_cast<int>(bytes_)); |
| |
| // Receive data back from device via IN control request. |
| res = usb_device_control_transfer( |
| dev, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, USB_PERIPHERAL_TEST_GET_DATA, 0, |
| test_interface, receive_buf.get(), static_cast<int>(bytes_), kUsbTimeoutMilliseconds); |
| ASSERT_EQ(res, static_cast<int>(bytes_)); |
| |
| // Sent and received data should match. |
| ASSERT_EQ(memcmp(send_buf.get(), receive_buf.get(), bytes_), 0); |
| |
| // Now the size_test can run. A temporary buffer size that is not a multiple of kBulkRequestSize. |
| uint64_t temp_buffer_size = 700; |
| uint8_t distinct_buffer_number = 1; |
| std::unique_ptr<uint8_t[]> receive_buffer = std::make_unique<uint8_t[]>(temp_buffer_size); |
| |
| pattern_buffer({receive_buffer.get(), temp_buffer_size}, distinct_buffer_number); |
| |
| auto* receive_req = usb_request_new(dev, bulk_in_ep); |
| EXPECT_NE(receive_req, nullptr); |
| receive_req->buffer = receive_buffer.get(); |
| receive_req->buffer_length = kBulkRequestSize; |
| |
| int ret = |
| usb_device_control_transfer(dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, |
| USB_PERIPHERAL_TEST_BULK_TRANSFER_SIZE, 0, test_interface, |
| &temp_buffer_size, sizeof(uint64_t), kUsbTimeoutMilliseconds); |
| ASSERT_EQ(ret, sizeof(uint64_t)); |
| |
| auto thread_func = [](struct usb_request** req) -> void { |
| *req = usb_request_wait(dev, kUsbTimeoutMilliseconds); |
| }; |
| |
| struct usb_request* complete_req = nullptr; |
| std::thread wait_thread(thread_func, &complete_req); |
| |
| // Queue read for bulk request |
| ret = usb_request_queue(receive_req); |
| EXPECT_EQ(ret, 0); |
| |
| wait_thread.join(); |
| |
| EXPECT_EQ(receive_req, complete_req); |
| |
| // Transfer is expected to fail due to incorrect buffer size and the receive buffer will |
| // be different. |
| EXPECT_FALSE(std::all_of( |
| receive_buffer.get(), receive_buffer.get() + temp_buffer_size, |
| [distinct_buffer_number](uint8_t elem) { return elem == distinct_buffer_number; })); |
| |
| usb_request_free(receive_req); |
| } |
| |
| // =============================== Fixed tests =================================== |
| // Test control and interrupt requests with 8 byte transfer size. |
| TEST(UsbPeripheralFixedTests, control_interrupt_test_8) { |
| ASSERT_NO_FATAL_FAILURE(control_interrupt_test(8)); |
| } |
| |
| // Test control and interrupt requests with 256 byte transfer size. |
| TEST(UsbPeripheralFixedTests, control_interrupt_test_256) { |
| ASSERT_NO_FATAL_FAILURE(control_interrupt_test(256)); |
| } |
| |
| // Test control and interrupt requests with 1024 byte transfer size. |
| TEST(UsbPeripheralFixedTests, control_interrupt_test_1024) { |
| ASSERT_NO_FATAL_FAILURE(control_interrupt_test(1024)); |
| } |
| |
| // usb_host_load() will call this for all connected USB devices. |
| int usb_device_added(const char* dev_name, void* client_data) { |
| usb_descriptor_iter iter; |
| struct usb_descriptor_header* header; |
| struct usb_interface_descriptor* intf = nullptr; |
| int ret; |
| |
| dev = usb_device_open(dev_name); |
| if (!dev) { |
| fprintf(stderr, "usb_device_open failed for %s\n", dev_name); |
| return 0; |
| } |
| |
| auto cleanup = fit::defer([]() { |
| usb_device_close(dev); |
| dev = nullptr; |
| }); |
| |
| uint16_t vid = usb_device_get_vendor_id(dev); |
| uint16_t pid = usb_device_get_product_id(dev); |
| |
| if (vid != GOOGLE_USB_VID || |
| (pid != GOOGLE_USB_FUNCTION_TEST_PID && pid != GOOGLE_USB_CDC_AND_FUNCTION_TEST_PID)) { |
| // Device doesn't match, so keep looking. |
| return 0; |
| } |
| |
| usb_descriptor_iter_init(dev, &iter); |
| |
| while ((header = usb_descriptor_iter_next(&iter)) != nullptr) { |
| if (header->bDescriptorType == USB_DT_INTERFACE) { |
| intf = reinterpret_cast<struct usb_interface_descriptor*>(header); |
| } else if (header->bDescriptorType == USB_DT_ENDPOINT) { |
| auto* ep = reinterpret_cast<struct usb_endpoint_descriptor*>(header); |
| if (usb_endpoint_type(ep) == USB_ENDPOINT_XFER_BULK) { |
| if (usb_endpoint_dir_in(ep)) { |
| bulk_in_ep = ep; |
| } else { |
| bulk_out_ep = ep; |
| } |
| } else if (usb_endpoint_type(ep) == USB_ENDPOINT_XFER_INT) { |
| if (usb_endpoint_dir_in(ep)) { |
| intr_in_ep = ep; |
| } else { |
| intr_out_ep = ep; |
| } |
| } |
| } |
| } |
| |
| if (!intf || !bulk_out_ep || !bulk_in_ep || !intr_in_ep || !intr_out_ep) { |
| fprintf(stderr, "could not find all our endpoints\n"); |
| return 1; |
| } |
| |
| ret = usb_device_claim_interface(dev, intf->bInterfaceNumber); |
| if (ret < 0) { |
| fprintf(stderr, "usb_device_claim_interface failed\n"); |
| } |
| test_interface = intf->bInterfaceNumber; |
| |
| cleanup.cancel(); |
| |
| // Device found, exit from usb_host_load(). |
| return 1; |
| } |
| |
| int usb_device_removed(const char* dev_name, void* client_data) { return 0; } |
| |
| int usb_discovery_done(void* client_data) { return 0; } |
| |
| } // anonymous namespace |
| |
| int main(int argc, char** argv) { |
| // Making command line arguments global to pass to UsbPeripheralConfigurableTests. |
| static const struct option long_opts[] = { |
| {"time", required_argument, nullptr, 't'}, |
| {"bytes", required_argument, nullptr, 'b'}, |
| {"bulk-iterations", required_argument, nullptr, 'i'}, |
| {"buffer-size", required_argument, nullptr, 's'}, |
| {"help", no_argument, nullptr, 'h'}, |
| {"retest", required_argument, nullptr, 'r'}, |
| {"retest-iterations", required_argument, nullptr, 'n'}, |
| {0, 0, 0, 0}, |
| }; |
| |
| int opt; |
| while ((opt = getopt_long_only(argc, argv, "t:b:i:s:r:n:h", long_opts, nullptr)) != -1) { |
| switch (opt) { |
| case 't': |
| UsbPeripheralConfigurableTests::time_ms_ = std::stoi(optarg); |
| break; |
| case 'b': |
| UsbPeripheralConfigurableTests::bytes_ = std::stoi(optarg); |
| break; |
| case 'i': |
| UsbPeripheralConfigurableTests::bulk_iterations_ = std::stoi(optarg); |
| break; |
| case 's': { |
| uint64_t buffer_size = std::stoi(optarg); |
| if (buffer_size == 0) { |
| fprintf(stderr, "Buffer size is 0, nothing will be transferred. Choose another size.\n"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| UsbPeripheralConfigurableTests::buffer_size_ = buffer_size; |
| break; |
| } |
| case 'r': { |
| zx_status_t status = UsbPeripheralConfigurableTests::SetRetestConfigFromString(optarg); |
| if (status != ZX_OK) { |
| fprintf( |
| stderr, |
| "Invalid transfer type for usb-peripheral-tests, ZX_STATUS: ZX_ERR_INVALID_ARGS. Expected one of: CONTROL, INTERRUPT, BULK\n"); |
| return status; |
| } |
| break; |
| } |
| case 'n': |
| UsbPeripheralConfigurableTests::retest_iterations_ = std::stoi(optarg); |
| break; |
| case 'h': |
| UsbPeripheralConfigurableTests::help_ = true; |
| fprintf(stderr, kUsageMsg); |
| return 0; |
| } |
| } |
| |
| struct usb_host_context* context = usb_host_init(); |
| if (!context) { |
| fprintf(stderr, "usb_host_context failed\n"); |
| return -1; |
| } |
| auto cleanup = fit::defer([context]() { |
| usb_host_cleanup(context); |
| if (dev) { |
| usb_device_close(dev); |
| } |
| }); |
| |
| auto ret = |
| usb_host_load(context, usb_device_added, usb_device_removed, usb_discovery_done, nullptr); |
| if (ret < 0) { |
| fprintf(stderr, "usb_host_load failed!\n"); |
| return -1; |
| } |
| if (!dev) { |
| fprintf(stderr, "No device found, skipping tests.\n"); |
| return 0; |
| } |
| |
| // TODO: Add arguments for the zxtest flag options. |
| return zxtest::RunAllTests(0, nullptr) ? 0 : -1; |
| } |