mirror of https://github.com/ARMmbed/mbed-os.git
Add USBDevice test code
Add a USB test and the class USBTester.cpp to go along with it.pull/9768/head
parent
caace4ac61
commit
f9f12766d8
|
@ -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()
|
|
@ -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<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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue