mirror of https://github.com/ARMmbed/mbed-os.git
commit
6b9d1573fb
|
@ -50,3 +50,4 @@ The Python modules used by Mbed tools are used under the following licenses:
|
|||
- [pycryptodome](https://pypi.org/project/pycryptodome) - BSD-2-Clause
|
||||
- [pyusb](https://pypi.org/project/pyusb/) - Apache-2.0
|
||||
- [cmsis-pack-manager](https://pypi.org/project/cmsis-pack-manager) - Apache-2.0
|
||||
- [hidapi](https://pypi.org/project/hidapi/) - BSD-style
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
"""
|
||||
mbed SDK
|
||||
Copyright (c) 2019 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
|
||||
import functools
|
||||
import time
|
||||
import threading
|
||||
import uuid
|
||||
import mbed_host_tests
|
||||
import usb.core
|
||||
from usb.util import (
|
||||
CTRL_IN,
|
||||
CTRL_OUT,
|
||||
CTRL_TYPE_STANDARD,
|
||||
CTRL_TYPE_CLASS,
|
||||
CTRL_RECIPIENT_DEVICE,
|
||||
CTRL_RECIPIENT_INTERFACE,
|
||||
DESC_TYPE_CONFIG,
|
||||
build_request_type)
|
||||
|
||||
try:
|
||||
import hid
|
||||
except ImportError:
|
||||
CYTHON_HIDAPI_PRESENT = False
|
||||
else:
|
||||
CYTHON_HIDAPI_PRESENT = True
|
||||
|
||||
# USB device -- device classes
|
||||
USB_CLASS_HID = 0x03
|
||||
|
||||
# USB device -- standard requests
|
||||
USB_REQUEST_GET_DESCRIPTOR = 0x06
|
||||
|
||||
# USB device -- HID class requests
|
||||
HID_REQUEST_GET_REPORT = 0x01
|
||||
HID_REQUEST_SET_REPORT = 0x09
|
||||
HID_REQUEST_GET_IDLE = 0x02
|
||||
HID_REQUEST_SET_IDLE = 0x0A
|
||||
HID_REQUEST_GET_PROTOCOL = 0x03
|
||||
HID_REQUEST_SET_PROTOCOL = 0x0B
|
||||
|
||||
# USB device -- HID class descriptors
|
||||
DESC_TYPE_HID_HID = 0x21
|
||||
DESC_TYPE_HID_REPORT = 0x22
|
||||
DESC_TYPE_HID_PHYSICAL = 0x23
|
||||
|
||||
# USB device -- HID class descriptor lengths
|
||||
DESC_LEN_HID_HID = 0x09
|
||||
|
||||
# USB device -- descriptor fields offsets
|
||||
DESC_OFFSET_BLENGTH = 0
|
||||
DESC_OFFSET_BDESCRIPTORTYPE = 1
|
||||
|
||||
# USB device -- HID subclasses
|
||||
HID_SUBCLASS_NONE = 0
|
||||
HID_SUBCLASS_BOOT = 1
|
||||
|
||||
# USB device -- HID protocols
|
||||
HID_PROTOCOL_NONE = 0
|
||||
HID_PROTOCOL_KEYBOARD = 1
|
||||
HID_PROTOCOL_MOUSE = 2
|
||||
|
||||
# Greentea message keys used for callbacks
|
||||
MSG_KEY_DEVICE_READY = 'dev_ready'
|
||||
MSG_KEY_HOST_READY = 'host_ready'
|
||||
MSG_KEY_SERIAL_NUMBER = 'usb_dev_sn'
|
||||
MSG_KEY_TEST_GET_DESCRIPTOR_HID = 'test_get_desc_hid'
|
||||
MSG_KEY_TEST_GET_DESCRIPTOR_CFG = 'test_get_desc_cfg'
|
||||
MSG_KEY_TEST_REQUESTS = 'test_requests'
|
||||
MSG_KEY_TEST_RAW_IO = 'test_raw_io'
|
||||
|
||||
# Greentea message keys used to notify DUT of test status
|
||||
MSG_KEY_TEST_CASE_FAILED = 'fail'
|
||||
MSG_KEY_TEST_CASE_PASSED = 'pass'
|
||||
MSG_VALUE_DUMMY = '0'
|
||||
MSG_VALUE_NOT_SUPPORTED = 'not_supported'
|
||||
|
||||
# Constants for the tests.
|
||||
KEYBOARD_IDLE_RATE_TO_SET = 0x00 # Duration = 0 (indefinite)
|
||||
HID_PROTOCOL_TO_SET = 0x01 # Protocol = 1 (Report Protocol)
|
||||
RAW_IO_REPS = 16 # Number of loopback test reps.
|
||||
|
||||
|
||||
def build_get_desc_value(desc_type, desc_index):
|
||||
"""Build and return a wValue field for control requests."""
|
||||
return (desc_type << 8) | desc_index
|
||||
|
||||
|
||||
def usb_hid_path(serial_number):
|
||||
"""Get a USB HID device system path based on the serial number."""
|
||||
if not CYTHON_HIDAPI_PRESENT:
|
||||
return None
|
||||
for device_info in hid.enumerate(): # pylint: disable=no-member
|
||||
if device_info.get('serial_number') == serial_number: # pylint: disable=not-callable
|
||||
return device_info['path']
|
||||
return None
|
||||
|
||||
|
||||
def get_descriptor_types(desc):
|
||||
"""Return a list of all bDescriptorType values found in desc.
|
||||
|
||||
desc is expected to be a sequence of bytes, i.e. array.array('B')
|
||||
returned from usb.core.
|
||||
|
||||
From the USB 2.0 spec, paragraph 9.5:
|
||||
Each descriptor begins with a byte-wide field that contains the total
|
||||
number of bytes in the descriptor followed by a byte-wide field that
|
||||
identifies the descriptor type.
|
||||
"""
|
||||
tmp_desc = desc[DESC_OFFSET_BLENGTH:]
|
||||
desc_types = []
|
||||
while True:
|
||||
try:
|
||||
bLength = tmp_desc[DESC_OFFSET_BLENGTH] # pylint: disable=invalid-name
|
||||
bDescriptorType = tmp_desc[DESC_OFFSET_BDESCRIPTORTYPE] # pylint: disable=invalid-name
|
||||
desc_types.append(int(bDescriptorType))
|
||||
tmp_desc = tmp_desc[int(bLength):]
|
||||
except IndexError:
|
||||
break
|
||||
return desc_types
|
||||
|
||||
|
||||
def get_hid_descriptor_parts(hid_descriptor):
|
||||
"""Return bNumDescriptors, bDescriptorType, wDescriptorLength from hid_descriptor."""
|
||||
err_msg = 'Invalid HID class descriptor'
|
||||
try:
|
||||
if hid_descriptor[1] != DESC_TYPE_HID_HID:
|
||||
raise TypeError(err_msg)
|
||||
bNumDescriptors = int(hid_descriptor[5]) # pylint: disable=invalid-name
|
||||
bDescriptorType = int(hid_descriptor[6]) # pylint: disable=invalid-name
|
||||
wDescriptorLength = int((hid_descriptor[8] << 8) | hid_descriptor[7]) # pylint: disable=invalid-name
|
||||
except (IndexError, ValueError):
|
||||
raise TypeError(err_msg)
|
||||
return bNumDescriptors, bDescriptorType, wDescriptorLength
|
||||
|
||||
|
||||
def get_usbhid_dev_type(intf):
|
||||
"""Return a name of the HID device class type for intf."""
|
||||
if not isinstance(intf, usb.core.Interface):
|
||||
return None
|
||||
if intf.bInterfaceClass != USB_CLASS_HID:
|
||||
# USB Device Class Definition for HID, v1.11, paragraphs 4.1, 4.2 & 4.3:
|
||||
# the class is specified in the Interface descriptor
|
||||
# and not the Device descriptor.
|
||||
return None
|
||||
if (intf.bInterfaceSubClass == HID_SUBCLASS_BOOT
|
||||
and intf.bInterfaceProtocol == HID_PROTOCOL_KEYBOARD):
|
||||
return 'boot_keyboard'
|
||||
if (intf.bInterfaceSubClass == HID_SUBCLASS_BOOT
|
||||
and intf.bInterfaceProtocol == HID_PROTOCOL_MOUSE):
|
||||
return 'boot_mouse'
|
||||
# Determining any other HID dev type, like a non-boot_keyboard or
|
||||
# a non-boot_mouse requires getting and parsing a HID Report descriptor
|
||||
# for intf.
|
||||
# Only the boot_keyboard, boot_mouse and other_device are used for this
|
||||
# greentea test suite.
|
||||
return 'other_device'
|
||||
|
||||
|
||||
class RetryError(Exception):
|
||||
"""Exception raised by retry_fun_call()."""
|
||||
|
||||
|
||||
def retry_fun_call(fun, num_retries=3, retry_delay=0.0):
|
||||
"""Call fun and retry if any exception was raised.
|
||||
|
||||
fun is called at most num_retries with a retry_dalay in between calls.
|
||||
Raises RetryError if the retry limit is exhausted.
|
||||
"""
|
||||
verbose = False
|
||||
final_err = None
|
||||
for retry in range(1, num_retries + 1):
|
||||
try:
|
||||
return fun() # pylint: disable=not-callable
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
final_err = exc
|
||||
if verbose:
|
||||
print('Retry {}/{} failed ({})'
|
||||
.format(retry, num_retries, str(fun)))
|
||||
time.sleep(retry_delay)
|
||||
err_msg = 'Failed with "{}". Tried {} times.'
|
||||
raise RetryError(err_msg.format(final_err, num_retries))
|
||||
|
||||
|
||||
def raise_if_different(expected, actual, text=''):
|
||||
"""Raise a RuntimeError if actual is different than expected."""
|
||||
if expected != actual:
|
||||
raise RuntimeError('{}Got {!r}, expected {!r}.'.format(text, actual, expected))
|
||||
|
||||
|
||||
def raise_if_false(expression, text):
|
||||
"""Raise a RuntimeError if expression is False."""
|
||||
if not expression:
|
||||
raise RuntimeError(text)
|
||||
|
||||
|
||||
class USBHIDTest(mbed_host_tests.BaseHostTest):
|
||||
"""Host side test for USB device HID class."""
|
||||
|
||||
@staticmethod
|
||||
def get_usb_hid_path(usb_id_str):
|
||||
"""Get a USB HID device path as registered in the system.
|
||||
|
||||
Search is based on the unique USB SN generated by the host
|
||||
during test suite setup.
|
||||
Raises RuntimeError if the device is not found.
|
||||
"""
|
||||
hid_path = usb_hid_path(usb_id_str)
|
||||
if hid_path is None:
|
||||
err_msg = 'USB HID device (SN={}) not found.'
|
||||
raise RuntimeError(err_msg.format(usb_id_str))
|
||||
return hid_path
|
||||
|
||||
@staticmethod
|
||||
def get_usb_dev(usb_id_str):
|
||||
"""Get a usb.core.Device instance.
|
||||
|
||||
Search is based on the unique USB SN generated by the host
|
||||
during test suite setup.
|
||||
Raises RuntimeError if the device is not found.
|
||||
"""
|
||||
usb_dev = usb.core.find(custom_match=lambda d: d.serial_number == usb_id_str)
|
||||
if usb_dev is None:
|
||||
err_msg = 'USB device (SN={}) not found.'
|
||||
raise RuntimeError(err_msg.format(usb_id_str))
|
||||
return usb_dev
|
||||
|
||||
def __init__(self):
|
||||
super(USBHIDTest, self).__init__()
|
||||
self.__bg_task = None
|
||||
self.dut_usb_dev_sn = uuid.uuid4().hex # 32 hex digit string
|
||||
|
||||
def notify_error(self, msg):
|
||||
"""Terminate the test with an error msg."""
|
||||
self.log('TEST ERROR: {}'.format(msg))
|
||||
self.notify_complete(None)
|
||||
|
||||
def notify_failure(self, msg):
|
||||
"""Report a host side test failure to the DUT."""
|
||||
self.log('TEST FAILED: {}'.format(msg))
|
||||
self.send_kv(MSG_KEY_TEST_CASE_FAILED, MSG_VALUE_DUMMY)
|
||||
|
||||
def notify_success(self, value=None, msg=''):
|
||||
"""Report a host side test success to the DUT."""
|
||||
if msg:
|
||||
self.log('TEST PASSED: {}'.format(msg))
|
||||
if value is None:
|
||||
value = MSG_VALUE_DUMMY
|
||||
self.send_kv(MSG_KEY_TEST_CASE_PASSED, value)
|
||||
|
||||
def cb_test_get_hid_desc(self, key, value, timestamp):
|
||||
"""Verify the device handles Get_Descriptor request correctly.
|
||||
|
||||
Two requests are tested for every HID interface:
|
||||
1. Get_Descriptor(HID),
|
||||
2. Get_Descriptor(Report).
|
||||
Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
|
||||
"""
|
||||
kwargs_hid_desc_req = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
|
||||
# Descriptor Index (part of wValue) is reset to zero for
|
||||
# HID class descriptors other than Physical ones.
|
||||
'wValue': build_get_desc_value(DESC_TYPE_HID_HID, 0x00),
|
||||
# wIndex is replaced with the Interface Number in the loop.
|
||||
'wIndex': None,
|
||||
'data_or_wLength': DESC_LEN_HID_HID}
|
||||
kwargs_report_desc_req = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
|
||||
# Descriptor Index (part of wValue) is reset to zero for
|
||||
# HID class descriptors other than Physical ones.
|
||||
'wValue': build_get_desc_value(DESC_TYPE_HID_REPORT, 0x00),
|
||||
# wIndex is replaced with the Interface Number in the loop.
|
||||
'wIndex': None,
|
||||
# wLength is replaced with the Report Descriptor Length in the loop.
|
||||
'data_or_wLength': None}
|
||||
mbed_hid_dev = None
|
||||
report_desc_lengths = []
|
||||
try:
|
||||
mbed_hid_dev = retry_fun_call(
|
||||
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
|
||||
num_retries=20,
|
||||
retry_delay=0.05)
|
||||
except RetryError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
try:
|
||||
for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable
|
||||
if intf.bInterfaceClass != USB_CLASS_HID:
|
||||
continue
|
||||
try:
|
||||
if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber):
|
||||
mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable
|
||||
except (NotImplementedError, AttributeError):
|
||||
pass
|
||||
|
||||
# Request the HID descriptor.
|
||||
kwargs_hid_desc_req['wIndex'] = intf.bInterfaceNumber
|
||||
hid_desc = mbed_hid_dev.ctrl_transfer(**kwargs_hid_desc_req) # pylint: disable=not-callable
|
||||
try:
|
||||
bNumDescriptors, bDescriptorType, wDescriptorLength = get_hid_descriptor_parts(hid_desc) # pylint: disable=invalid-name
|
||||
except TypeError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
raise_if_different(1, bNumDescriptors, 'Exactly one HID Report descriptor expected. ')
|
||||
raise_if_different(DESC_TYPE_HID_REPORT, bDescriptorType, 'Invalid HID class descriptor type. ')
|
||||
raise_if_false(wDescriptorLength > 0, 'Invalid HID Report descriptor length. ')
|
||||
|
||||
# Request the Report descriptor.
|
||||
kwargs_report_desc_req['wIndex'] = intf.bInterfaceNumber
|
||||
kwargs_report_desc_req['data_or_wLength'] = wDescriptorLength
|
||||
report_desc = mbed_hid_dev.ctrl_transfer(**kwargs_report_desc_req) # pylint: disable=not-callable
|
||||
raise_if_different(wDescriptorLength, len(report_desc),
|
||||
'The size of data received does not match the HID Report descriptor length. ')
|
||||
report_desc_lengths.append(len(report_desc))
|
||||
except usb.core.USBError as exc:
|
||||
self.notify_failure('Get_Descriptor request failed. {}'.format(exc))
|
||||
except RuntimeError as exc:
|
||||
self.notify_failure(exc)
|
||||
else:
|
||||
# Send the report desc len to the device.
|
||||
# USBHID::report_desc_length() returns uint16_t
|
||||
msg_value = '{0:04x}'.format(max(report_desc_lengths))
|
||||
self.notify_success(msg_value)
|
||||
|
||||
def cb_test_get_cfg_desc(self, key, value, timestamp):
|
||||
"""Verify the device provides required HID descriptors.
|
||||
|
||||
USB Device Class Definition for HID, v1.11, paragraph 7.1:
|
||||
When a Get_Descriptor(Configuration) request is issued, it
|
||||
returns (...), and the HID descriptor for each interface.
|
||||
"""
|
||||
kwargs_cfg_desc_req = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_STANDARD, CTRL_RECIPIENT_DEVICE),
|
||||
'bRequest': USB_REQUEST_GET_DESCRIPTOR,
|
||||
# Descriptor Index (part of wValue) is reset to zero.
|
||||
'wValue': build_get_desc_value(DESC_TYPE_CONFIG, 0x00),
|
||||
# wIndex is reset to zero.
|
||||
'wIndex': 0x00,
|
||||
# wLength unknown, set to 1024.
|
||||
'data_or_wLength': 1024}
|
||||
mbed_hid_dev = None
|
||||
try:
|
||||
mbed_hid_dev = retry_fun_call(
|
||||
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
|
||||
num_retries=20,
|
||||
retry_delay=0.05)
|
||||
except RetryError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
try:
|
||||
# Request the Configuration descriptor.
|
||||
cfg_desc = mbed_hid_dev.ctrl_transfer(**kwargs_cfg_desc_req) # pylint: disable=not-callable
|
||||
raise_if_false(DESC_TYPE_HID_HID in get_descriptor_types(cfg_desc),
|
||||
'No HID class descriptor in the Configuration descriptor.')
|
||||
except usb.core.USBError as exc:
|
||||
self.notify_failure('Get_Descriptor request failed. {}'.format(exc))
|
||||
except RuntimeError as exc:
|
||||
self.notify_failure(exc)
|
||||
else:
|
||||
self.notify_success()
|
||||
|
||||
def cb_test_class_requests(self, key, value, timestamp):
|
||||
"""Verify all required HID requests are supported.
|
||||
|
||||
USB Device Class Definition for HID, v1.11, Appendix G:
|
||||
1. Get_Report -- required for all types,
|
||||
2. Set_Report -- not required if dev doesn't declare an Output Report,
|
||||
3. Get_Idle -- required for keyboards,
|
||||
4. Set_Idle -- required for keyboards,
|
||||
5. Get_Protocol -- required for boot_keyboard and boot_mouse,
|
||||
6. Set_Protocol -- required for boot_keyboard and boot_mouse.
|
||||
|
||||
Details in USB Device Class Definition for HID, v1.11, paragraph 7.2.
|
||||
"""
|
||||
kwargs_get_report_request = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': HID_REQUEST_GET_REPORT,
|
||||
# wValue: ReportType = Input, ReportID = 0 (not used)
|
||||
'wValue': (0x01 << 8) | 0x00,
|
||||
# wIndex: InterfaceNumber (defined later)
|
||||
'wIndex': None,
|
||||
# wLength: unknown, set to 1024
|
||||
'data_or_wLength': 1024}
|
||||
kwargs_get_idle_request = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': HID_REQUEST_GET_IDLE,
|
||||
# wValue: 0, ReportID = 0 (not used)
|
||||
'wValue': (0x00 << 8) | 0x00,
|
||||
# wIndex: InterfaceNumber (defined later)
|
||||
'wIndex': None,
|
||||
'data_or_wLength': 1}
|
||||
kwargs_set_idle_request = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': HID_REQUEST_SET_IDLE,
|
||||
# wValue: Duration, ReportID = 0 (all input reports)
|
||||
'wValue': (KEYBOARD_IDLE_RATE_TO_SET << 8) | 0x00,
|
||||
# wIndex: InterfaceNumber (defined later)
|
||||
'wIndex': None,
|
||||
'data_or_wLength': 0}
|
||||
kwargs_get_protocol_request = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_IN, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': HID_REQUEST_GET_PROTOCOL,
|
||||
'wValue': 0x00,
|
||||
# wIndex: InterfaceNumber (defined later)
|
||||
'wIndex': None,
|
||||
'data_or_wLength': 1}
|
||||
kwargs_set_protocol_request = {
|
||||
'bmRequestType': build_request_type(
|
||||
CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE),
|
||||
'bRequest': HID_REQUEST_SET_PROTOCOL,
|
||||
'wValue': HID_PROTOCOL_TO_SET,
|
||||
# wIndex: InterfaceNumber (defined later)
|
||||
'wIndex': None,
|
||||
'data_or_wLength': 0}
|
||||
mbed_hid_dev = None
|
||||
try:
|
||||
mbed_hid_dev = retry_fun_call(
|
||||
fun=functools.partial(self.get_usb_dev, self.dut_usb_dev_sn), # pylint: disable=not-callable
|
||||
num_retries=20,
|
||||
retry_delay=0.05)
|
||||
except RetryError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
hid_dev_type = None
|
||||
tested_request_name = None
|
||||
try:
|
||||
for intf in mbed_hid_dev.get_active_configuration(): # pylint: disable=not-callable
|
||||
hid_dev_type = get_usbhid_dev_type(intf)
|
||||
if hid_dev_type is None:
|
||||
continue
|
||||
try:
|
||||
if mbed_hid_dev.is_kernel_driver_active(intf.bInterfaceNumber):
|
||||
mbed_hid_dev.detach_kernel_driver(intf.bInterfaceNumber) # pylint: disable=not-callable
|
||||
except (NotImplementedError, AttributeError):
|
||||
pass
|
||||
if hid_dev_type == 'boot_keyboard':
|
||||
# 4. Set_Idle
|
||||
tested_request_name = 'Set_Idle'
|
||||
kwargs_set_idle_request['wIndex'] = intf.bInterfaceNumber
|
||||
mbed_hid_dev.ctrl_transfer(**kwargs_set_idle_request) # pylint: disable=not-callable
|
||||
# 3. Get_Idle
|
||||
tested_request_name = 'Get_Idle'
|
||||
kwargs_get_idle_request['wIndex'] = intf.bInterfaceNumber
|
||||
idle_rate = mbed_hid_dev.ctrl_transfer(**kwargs_get_idle_request) # pylint: disable=not-callable
|
||||
raise_if_different(KEYBOARD_IDLE_RATE_TO_SET, idle_rate, 'Invalid idle rate received. ')
|
||||
if hid_dev_type in ('boot_keyboard', 'boot_mouse'):
|
||||
# 6. Set_Protocol
|
||||
tested_request_name = 'Set_Protocol'
|
||||
kwargs_set_protocol_request['wIndex'] = intf.bInterfaceNumber
|
||||
mbed_hid_dev.ctrl_transfer(**kwargs_set_protocol_request) # pylint: disable=not-callable
|
||||
# 5. Get_Protocol
|
||||
tested_request_name = 'Get_Protocol'
|
||||
kwargs_get_protocol_request['wIndex'] = intf.bInterfaceNumber
|
||||
protocol = mbed_hid_dev.ctrl_transfer(**kwargs_get_protocol_request) # pylint: disable=not-callable
|
||||
raise_if_different(HID_PROTOCOL_TO_SET, protocol, 'Invalid protocol received. ')
|
||||
# 1. Get_Report
|
||||
tested_request_name = 'Get_Report'
|
||||
kwargs_get_report_request['wIndex'] = intf.bInterfaceNumber
|
||||
mbed_hid_dev.ctrl_transfer(**kwargs_get_report_request) # pylint: disable=not-callable
|
||||
except usb.core.USBError as exc:
|
||||
self.notify_failure('The {!r} does not support the {!r} HID class request ({}).'
|
||||
.format(hid_dev_type, tested_request_name, exc))
|
||||
except RuntimeError as exc:
|
||||
self.notify_failure('Set/Get data mismatch for {!r} for the {!r} HID class request ({}).'
|
||||
.format(hid_dev_type, tested_request_name, exc))
|
||||
else:
|
||||
self.notify_success()
|
||||
|
||||
def raw_loopback(self, report_size):
|
||||
"""Send every input report back to the device."""
|
||||
mbed_hid_path = None
|
||||
mbed_hid = hid.device()
|
||||
try:
|
||||
mbed_hid_path = retry_fun_call(
|
||||
fun=functools.partial(self.get_usb_hid_path, self.dut_usb_dev_sn), # pylint: disable=not-callable
|
||||
num_retries=20,
|
||||
retry_delay=0.05)
|
||||
retry_fun_call(
|
||||
fun=functools.partial(mbed_hid.open_path, mbed_hid_path), # pylint: disable=not-callable
|
||||
num_retries=10,
|
||||
retry_delay=0.05)
|
||||
except RetryError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
# Notify the device it can send reports now.
|
||||
self.send_kv(MSG_KEY_HOST_READY, MSG_VALUE_DUMMY)
|
||||
try:
|
||||
for _ in range(RAW_IO_REPS):
|
||||
# There are no Report ID tags in the Report descriptor.
|
||||
# Receiving only the Report Data, Report ID is omitted.
|
||||
report_in = mbed_hid.read(report_size)
|
||||
report_out = report_in[:]
|
||||
# Set the Report ID to 0x00 (not used).
|
||||
report_out.insert(0, 0x00)
|
||||
mbed_hid.write(report_out)
|
||||
except (ValueError, IOError) as exc:
|
||||
self.notify_failure('HID Report transfer failed. {}'.format(exc))
|
||||
finally:
|
||||
mbed_hid.close()
|
||||
|
||||
def setup(self):
|
||||
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
|
||||
self.register_callback(MSG_KEY_TEST_GET_DESCRIPTOR_HID, self.cb_test_get_hid_desc)
|
||||
self.register_callback(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, self.cb_test_get_cfg_desc)
|
||||
self.register_callback(MSG_KEY_TEST_REQUESTS, self.cb_test_class_requests)
|
||||
self.register_callback(MSG_KEY_TEST_RAW_IO, self.cb_test_raw_io)
|
||||
|
||||
def cb_device_ready(self, key, value, timestamp):
|
||||
"""Send a unique USB SN to the device.
|
||||
|
||||
DUT uses this SN every time it connects to host as a USB device.
|
||||
"""
|
||||
self.send_kv(MSG_KEY_SERIAL_NUMBER, self.dut_usb_dev_sn)
|
||||
|
||||
def start_bg_task(self, **thread_kwargs):
|
||||
"""Start a new daemon thread.
|
||||
|
||||
Some callbacks delegate HID dev handling to a background task to
|
||||
prevent any delays in the device side assert handling. Only one
|
||||
background task is kept running to prevent multiple access
|
||||
to the HID device.
|
||||
"""
|
||||
try:
|
||||
self.__bg_task.join()
|
||||
except (AttributeError, RuntimeError):
|
||||
pass
|
||||
self.__bg_task = threading.Thread(**thread_kwargs)
|
||||
self.__bg_task.daemon = True
|
||||
self.__bg_task.start()
|
||||
|
||||
def cb_test_raw_io(self, key, value, timestamp):
|
||||
"""Receive HID reports and send them back to the device."""
|
||||
if not CYTHON_HIDAPI_PRESENT:
|
||||
self.send_kv(MSG_KEY_HOST_READY, MSG_VALUE_NOT_SUPPORTED)
|
||||
return
|
||||
try:
|
||||
# The size of input and output reports used in test.
|
||||
report_size = int(value)
|
||||
except ValueError as exc:
|
||||
self.notify_error(exc)
|
||||
return
|
||||
self.start_bg_task(
|
||||
target=self.raw_loopback,
|
||||
args=(report_size, ))
|
|
@ -0,0 +1,23 @@
|
|||
# Testing the USB HID device with a Linux host
|
||||
|
||||
Before running `tests-usb_device-hid` test suite on a Linux machine, please
|
||||
make sure to install the `hidapi` Python module first, otherwise some test
|
||||
cases will be skipped. Due to external dependencies for Linux, this module
|
||||
is not installed during the initial setup, to keep the process as simple
|
||||
as possible.
|
||||
|
||||
For Debian-based Linux distros, the dependencies can be installed as follows
|
||||
(based on module's [README][1]):
|
||||
|
||||
```bash
|
||||
apt-get install python-dev libusb-1.0-0-dev libudev-dev
|
||||
pip install --upgrade setuptools
|
||||
```
|
||||
To install the `hidapi` module itself, please use the attached
|
||||
`TESTS/usb_device/hid/requirements.txt` file:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
[1]: https://github.com/trezor/cython-hidapi/blob/master/README.rst#install
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
|
||||
#error [NOT_SUPPORTED] USB Device not supported for this target
|
||||
#endif
|
||||
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "utest/utest.h"
|
||||
#include "unity/unity.h"
|
||||
#include "mbed.h"
|
||||
#include <stdlib.h>
|
||||
#include "usb_phy_api.h"
|
||||
#include "USBHID.h"
|
||||
#include "USBMouse.h"
|
||||
#include "USBKeyboard.h"
|
||||
|
||||
// Reuse the VID & PID from basic USB test.
|
||||
#define USB_HID_VID 0x0d28
|
||||
#define USB_HID_PID_GENERIC 0x0206
|
||||
#define USB_HID_PID_KEYBOARD 0x0206
|
||||
#define USB_HID_PID_MOUSE 0x0206
|
||||
#define USB_HID_PID_GENERIC2 0x0007
|
||||
|
||||
#define MSG_VALUE_LEN 24
|
||||
#define MSG_KEY_LEN 24
|
||||
#define MSG_KEY_DEVICE_READY "ready"
|
||||
#define MSG_KEY_DEVICE_READY "dev_ready"
|
||||
#define MSG_KEY_HOST_READY "host_ready"
|
||||
#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn"
|
||||
#define MSG_KEY_TEST_GET_DESCRIPTOR_HID "test_get_desc_hid"
|
||||
#define MSG_KEY_TEST_GET_DESCRIPTOR_CFG "test_get_desc_cfg"
|
||||
#define MSG_KEY_TEST_REQUESTS "test_requests"
|
||||
#define MSG_KEY_TEST_RAW_IO "test_raw_io"
|
||||
|
||||
#define MSG_KEY_TEST_CASE_FAILED "fail"
|
||||
#define MSG_KEY_TEST_CASE_PASSED "pass"
|
||||
#define MSG_VALUE_DUMMY "0"
|
||||
#define MSG_VALUE_NOT_SUPPORTED "not_supported"
|
||||
|
||||
#define RAW_IO_REPS 16
|
||||
|
||||
#define USB_DEV_SN_LEN (32) // 32 hex digit UUID
|
||||
#define NONASCII_CHAR ('?')
|
||||
#define USB_DEV_SN_DESC_SIZE (USB_DEV_SN_LEN * 2 + 2)
|
||||
|
||||
const char *default_serial_num = "0123456789";
|
||||
char usb_dev_sn[USB_DEV_SN_LEN + 1];
|
||||
|
||||
using utest::v1::Case;
|
||||
using utest::v1::Specification;
|
||||
using utest::v1::Harness;
|
||||
|
||||
/**
|
||||
* Convert a USB string descriptor to C style ASCII
|
||||
*
|
||||
* The string placed in str is always null-terminated which may cause the
|
||||
* loss of data if n is to small. If the length of descriptor string is less
|
||||
* than n, additional null bytes are written to str.
|
||||
*
|
||||
* @param str output buffer for the ASCII string
|
||||
* @param usb_desc USB string descriptor
|
||||
* @param n size of str buffer
|
||||
* @returns number of non-null bytes returned in str or -1 on failure
|
||||
*/
|
||||
int usb_string_desc2ascii(char *str, const uint8_t *usb_desc, size_t n)
|
||||
{
|
||||
if (str == NULL || usb_desc == NULL || n < 1) {
|
||||
return -1;
|
||||
}
|
||||
// bDescriptorType @ offset 1
|
||||
if (usb_desc[1] != STRING_DESCRIPTOR) {
|
||||
return -1;
|
||||
}
|
||||
// bLength @ offset 0
|
||||
const size_t bLength = usb_desc[0];
|
||||
if (bLength % 2 != 0) {
|
||||
return -1;
|
||||
}
|
||||
size_t s, d;
|
||||
for (s = 0, d = 2; s < n - 1 && d < bLength; s++, d += 2) {
|
||||
// handle non-ASCII characters
|
||||
if (usb_desc[d] > 0x7f || usb_desc[d + 1] != 0) {
|
||||
str[s] = NONASCII_CHAR;
|
||||
} else {
|
||||
str[s] = usb_desc[d];
|
||||
}
|
||||
}
|
||||
int str_len = s;
|
||||
for (; s < n; s++) {
|
||||
str[s] = '\0';
|
||||
}
|
||||
return str_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a C style ASCII to a USB string descriptor
|
||||
*
|
||||
* @param usb_desc output buffer for the USB string descriptor
|
||||
* @param str ASCII string
|
||||
* @param n size of usb_desc buffer, even number
|
||||
* @returns number of bytes returned in usb_desc or -1 on failure
|
||||
*/
|
||||
int ascii2usb_string_desc(uint8_t *usb_desc, const char *str, size_t n)
|
||||
{
|
||||
if (str == NULL || usb_desc == NULL || n < 4) {
|
||||
return -1;
|
||||
}
|
||||
if (n % 2 != 0) {
|
||||
return -1;
|
||||
}
|
||||
size_t s, d;
|
||||
// set bString (@ offset 2 onwards) as a UNICODE UTF-16LE string
|
||||
memset(usb_desc, 0, n);
|
||||
for (s = 0, d = 2; str[s] != '\0' && d < n; s++, d += 2) {
|
||||
usb_desc[d] = str[s];
|
||||
}
|
||||
// set bLength @ offset 0
|
||||
usb_desc[0] = d;
|
||||
// set bDescriptorType @ offset 1
|
||||
usb_desc[1] = STRING_DESCRIPTOR;
|
||||
return d;
|
||||
}
|
||||
|
||||
class TestUSBHID: public USBHID {
|
||||
private:
|
||||
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
|
||||
public:
|
||||
TestUSBHID(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num, uint8_t output_report_length = 64, uint8_t input_report_length = 64) :
|
||||
USBHID(get_usb_phy(), output_report_length, input_report_length, vendor_id, product_id, 0x01)
|
||||
{
|
||||
init();
|
||||
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
|
||||
if (rc < 0) {
|
||||
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~TestUSBHID()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
virtual const uint8_t *string_iserial_desc()
|
||||
{
|
||||
return (const uint8_t *) _serial_num_descriptor;
|
||||
}
|
||||
|
||||
// Make this accessible for tests (public).
|
||||
using USBHID::report_desc_length;
|
||||
};
|
||||
|
||||
class TestUSBMouse: public USBMouse {
|
||||
private:
|
||||
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
|
||||
public:
|
||||
TestUSBMouse(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) :
|
||||
USBMouse(get_usb_phy(), REL_MOUSE, vendor_id, product_id, 0x01)
|
||||
{
|
||||
init();
|
||||
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
|
||||
if (rc < 0) {
|
||||
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~TestUSBMouse()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
virtual const uint8_t *string_iserial_desc()
|
||||
{
|
||||
return (const uint8_t *) _serial_num_descriptor;
|
||||
}
|
||||
|
||||
// Make this accessible for tests (public).
|
||||
using USBHID::report_desc_length;
|
||||
};
|
||||
|
||||
class TestUSBKeyboard: public USBKeyboard {
|
||||
private:
|
||||
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
|
||||
public:
|
||||
TestUSBKeyboard(uint16_t vendor_id, uint16_t product_id, const char *serial_number = default_serial_num) :
|
||||
USBKeyboard(get_usb_phy(), vendor_id, product_id, 0x01)
|
||||
{
|
||||
init();
|
||||
int rc = ascii2usb_string_desc(_serial_num_descriptor, serial_number, USB_DEV_SN_DESC_SIZE);
|
||||
if (rc < 0) {
|
||||
ascii2usb_string_desc(_serial_num_descriptor, default_serial_num, USB_DEV_SN_DESC_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~TestUSBKeyboard()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
virtual const uint8_t *string_iserial_desc()
|
||||
{
|
||||
return (const uint8_t *) _serial_num_descriptor;
|
||||
}
|
||||
|
||||
// Make this accessible for tests (public).
|
||||
using USBHID::report_desc_length;
|
||||
};
|
||||
|
||||
/** Test Get_Descriptor request with the HID class descriptors
|
||||
*
|
||||
* Given a USB HID class device connected to a host,
|
||||
* when the host issues the Get_Descriptor(HID) request,
|
||||
* then the device returns the HID descriptor.
|
||||
*
|
||||
* When the host issues the Get_Descriptor(Report) request,
|
||||
* then the device returns the Report descriptor
|
||||
* and the size of the descriptor is equal to USBHID::report_desc_length().
|
||||
*
|
||||
* Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
|
||||
*/
|
||||
template<typename T, uint16_t PID>
|
||||
void test_get_hid_class_desc()
|
||||
{
|
||||
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
|
||||
usb_hid.connect();
|
||||
greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_HID, MSG_VALUE_DUMMY);
|
||||
|
||||
char key[MSG_KEY_LEN + 1] = { };
|
||||
char value[MSG_VALUE_LEN + 1] = { };
|
||||
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
|
||||
uint16_t host_report_desc_len;
|
||||
int num_args = sscanf(value, "%04hx", &host_report_desc_len);
|
||||
TEST_ASSERT_MESSAGE(num_args != 0 && num_args != EOF, "Invalid data received from host.");
|
||||
TEST_ASSERT_EQUAL_UINT16(usb_hid.report_desc_length(), host_report_desc_len);
|
||||
}
|
||||
|
||||
/** Test Get_Descriptor request with the Configuration descriptor
|
||||
*
|
||||
* Given a USB HID class device connected to a host,
|
||||
* when the host issues the Get_Descriptor(Configuration) request,
|
||||
* then the device returns the Configuration descriptor and a HID
|
||||
* descriptor for each HID interface.
|
||||
*
|
||||
* Details in USB Device Class Definition for HID, v1.11, paragraph 7.1.
|
||||
*/
|
||||
template<typename T, uint16_t PID>
|
||||
void test_get_configuration_desc()
|
||||
{
|
||||
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
|
||||
usb_hid.connect();
|
||||
greentea_send_kv(MSG_KEY_TEST_GET_DESCRIPTOR_CFG, MSG_VALUE_DUMMY);
|
||||
|
||||
char key[MSG_KEY_LEN + 1] = { };
|
||||
char value[MSG_VALUE_LEN + 1] = { };
|
||||
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
|
||||
}
|
||||
|
||||
/** Test HID class requests
|
||||
*
|
||||
* Given a USB HID class device connected to a host,
|
||||
* when the host issues a request specific to the HID class device type,
|
||||
* then the device returns valid data.
|
||||
*
|
||||
* Details in USB Device Class Definition for HID, v1.11,
|
||||
* paragraph 7.2 and Appendix G.
|
||||
*/
|
||||
template<typename T, uint16_t PID>
|
||||
void test_class_requests()
|
||||
{
|
||||
T usb_hid(USB_HID_VID, PID, usb_dev_sn);
|
||||
usb_hid.connect();
|
||||
greentea_send_kv(MSG_KEY_TEST_REQUESTS, MSG_VALUE_DUMMY);
|
||||
|
||||
char key[MSG_KEY_LEN + 1] = { };
|
||||
char value[MSG_VALUE_LEN + 1] = { };
|
||||
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||
TEST_ASSERT_EQUAL_STRING(MSG_KEY_TEST_CASE_PASSED, key);
|
||||
}
|
||||
|
||||
/** Test send & read
|
||||
*
|
||||
* Given a USB HID class device connected to a host,
|
||||
* when the device sends input reports with a random data to the host
|
||||
* and the host sends them back to the device,
|
||||
* then received output report data is equal to the input report data.
|
||||
*/
|
||||
template<uint8_t REPORT_SIZE> // Range [1, MAX_HID_REPORT_SIZE].
|
||||
void test_generic_raw_io()
|
||||
{
|
||||
TestUSBHID usb_hid(USB_HID_VID, USB_HID_PID_GENERIC2, usb_dev_sn, REPORT_SIZE, REPORT_SIZE);
|
||||
usb_hid.connect();
|
||||
greentea_send_kv(MSG_KEY_TEST_RAW_IO, REPORT_SIZE);
|
||||
|
||||
// Wait for the host HID driver to complete setup.
|
||||
char key[MSG_KEY_LEN + 1] = { };
|
||||
char value[MSG_VALUE_LEN + 1] = { };
|
||||
greentea_parse_kv(key, value, MSG_KEY_LEN, MSG_VALUE_LEN);
|
||||
TEST_ASSERT_EQUAL_STRING(MSG_KEY_HOST_READY, key);
|
||||
if (strcmp(value, MSG_VALUE_NOT_SUPPORTED) == 0) {
|
||||
TEST_IGNORE_MESSAGE("Test case not supported by host plarform.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Report ID omitted here. There are no Report ID tags in the Report descriptor.
|
||||
HID_REPORT input_report = {};
|
||||
HID_REPORT output_report = {};
|
||||
for (size_t r = 0; r < RAW_IO_REPS; r++) {
|
||||
for (size_t i = 0; i < REPORT_SIZE; i++) {
|
||||
input_report.data[i] = (uint8_t)(rand() % 0x100);
|
||||
}
|
||||
input_report.length = REPORT_SIZE;
|
||||
output_report.length = 0;
|
||||
TEST_ASSERT(usb_hid.send(&input_report));
|
||||
TEST_ASSERT(usb_hid.read(&output_report));
|
||||
TEST_ASSERT_EQUAL_UINT32(input_report.length, output_report.length);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(input_report.data, output_report.data, REPORT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(45, "usb_device_hid");
|
||||
srand((unsigned) ticker_read_us(get_us_ticker_data()));
|
||||
|
||||
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
|
||||
if (status != utest::v1::STATUS_CONTINUE) {
|
||||
return status;
|
||||
}
|
||||
|
||||
char key[MSG_KEY_LEN + 1] = { };
|
||||
char usb_dev_uuid[USB_DEV_SN_LEN + 1] = { };
|
||||
|
||||
greentea_send_kv(MSG_KEY_DEVICE_READY, MSG_VALUE_DUMMY);
|
||||
greentea_parse_kv(key, usb_dev_uuid, MSG_KEY_LEN, USB_DEV_SN_LEN + 1);
|
||||
|
||||
if (strcmp(key, MSG_KEY_SERIAL_NUMBER) != 0) {
|
||||
utest_printf("Invalid message key.\n");
|
||||
return utest::v1::STATUS_ABORT;
|
||||
}
|
||||
|
||||
strncpy(usb_dev_sn, usb_dev_uuid, USB_DEV_SN_LEN + 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Configuration descriptor, generic", test_get_configuration_desc<TestUSBHID, USB_HID_PID_GENERIC>),
|
||||
Case("Configuration descriptor, keyboard", test_get_configuration_desc<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
|
||||
Case("Configuration descriptor, mouse", test_get_configuration_desc<TestUSBMouse, USB_HID_PID_MOUSE>),
|
||||
|
||||
Case("HID class descriptors, generic", test_get_hid_class_desc<TestUSBHID, USB_HID_PID_GENERIC>),
|
||||
Case("HID class descriptors, keyboard", test_get_hid_class_desc<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
|
||||
Case("HID class descriptors, mouse", test_get_hid_class_desc<TestUSBMouse, USB_HID_PID_MOUSE>),
|
||||
|
||||
// HID class requests not supported by Mbed
|
||||
// Case("HID class requests, generic", test_class_requests<TestUSBHID, USB_HID_PID_GENERIC>),
|
||||
// Case("HID class requests, keyboard", test_class_requests<TestUSBKeyboard, USB_HID_PID_KEYBOARD>),
|
||||
// Case("HID class requests, mouse", test_class_requests<TestUSBMouse, USB_HID_PID_MOUSE>),
|
||||
|
||||
Case("Raw input/output, 1-byte reports", test_generic_raw_io<1>),
|
||||
Case("Raw input/output, 20-byte reports", test_generic_raw_io<20>),
|
||||
Case("Raw input/output, 64-byte reports", test_generic_raw_io<64>),
|
||||
};
|
||||
|
||||
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
hidapi>=0.7.99,<0.8.0
|
|
@ -21,4 +21,5 @@ manifest-tool==1.4.8
|
|||
icetea>=1.2.1,<1.3
|
||||
pycryptodome>=3.7.2,<=3.7.3
|
||||
pyusb>=1.0.0,<2.0.0
|
||||
hidapi>=0.7.99,<0.8.0;platform_system!="Linux"
|
||||
cmsis-pack-manager>=0.2.3,<0.3.0
|
||||
|
|
|
@ -380,7 +380,7 @@ void USBHID::callback_set_configuration(uint8_t configuration)
|
|||
endpoint_add(_int_out, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_read_isr);
|
||||
|
||||
// We activate the endpoint to be able to recceive data
|
||||
read_start(_int_out, (uint8_t *)&_output_report, MAX_HID_REPORT_SIZE);
|
||||
read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE);
|
||||
_read_idle = false;
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue