mbed-os/TESTS/usb_device/basic/USBTester.cpp

456 lines
15 KiB
C++
Raw Normal View History

/*
* 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
2018-03-13 15:18:20 +00:00
#define VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#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), reset_count(0), suspend_count(0),
resume_count(0), interface_set(NONE), configuration_set(NONE)
{
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(0);
init();
USBDevice::connect(connect_blocking);
}
USBTester::~USBTester()
{
deinit();
}
2018-03-13 15:18:20 +00:00
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;
}
}
void USBTester::remove_endpoints()
{
if(configuration_set == 1) {
endpoint_remove(int_in);
endpoint_remove(int_out);
endpoint_remove(bulk_in);
endpoint_remove(bulk_out);
}
}
2018-03-13 15:18:20 +00:00
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) {
configuration_set = NONE;
interface_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;
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;
2018-03-13 15:18:20 +00:00
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;
}
}
if (delay) {
queue->call_in(delay, static_cast<USBDevice *>(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;
2018-03-13 15:18:20 +00:00
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(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(set_configuration(configuration));
} else {
complete_set_configuration(false);
}
}
bool USBTester::set_configuration(uint16_t configuration)
{
if(set_interface(configuration, 0, 0)) {
configuration_set = configuration;
return true;
}
return false;
}
void USBTester::callback_set_interface(uint16_t interface, uint8_t alternate)
{
bool success = set_interface(configuration_set, interface, alternate);
complete_set_interface(success);
}
bool USBTester::set_interface(uint16_t configuration, uint16_t interface, uint16_t alternate)
{
bool success = false;
if (configuration == 1) {
if (interface == 0 && alternate == 0) {
remove_endpoints();
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);
read_start(int_out, int_buf, sizeof(int_buf));
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(bulk_out, bulk_buf, sizeof(bulk_buf));
interface_set = interface;
success = true;
}
if (interface == 0 && alternate == 1) {
remove_endpoints();
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);
read_start(int_out, int_buf, sizeof(int_buf));
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(bulk_out, bulk_buf, sizeof(bulk_buf));
interface_set = interface;
success = true;
}
}
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
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[] = {
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 CONFIG1_DESC_SIZE (9+9+7+7+7+7 + 9+7+7+7+7)
const uint8_t *USBTester::configuration_desc(uint8_t index)
{
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
};
if (index == 0) {
return config_descriptor;
} else {
return NULL;
}
}
void USBTester::epint_out_callback(usb_ep_t endpoint)
{
read_finish(endpoint);
read_start(endpoint, int_buf, sizeof(int_buf));
}
void USBTester::epbulk_out_callback(usb_ep_t endpoint)
{
read_finish(endpoint);
read_start(endpoint, bulk_buf, sizeof(bulk_buf));
}