/* * Copyright (c) 2018-2020, 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. */ #if DEVICE_USBDEVICE #include "stdint.h" #include "USBTester.h" #include "events/mbed_shared_queues.h" #include "EndpointResolver.h" #define DEFAULT_CONFIGURATION (1) #define LAST_CONFIGURATION (2) #define VENDOR_TEST_CTRL_IN 1 #define VENDOR_TEST_CTRL_OUT 2 #define VENDOR_TEST_CTRL_IN_SIZES 3 #define VENDOR_TEST_CTRL_OUT_SIZES 4 #define MAX_EP_SIZE 64 #define MIN_EP_SIZE 8 #define CTRL_BUF_SIZE (2048) #define EVENT_READY (1 << 0) USBTester::USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release): USBDevice(phy, vendor_id, product_id, product_release), interface_0_alt_set(NONE), interface_1_alt_set(NONE), configuration_set(NONE), reset_count(0), suspend_count(0), resume_count(0) { 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()); configuration_desc(0); ctrl_buf = new uint8_t[CTRL_BUF_SIZE]; init(); USBDevice::connect(); flags.wait_any(EVENT_READY, osWaitForever, false); } USBTester::~USBTester() { deinit(); delete[] ctrl_buf; } const char *USBTester::get_desc_string(const uint8_t *desc) { static char ret_string[128] = { }; const uint8_t desc_size = desc[0] - 2; const uint8_t *desc_str = &desc[2]; uint32_t j = 0; for (uint32_t i = 0; i < desc_size; i += 2, j++) { ret_string[j] = desc_str[i]; } ret_string[j] = '\0'; return ret_string; } void USBTester::suspend(bool suspended) { if (suspended) { ++suspend_count; } else { ++resume_count; } } const char *USBTester::get_serial_desc_string() { return get_desc_string(string_iserial_desc()); } const char *USBTester::get_iinterface_desc_string() { return get_desc_string(string_iserial_desc()); } const char *USBTester::get_iproduct_desc_string() { return get_desc_string(string_iserial_desc()); } void USBTester::callback_state_change(DeviceState new_state) { if (new_state == Configured) { flags.set(EVENT_READY); } else { flags.clear(EVENT_READY); configuration_set = NONE; interface_0_alt_set = NONE; interface_1_alt_set = NONE; } } void USBTester::callback_reset() { ++reset_count; } void USBTester::callback_request(const setup_packet_t *setup) { /* Called in ISR context */ RequestResult result = PassThrough; uint8_t *data = NULL; uint32_t size = 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 < CTRL_BUF_SIZE ? setup->wValue : CTRL_BUF_SIZE; break; case VENDOR_TEST_CTRL_OUT: result = Receive; data = ctrl_buf; size = setup->wValue < 8 ? setup->wValue : 8; break; case VENDOR_TEST_CTRL_IN_SIZES: result = Send; data = ctrl_buf; size = setup->wLength; break; case VENDOR_TEST_CTRL_OUT_SIZES: result = Receive; data = ctrl_buf; size = setup->wValue; break; default: result = PassThrough; break; } } 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; case VENDOR_TEST_CTRL_OUT_SIZES: result = true; break; case VENDOR_TEST_CTRL_IN_SIZES: result = true; break; default: result = false; break; } } complete_request_xfer_done(result); } // Called in ISR context // Set configuration. Return false if the // configuration is not supported. void USBTester::callback_set_configuration(uint8_t configuration) { bool ret = false; if (configuration >= DEFAULT_CONFIGURATION && configuration <= LAST_CONFIGURATION) { endpoint_remove_all(); ret = set_configuration(configuration); } complete_set_configuration(ret); } bool USBTester::setup_iterface(uint8_t ep_in, uint8_t ep_out, uint32_t ep_size, usb_ep_type_t ep_type, uint8_t *buf, uint32_t buf_size, void (USBTester::*callback)()) { bool success = false; success = endpoint_add(ep_in, ep_size, ep_type); success &= endpoint_add(ep_out, ep_size, ep_type, callback); success &= read_start(ep_out, buf, buf_size); return success; } void USBTester::remove_iterface(uint16_t interface) { if (configuration_set == 1) { if (interface == 0) { endpoint_remove(bulk_in); endpoint_remove(bulk_out); interface_0_alt_set = NONE; } if (interface == 1) { endpoint_remove(int_in); endpoint_remove(int_out); interface_1_alt_set = NONE; } } if (configuration_set == 2) { if (interface == 0) { endpoint_remove(int_in); endpoint_remove(int_out); interface_0_alt_set = NONE; } if (interface == 1) { endpoint_remove(bulk_in); endpoint_remove(bulk_out); interface_1_alt_set = NONE; } } } bool USBTester::set_configuration(uint16_t configuration) { bool success = false; // set 0 alt setting for each interface if (configuration == 1) { // interface 0 alternate 0 success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); // interface 1 alternate 0 success &= setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); } else if (configuration == 2) { // interface 0 alternate 0 success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); // interface 1 alternate 0 success &= setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); } if (success) { configuration_set = configuration; interface_0_alt_set = interface_1_alt_set = 0; } return success; } void USBTester::callback_set_interface(uint16_t interface, uint8_t alternate) { complete_set_interface(set_interface(interface, alternate)); } bool USBTester::set_interface(uint16_t interface, uint16_t alternate) { bool success = false; if (interface == 0) { if (configuration_set == 1) { if (alternate == 0) { remove_iterface(interface); success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); } if (alternate == 1) { remove_iterface(interface); success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); } } if (configuration_set == 2) { if (alternate == 0) { remove_iterface(interface); success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); } if (alternate == 1) { remove_iterface(interface); success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); } } if (success) { interface_0_alt_set = alternate; } } if (interface == 1) { if (configuration_set == 1) { if (alternate == 0) { remove_iterface(interface); success = setup_iterface(int_in, int_out, MAX_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); } if (alternate == 1) { remove_iterface(interface); success = setup_iterface(int_in, int_out, MIN_EP_SIZE, USB_EP_TYPE_INT, int_buf, sizeof(int_buf), &USBTester::epint_out_callback); } } if (configuration_set == 2) { if (alternate == 0) { remove_iterface(interface); success = setup_iterface(bulk_in, bulk_out, MIN_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); } if (alternate == 1) { remove_iterface(interface); success = setup_iterface(bulk_in, bulk_out, MAX_EP_SIZE, USB_EP_TYPE_BULK, bulk_buf, sizeof(bulk_buf), &USBTester::epbulk_out_callback); } } if (success) { interface_1_alt_set = alternate; } } return success; } 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 2 // 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[] = { 0x22, STRING_DESCRIPTOR, 'M', 0, 'B', 0, 'E', 0, 'D', 0, ' ', 0, 'T', 0, 'E', 0, 'S', 0, 'T', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0, }; return string_iproduct_descriptor; } #define CONFIG_1_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) #define CONFIG_2_DESC_SIZE (9+9+7+7 + 9+7+7 + 9+7+7 + 9+7+7) const uint8_t *USBTester::configuration_desc(uint8_t index) { static const uint8_t config_1_descriptor[] = { // configuration descriptor CONFIGURATION_DESCRIPTOR_LENGTH,// bLength CONFIGURATION_DESCRIPTOR, // bDescriptorType LSB(CONFIG_1_DESC_SIZE), // wTotalLength MSB(CONFIG_1_DESC_SIZE), 2, // 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 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 0, // bInterfaceNumber 0, // bAlternateSetting 2, // 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) 1, // 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) 1, // bInterval // Interface 0 setting 1 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 0, // bInterfaceNumber 1, // bAlternateSetting 2, // 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) 1, // 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) 1, // bInterval // Interface 1 setting 0 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 1, // bInterfaceNumber 0, // bAlternateSetting 2, // 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 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 1 setting 1 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 1, // bInterfaceNumber 1, // bAlternateSetting 2, // 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 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 }; static const uint8_t config_2_descriptor[] = { // configuration descriptor CONFIGURATION_DESCRIPTOR_LENGTH,// bLength CONFIGURATION_DESCRIPTOR, // bDescriptorType LSB(CONFIG_2_DESC_SIZE), // wTotalLength MSB(CONFIG_2_DESC_SIZE), 2, // bNumInterfaces 2, // bConfigurationValue 0, // iConfiguration 0x80, // bmAttributes 50, // bMaxPower // Interface 0 setting 0 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 0, // bInterfaceNumber 0, // bAlternateSetting 2, // 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 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 // Interface 0 setting 1 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 0, // bInterfaceNumber 1, // bAlternateSetting 2, // 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 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 1 setting 0 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 1, // bInterfaceNumber 0, // bAlternateSetting 2, // 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) 1, // 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) 1, // bInterval // Interface 1 setting 1 // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12 INTERFACE_DESCRIPTOR_LENGTH,// bLength INTERFACE_DESCRIPTOR, // bDescriptorType 1, // bInterfaceNumber 1, // bAlternateSetting 2, // 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) 1, // 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) 1 // bInterval }; if (index == 0) { return config_1_descriptor; } else if (index == 1) { return config_2_descriptor; } else { return NULL; } } void USBTester::epint_out_callback() { read_finish(int_out); read_start(int_out, int_buf, sizeof(int_buf)); } void USBTester::epbulk_out_callback() { read_finish(bulk_out); read_start(bulk_out, bulk_buf, sizeof(bulk_buf)); } #endif //USB_DEVICE_TESTS