diff --git a/TESTS/host_tests/pyusb_basic.py b/TESTS/host_tests/pyusb_basic.py new file mode 100644 index 0000000000..24c7e95145 --- /dev/null +++ b/TESTS/host_tests/pyusb_basic.py @@ -0,0 +1,349 @@ +""" +mbed SDK +Copyright (c) 2018-2018 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +from __future__ import print_function + +from mbed_host_tests import BaseHostTest +from argparse import ArgumentParser +import time +import sys +from threading import Thread + +import usb.core +from usb.util import build_request_type +from usb.util import CTRL_OUT, CTRL_IN +from usb.util import CTRL_TYPE_STANDARD, CTRL_TYPE_CLASS, CTRL_TYPE_VENDOR +from usb.util import (CTRL_RECIPIENT_DEVICE, CTRL_RECIPIENT_INTERFACE, + CTRL_RECIPIENT_ENDPOINT, CTRL_RECIPIENT_OTHER) + +def get_interface(dev, interface, alternate=0): + intf = None + for active_if in dev.get_active_configuration(): + if active_if.bInterfaceNumber == interface and active_if.bAlternateSetting == alternate: + assert intf is None, "duplicate interface" + intf = active_if + return intf + +VENDOR_TEST_CTRL_IN = 1 +VENDOR_TEST_CTRL_OUT = 2 +VENDOR_TEST_CTRL_NONE = 3 +VENDOR_TEST_CTRL_IN_DELAY = 4 +VENDOR_TEST_CTRL_OUT_DELAY = 5 +VENDOR_TEST_CTRL_NONE_DELAY = 6 +VENDOR_TEST_CTRL_IN_STATUS_DELAY = 7 +VENDOR_TEST_CTRL_OUT_STATUS_DELAY = 8 +VENDOR_TEST_UNSUPPORTED_REQUEST = 32 + +class PyusbBasicTest(BaseHostTest): + """ + """ + def _callback_usb_enumeration_done(self, key, value, timestamp): + print("Received key %s = %s" % (key, value)) + self.log("Received key %s = %s" % (key, value)) + test_device(value, self.log) + passed = True + results = "pass" if passed else "fail" + self.send_kv(results, "0") + + def setup(self): + self.__result = False + self.register_callback('usb_enumeration_done', self._callback_usb_enumeration_done) + + def result(self): + return self.__result + + def teardown(self): + pass + + +class TestMatch(object): + + def __init__(self, serial): + self.serial = serial + + def __call__(self, dev): + try: + return dev.serial_number == self.serial + except ValueError: + return False + + +def test_device(serial_number, log=print): + dev = usb.core.find(custom_match=TestMatch(serial_number)) + if dev is None: + log("Device not found") + return + + ## --Control Tests-- ## + #control_basic_test(dev, log) + # Test control IN/OUT/NODATA + control_stall_test(dev, log) + # Invalid control in/out/nodata requests are stalled + # Stall during different points in the control transfer + #control_sizes_test(dev, log) + # Test control requests of various data stage sizes (1,8,16,32,64,255,256,...) + control_stress_test(dev, log) + # normal and delay mode + + ## --Endpoint test-- ## + #for each endpoint + #-test all allowed wMaxPacketSize sizes and transfer types + #-stall tests + #-set/clear stall control request + #-stall at random points of sending/receiveing data + #-test aborting an in progress transfer + #test as many endpoints at once as possible + #test biggest configuration possible + + ## --physical test-- ## + #-reset notification/handling + #-connect/disconnect tests - have device disconnect and then reconnect + #-disconnect during various phases of control transfers and endpoint transfers + #-suspend/resume tests (may not be possible to test with current framework) + #-suspend/resume notifications + + ## -- Stress tests-- ## + #-concurrent tests (all endpoints at once including control) + #-concurrent tests + reset + delay + + ## -- other tests-- ## + #-report throughput for in/out of control, bulk, interrupt and iso transfers + #-verify that construction/destruction repeatedly works gracefully + + + intf = get_interface(dev, 0, 0) + + # Find endpoints + bulk_in = None + bulk_out = None + int_in = None + int_out = None + + for endpoint in intf: + log("Processing endpoint %s" % endpoint) + ep_type = endpoint.bmAttributes & 0x3 + if ep_type == 2: + if endpoint.bEndpointAddress & 0x80: + assert bulk_in is None + bulk_in = endpoint + else: + assert bulk_out is None + bulk_out = endpoint + elif ep_type == 3: + if endpoint.bEndpointAddress & 0x80: + assert int_in is None + int_in = endpoint + else: + assert int_out is None + int_out = endpoint + assert bulk_in is not None + assert bulk_out is not None + assert int_in is not None + assert int_out is not None + bulk_out.write("hello" + "x" *256); + int_out.write("world" + "x" *256); + + dev.set_interface_altsetting(0, 1) + + intf = get_interface(dev, 0, 0) + + # Find endpoints + bulk_in = None + bulk_out = None + int_in = None + int_out = None + + for endpoint in intf: + log("Processing endpoint %s" % endpoint) + ep_type = endpoint.bmAttributes & 0x3 + if ep_type == 2: + if endpoint.bEndpointAddress & 0x80: + assert bulk_in is None + bulk_in = endpoint + else: + assert bulk_out is None + bulk_out = endpoint + elif ep_type == 3: + if endpoint.bEndpointAddress & 0x80: + assert int_in is None + int_in = endpoint + else: + assert int_out is None + int_out = endpoint + assert bulk_in is not None + assert bulk_out is not None + assert int_in is not None + assert int_out is not None + bulk_out.write("hello2" + "x" *256); + int_out.write("world2" + "x" *256); + + + t = Thread(target=write_data, args=(bulk_out,)) + t.start() + + for _ in range(10): + request_type = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_CTRL_NONE_DELAY + value = 0 # Always 0 for this request + index = 0 # Communication interface + length = 0 # No data + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + + t.join() + + return True + +def write_data(pipe): + print("Write data running") + count = 0 + for _ in range(40): + pipe.write("Value is %s" % count) + count += 1 + print("Count %s" % count) + time.sleep(0.5) + + +def control_stall_test(dev, log): + + # Control OUT stall + try: + request_type = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_UNSUPPORTED_REQUEST + value = 0 # Always 0 for this request + index = 0 # Communication interface + data = bytearray(64) # Dummy data + dev.ctrl_transfer(request_type, request, value, index, data, 5000) + raise Exception("Invalid request not stalled") + except usb.core.USBError: + log("Invalid request stalled") + + # Control request with no data stage (Device-to-host) + try: + request_type = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_UNSUPPORTED_REQUEST + value = 0 # Always 0 for this request + index = 0 # Communication interface + length = 0 + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + raise Exception("Invalid request not stalled") + except usb.core.USBError: + log("Invalid request stalled") + + # Control request with no data stage (Host-to-device) + try: + request_type = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_UNSUPPORTED_REQUEST + value = 0 # Always 0 for this request + index = 0 # Communication interface + length = 0 + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + raise Exception("Invalid request not stalled") + except usb.core.USBError: + log("Invalid request stalled") + + # Control IN stall + try: + request_type = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_UNSUPPORTED_REQUEST + value = 0 # Always 0 for this request + index = 0 # Communication interface + length = 255 + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + raise Exception("Invalid request not stalled") + except usb.core.USBError: + log("Invalid request stalled") + + for i in (6, 7, 5): + try: + request_type = build_request_type(CTRL_IN, CTRL_TYPE_STANDARD, + CTRL_RECIPIENT_DEVICE) + request = 0x6 # GET_DESCRIPTOR + value = (0x03 << 8) | (i << 0) # String descriptor index + index = 0 # Communication interface + length = 255 + resp = dev.ctrl_transfer(request_type, request, value, index, length, 5000) + log("Requesting string %s passed" % i) + except usb.core.USBError: + log("Requesting string %s failed" % i) + + +def control_stress_test(dev, log): + + # Test various patterns of control transfers + # + # Some devices have had problems with back-to-back + # control transfers. Intentionally send these sequences + # to make sure they are properly handled. + count = 0 + for _ in range(100): + # Control transfer with a data in stage + request_type = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_CTRL_IN + value = 8 # Size of data the device should actually send + index = count # Unused - set for debugging only + length = 255 + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + count += 1 + + for _ in range(100): + # Control transfer with a data out stage followed + # by a control transfer with a data in stage + request_type = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_CTRL_OUT + value = 8 # Size of data the device should actually read + index = count # Unused - set for debugging only + data = bytearray(8) # Dummy data + dev.ctrl_transfer(request_type, request, value, index, data, 5000) + count += 1 + + request_type = build_request_type(CTRL_IN, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_CTRL_IN + value = 8 # Size of data the device should actually send + index = count # Unused - set for debugging only + length = 255 + dev.ctrl_transfer(request_type, request, value, index, length, 5000) + count += 1 + + for _ in range(100): + # Control transfer with a data out stage + request_type = build_request_type(CTRL_OUT, CTRL_TYPE_VENDOR, + CTRL_RECIPIENT_DEVICE) + request = VENDOR_TEST_CTRL_OUT + value = 8 # Size of data the device should actually read + index = count # Unused - set for debugging only + data = bytearray(8) # Dummy data + dev.ctrl_transfer(request_type, request, value, index, data, 5000) + count += 1 + + +def main(): + parser = ArgumentParser(description="USB basic test") + parser.add_argument('serial', help='USB serial number of DUT') + args = parser.parse_args() + ret = test_device(args.serial) + print("Test %s" % "passed" if ret else "failed") + + +if __name__ == "__main__": + main() diff --git a/TESTS/usb_device/basic/USBTester.cpp b/TESTS/usb_device/basic/USBTester.cpp new file mode 100644 index 0000000000..1f26fce2f1 --- /dev/null +++ b/TESTS/usb_device/basic/USBTester.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdint.h" +#include "USBTester.h" +#include "mbed_shared_queues.h" +#include "EndpointResolver.h" + +#define DEFAULT_CONFIGURATION (1) + +#define VENDOR_TEST_CTRL_IN 1 +#define VENDOR_TEST_CTRL_OUT 2 +#define VENDOR_TEST_CTRL_NONE 3 +#define VENDOR_TEST_CTRL_IN_DELAY 4 +#define VENDOR_TEST_CTRL_OUT_DELAY 5 +#define VENDOR_TEST_CTRL_NONE_DELAY 6 +#define VENDOR_TEST_CTRL_IN_STATUS_DELAY 7 +#define VENDOR_TEST_CTRL_OUT_STATUS_DELAY 8 + +#define MAX_EP_SIZE 64 +#define MIN_EP_SIZE 8 + + +USBTester::USBTester(uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking): USBDevice(vendor_id, product_id, product_release) +{ + + EndpointResolver resolver(endpoint_table()); + + resolver.endpoint_ctrl(64); + bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, 64); + bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, 64); + int_in = resolver.endpoint_in(USB_EP_TYPE_INT, 64); + int_out = resolver.endpoint_out(USB_EP_TYPE_INT, 64); + MBED_ASSERT(resolver.valid()); + queue = mbed_highprio_event_queue(); + + configuration_desc(); + + init(); + USBDevice::connect(connect_blocking); + +} + +USBTester::~USBTester() +{ + deinit(); +} + +void USBTester::callback_state_change(DeviceState new_state) +{ + // Nothing to do +}; + +void USBTester::callback_request(const setup_packet_t *setup) +{ + /* Called in ISR context */ + RequestResult result = PassThrough; + uint8_t *data = NULL; + uint32_t size = 0; + uint32_t delay = 0; + + /* Process vendor-specific requests */ + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = Send; + data = ctrl_buf; + size = setup->wValue < sizeof(ctrl_buf) ? setup->wValue : sizeof(ctrl_buf); + break; + case VENDOR_TEST_CTRL_OUT: + result = Receive; + data = ctrl_buf; + size = setup->wValue < 8 ? setup->wValue : 8; + break; + case VENDOR_TEST_CTRL_NONE: + result = Success; + break; + case VENDOR_TEST_CTRL_NONE_DELAY: + result = Success; + delay = 2000; + break; + default: + result = PassThrough; + break; + } + } + + if (delay) { + queue->call_in(delay, static_cast(this), &USBTester::complete_request, Success, data, size); + } else { + complete_request(result, data, size); + } + + +} + +void USBTester::callback_request_xfer_done(const setup_packet_t *setup, bool aborted) +{ + if (aborted) { + complete_request_xfer_done(false); + return; + } + + bool result = false; + if (setup->bmRequestType.Type == VENDOR_TYPE) { + switch (setup->bRequest) { + case VENDOR_TEST_CTRL_IN: + result = true; + break; + case VENDOR_TEST_CTRL_OUT: + result = true; + break; + default: + result = false; + break; + } + } + complete_request_xfer_done(true); +} + +// Called in ISR context +// Set configuration. Return false if the +// configuration is not supported. +void USBTester::callback_set_configuration(uint8_t configuration) +{ + if (configuration != DEFAULT_CONFIGURATION) { + complete_set_configuration(false); + return; + } + + // Configure endpoints > 0 + endpoint_add(int_in, MAX_EP_SIZE, USB_EP_TYPE_INT); + endpoint_add(int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, &USBTester::epint_out_callback); + endpoint_add(bulk_in, MAX_EP_SIZE, USB_EP_TYPE_BULK); + endpoint_add(bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, &USBTester::epbulk_out_callback); + + read_start(int_out); + read_start(bulk_out); + + complete_set_configuration(true); +} + +void USBTester::callback_set_interface(uint16_t interface, uint8_t alternate) +{ + if (interface == 0 && alternate == 0) { + endpoint_remove(int_in); + endpoint_remove(int_out); + endpoint_remove(bulk_in); + endpoint_remove(bulk_out); + + endpoint_add(int_in, MAX_EP_SIZE, USB_EP_TYPE_INT); + endpoint_add(int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, &USBTester::epint_out_callback); + endpoint_add(bulk_in, MAX_EP_SIZE, USB_EP_TYPE_BULK); + endpoint_add(bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, &USBTester::epbulk_out_callback); + + read_start(int_out); + read_start(bulk_out); + + complete_set_interface(true); + return; + } + if (interface == 0 && alternate == 1) { + endpoint_remove(int_in); + endpoint_remove(int_out); + endpoint_remove(bulk_in); + endpoint_remove(bulk_out); + + endpoint_add(int_in, MIN_EP_SIZE, USB_EP_TYPE_INT); + endpoint_add(int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, &USBTester::epint_out_callback); + endpoint_add(bulk_in, MIN_EP_SIZE, USB_EP_TYPE_BULK); + endpoint_add(bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, &USBTester::epbulk_out_callback); + + read_start(int_out); + read_start(bulk_out); + + complete_set_interface(true); + return; + } + complete_set_interface(false); +} + +const uint8_t *USBTester::device_desc() +{ + uint8_t ep0_size = endpoint_max_packet_size(0x00); + uint8_t device_descriptor_temp[] = { + 18, // bLength + 1, // bDescriptorType + 0x10, 0x01, // bcdUSB + 0, // bDeviceClass + 0, // bDeviceSubClass + 0, // bDeviceProtocol + ep0_size, // bMaxPacketSize0 + (uint8_t)(LSB(vendor_id)), (uint8_t)(MSB(vendor_id)), // idVendor + (uint8_t)(LSB(product_id)), (uint8_t)(MSB(product_id)),// idProduct + 0x00, 0x01, // bcdDevice + 1, // iManufacturer + 2, // iProduct + 3, // iSerialNumber + 1 // bNumConfigurations + }; + MBED_ASSERT(sizeof(device_descriptor_temp) == sizeof(device_descriptor)); + memcpy(device_descriptor, device_descriptor_temp, sizeof(device_descriptor)); + return device_descriptor; +} + +const uint8_t *USBTester::string_iinterface_desc() +{ + static const uint8_t string_iinterface_descriptor[] = { + 0x08, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, + }; + return string_iinterface_descriptor; +} + +const uint8_t *USBTester::string_iproduct_desc() +{ + static const uint8_t string_iproduct_descriptor[] = { + 0x16, + STRING_DESCRIPTOR, + 'C', 0, 'D', 0, 'C', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 + }; + return string_iproduct_descriptor; +} + + +#define CONFIG1_DESC_SIZE (9+9+7+7+7+7 + 9+7+7+7+7) + +const uint8_t *USBTester::configuration_desc() +{ + static const uint8_t config_descriptor[] = { + // configuration descriptor + 9, // bLength + 2, // bDescriptorType + LSB(CONFIG1_DESC_SIZE), // wTotalLength + MSB(CONFIG1_DESC_SIZE), + 1, // bNumInterfaces + 1, // bConfigurationValue + 0, // iConfiguration + 0x80, // bmAttributes + 50, // bMaxPower + + // Interface 0 setting 0 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber + 0, // bAlternateSetting + 4, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE),// wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE),// wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MAX_EP_SIZE),// wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE),// wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MAX_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MAX_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // Interface 0 setting 1 + + // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber + 1, // bAlternateSetting + 4, // bNumEndpoints + 0xFF, // bInterfaceClass + 0xFF, // bInterfaceSubClass + 0xFF, // bInterfaceProtocol + 0, // iInterface + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_in, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + bulk_out, // bEndpointAddress + E_BULK, // bmAttributes (0x02=bulk) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 0, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_in, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1, // bInterval + + // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13 + ENDPOINT_DESCRIPTOR_LENGTH, // bLength + ENDPOINT_DESCRIPTOR, // bDescriptorType + int_out, // bEndpointAddress + E_INTERRUPT, // bmAttributes (0x03=interrupt) + LSB(MIN_EP_SIZE), // wMaxPacketSize (LSB) + MSB(MIN_EP_SIZE), // wMaxPacketSize (MSB) + 1 // bInterval + + }; + return config_descriptor; +} + + +void USBTester::epint_out_callback(usb_ep_t endpoint) +{ + uint8_t buffer[65]; + uint32_t size = 0; + + if (!read_finish(endpoint, buffer, sizeof(buffer), &size)) { + return; + } + if (!read_start(endpoint)) { + return; + } +} +void USBTester::epbulk_out_callback(usb_ep_t endpoint) +{ + uint8_t buffer[65]; + uint32_t size = 0; + + if (!read_finish(endpoint, buffer, sizeof(buffer), &size)) { + return; + } + if (!read_start(endpoint)) { + return; + } +} + diff --git a/TESTS/usb_device/basic/USBTester.h b/TESTS/usb_device/basic/USBTester.h new file mode 100644 index 0000000000..0e527af98a --- /dev/null +++ b/TESTS/usb_device/basic/USBTester.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef USB_TESTER_H +#define USB_TESTER_H + +/* These headers are included for child class. */ +#include "USBDescriptor.h" +#include "USBDevice_Types.h" +#include "EventQueue.h" + +#include "USBDevice.h" + +class USBTester: public USBDevice { +public: + + /* + * Constructor + * + * @param vendor_id Your vendor_id + * @param product_id Your product_id + * @param product_release Your preoduct_release + * @param connect_blocking define if the connection must be blocked if USB not plugged in + */ + USBTester(uint16_t vendor_id, uint16_t product_id, uint16_t product_release, bool connect_blocking); + + ~USBTester(); + +protected: + + /* + * Get device descriptor. Warning: this method has to store the length of the report descriptor in reportLength. + * + * @returns pointer to the device descriptor + */ + virtual const uint8_t *device_desc(); + + /* + * Get string product descriptor + * + * @returns pointer to the string product descriptor + */ + virtual const uint8_t *string_iproduct_desc(); + + /* + * Get string interface descriptor + * + * @returns pointer to the string interface descriptor + */ + virtual const uint8_t *string_iinterface_desc(); + + /* + * Get configuration descriptor + * + * @returns pointer to the configuration descriptor + */ + virtual const uint8_t *configuration_desc(); + +protected: + uint8_t bulk_in; + uint8_t bulk_out; + uint8_t int_in; + uint8_t int_out; + EventQueue *queue; + + virtual void callback_state_change(DeviceState new_state); + virtual void callback_request(const setup_packet_t *setup); + virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted); + virtual void callback_set_configuration(uint8_t configuration); + virtual void callback_set_interface(uint16_t interface, uint8_t alternate); + virtual void epbulk_out_callback(usb_ep_t endpoint); + virtual void epint_out_callback(usb_ep_t endpoint); + uint8_t ctrl_buf[2048]; + +}; + +#endif diff --git a/TESTS/usb_device/basic/main.cpp b/TESTS/usb_device/basic/main.cpp new file mode 100644 index 0000000000..141bb473e2 --- /dev/null +++ b/TESTS/usb_device/basic/main.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018-2018, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity/unity.h" +#include "utest/utest.h" + +#include "USBTester.h" + +#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE +#error [NOT_SUPPORTED] USB Device not supported for this target +#endif + +using namespace utest::v1; + +// Echo server (echo payload to host) +void test_case_basic() +{ + char _key[11] = {}; + char _value[128] = {}; + + { + USBTester serial(0x0d28, 0x0205, 0x0001, true); + + greentea_send_kv("usb_enumeration_done", "0123456789"); + // Wait for host before terminating + greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value)); + } +} + +Case cases[] = { + Case("pyusb basic test", test_case_basic), +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(120, "pyusb_basic"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +}