Merge pull request #9768 from c1728p9/feature-hal-spec-usb-device

Bring USB Feature branch into master
pull/9856/head
Cruz Monrreal 2019-02-26 22:48:02 -06:00 committed by GitHub
commit 4b13c8a212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 26010 additions and 311 deletions

View File

@ -204,7 +204,7 @@ matrix:
- python tools/make.py -t GCC_ARM -m K64F --source=. --build=BUILD/K64F/GCC_ARM -j0
# Check that example compiles without rtos
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' $EVENTS/README.md > main.cpp
- rm -r rtos features/cellular features/netsocket features/nanostack features/lwipstack features/frameworks/greentea-client features/frameworks/utest features/frameworks/unity components BUILD
- rm -r rtos usb features/cellular features/netsocket features/nanostack features/lwipstack features/frameworks/greentea-client features/frameworks/utest features/frameworks/unity components BUILD
- python tools/make.py -t GCC_ARM -m DISCO_F401VC --source=. --build=BUILD/DISCO_F401VC/GCC_ARM -j0
# Run local equeue tests
- make -C $EVENTS/equeue test

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,347 @@
"""
mbed SDK
Copyright (c) 2018 ARM Limited
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.
"""
from __future__ import print_function
import functools
import itertools
import time
import threading
import uuid
import sys
import serial
import serial.tools.list_ports as stlp
import mbed_host_tests
MSG_KEY_DEVICE_READY = 'ready'
MSG_KEY_SERIAL_NUMBER = 'usb_dev_sn'
MSG_KEY_PORT_OPEN_WAIT = 'port_open_wait'
MSG_KEY_PORT_OPEN_CLOSE = 'port_open_close'
MSG_KEY_SEND_BYTES_SINGLE = 'send_single'
MSG_KEY_SEND_BYTES_MULTIPLE = 'send_multiple'
MSG_KEY_LOOPBACK = 'loopback'
MSG_KEY_CHANGE_LINE_CODING = 'change_lc'
RX_BUFF_SIZE = 32
# This delay eliminates the possibility of the device detecting
# the port being closed when still waiting for data.
TERM_CLOSE_DELAY = 0.01
# A duration the serial terminal is open on the host side
# during terminal reopen test.
TERM_REOPEN_DELAY = 0.1
# 6 (baud) + 2 (bits) + 1 (parity) + 1 (stop) + 3 * comma
LINE_CODING_STRLEN = 13
def usb_serial_name(serial_number):
"""Get USB serial device name based on the device serial number."""
if sys.platform.startswith('win'):
# The USB spec defines all USB string descriptors to be
# UNICODE UTF-16LE. Windows however, decodes the USB serial
# number string descriptor as uppercase characters only.
# To solve this issue, convert the pattern to uppercase.
serial_number = str(serial_number).upper()
for port_info in stlp.comports():
if port_info.serial_number == serial_number:
return port_info.device
return None
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))
class USBSerialTest(mbed_host_tests.BaseHostTest):
"""Host side test for USB CDC & Serial classes."""
_BYTESIZES = {
5: serial.FIVEBITS,
6: serial.SIXBITS,
7: serial.SEVENBITS,
8: serial.EIGHTBITS}
_PARITIES = {
0: serial.PARITY_NONE,
1: serial.PARITY_ODD,
2: serial.PARITY_EVEN,
3: serial.PARITY_MARK,
4: serial.PARITY_SPACE}
_STOPBITS = {
0: serial.STOPBITS_ONE,
1: serial.STOPBITS_ONE_POINT_FIVE,
2: serial.STOPBITS_TWO}
@staticmethod
def get_usb_serial_name(usb_id_str):
"""Get USB serial device name 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.
"""
port_name = usb_serial_name(usb_id_str)
if port_name is None:
err_msg = 'USB serial device (SN={}) not found.'
raise RuntimeError(err_msg.format(usb_id_str))
return port_name
def __init__(self):
super(USBSerialTest, self).__init__()
self.__bg_task = None
self.dut_usb_dev_sn = uuid.uuid4().hex # 32 hex digit string
def port_open_wait(self):
"""Open the serial and wait until it's closed by the device."""
mbed_serial = serial.Serial(dsrdtr=False)
mbed_serial.dtr = False
try:
mbed_serial.port = retry_fun_call(
fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=mbed_serial.open,
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
mbed_serial.dtr = True
try:
mbed_serial.read() # wait until closed
except (serial.portNotOpenError, serial.SerialException):
pass
def port_open_close(self):
"""Open the serial and close it with a delay."""
mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False)
mbed_serial.dtr = False
try:
mbed_serial.port = retry_fun_call(
fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=mbed_serial.open,
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
mbed_serial.reset_output_buffer()
mbed_serial.dtr = True
time.sleep(TERM_REOPEN_DELAY)
mbed_serial.close()
def send_data_sequence(self, chunk_size=1):
"""Open the serial and send a sequence of values.
chunk_size defines the size of data sent in each write operation.
The input buffer content is discarded.
"""
mbed_serial = serial.Serial(write_timeout=0.1, dsrdtr=False)
try:
mbed_serial.port = retry_fun_call(
fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=mbed_serial.open,
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
mbed_serial.reset_output_buffer()
mbed_serial.dtr = True
for byteval in itertools.chain(reversed(range(0x100)), range(0x100)):
try:
payload = bytearray(chunk_size * (byteval,))
mbed_serial.write(payload)
# self.log('SENT: {!r}'.format(payload))
# Discard input buffer content. The data received from the
# device during the concurrent rx/tx test is irrelevant.
mbed_serial.reset_input_buffer()
except serial.SerialException as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
while mbed_serial.out_waiting > 0:
time.sleep(0.001)
time.sleep(TERM_CLOSE_DELAY)
mbed_serial.close()
def loopback(self):
"""Open the serial and send back every byte received."""
mbed_serial = serial.Serial(timeout=0.5, write_timeout=0.1, dsrdtr=False)
mbed_serial.dtr = False
try:
mbed_serial.port = retry_fun_call(
fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=mbed_serial.open,
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
mbed_serial.reset_output_buffer()
mbed_serial.dtr = True
try:
payload = mbed_serial.read(1)
while len(payload) == 1:
mbed_serial.write(payload)
# self.log('SENT: {!r}'.format(payload))
payload = mbed_serial.read(1)
except serial.SerialException as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
while mbed_serial.out_waiting > 0:
time.sleep(0.001)
time.sleep(TERM_CLOSE_DELAY)
mbed_serial.close()
def change_line_coding(self):
"""Open the serial and change serial params according to device request.
New line coding params are read from the device serial data.
"""
mbed_serial = serial.Serial(timeout=0.5, dsrdtr=False)
mbed_serial.dtr = False
try:
mbed_serial.port = retry_fun_call(
fun=functools.partial(self.get_usb_serial_name, self.dut_usb_dev_sn), # pylint: disable=not-callable
num_retries=20,
retry_delay=0.05)
retry_fun_call(
fun=mbed_serial.open,
num_retries=20,
retry_delay=0.05)
except RetryError as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
mbed_serial.reset_output_buffer()
mbed_serial.dtr = True
try:
payload = mbed_serial.read(LINE_CODING_STRLEN)
while len(payload) == LINE_CODING_STRLEN:
baud, bits, parity, stop = (int(i) for i in payload.split(','))
new_line_coding = {
'baudrate': baud,
'bytesize': self._BYTESIZES[bits],
'parity': self._PARITIES[parity],
'stopbits': self._STOPBITS[stop]}
mbed_serial.apply_settings(new_line_coding)
payload = mbed_serial.read(LINE_CODING_STRLEN)
except serial.SerialException as exc:
self.log('TEST ERROR: {}'.format(exc))
self.notify_complete(False)
return
time.sleep(TERM_CLOSE_DELAY)
mbed_serial.close()
def setup(self):
self.register_callback(MSG_KEY_DEVICE_READY, self.cb_device_ready)
self.register_callback(MSG_KEY_PORT_OPEN_WAIT, self.cb_port_open_wait)
self.register_callback(MSG_KEY_PORT_OPEN_CLOSE, self.cb_port_open_close)
self.register_callback(MSG_KEY_SEND_BYTES_SINGLE, self.cb_send_bytes_single)
self.register_callback(MSG_KEY_SEND_BYTES_MULTIPLE, self.cb_send_bytes_multiple)
self.register_callback(MSG_KEY_LOOPBACK, self.cb_loopback)
self.register_callback(MSG_KEY_CHANGE_LINE_CODING, self.cb_change_line_coding)
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.
The callbacks delegate serial 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 serial.
"""
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_port_open_wait(self, key, value, timestamp):
"""Open the serial and wait until it's closed by the device."""
self.start_bg_task(target=self.port_open_wait)
def cb_port_open_close(self, key, value, timestamp):
"""Open the serial and close it with a delay."""
self.start_bg_task(target=self.port_open_close)
def cb_send_bytes_single(self, key, value, timestamp):
"""Open the serial and send a sequence of values."""
self.start_bg_task(
target=self.send_data_sequence,
args=(1, ))
def cb_send_bytes_multiple(self, key, value, timestamp):
"""Open the serial and send a sequence of one byte values."""
chunk_size = RX_BUFF_SIZE * int(value)
self.start_bg_task(
target=self.send_data_sequence,
args=(chunk_size, ))
def cb_loopback(self, key, value, timestamp):
"""Open the serial and send a sequence of multibyte values."""
self.start_bg_task(target=self.loopback)
def cb_change_line_coding(self, key, value, timestamp):
"""Open the serial and change the line coding."""
self.start_bg_task(target=self.change_line_coding)

View File

@ -0,0 +1,15 @@
# Testing the USB device data toggle reset with a Linux host
When you run the `tests-usb_device-basic` test suite on a Linux machine, please make
sure that at least one of the following prerequisites are met:
* using the Linux kernel ***4.17* or newer**,
* using the ***eHCI*** USB driver instead of *xHCI*.
Implementations of the *xHCI* driver prior to version 4.17 of the Linux kernel did
not have the functionality necessary to test `"endpoint test data toggle reset"`.
Even though the data toggle is correctly reset on the device side, the host side will
not be synchronized and the test will falsely fail.
Further reading:
1. [the Linux kernel patch adding missing xHCI behavior](https://github.com/torvalds/linux/commit/f5249461b504d35aa1a40140983b7ec415807d9e),
1. [LKML discussion explaining the details of this issue](https://lkml.org/lkml/2016/12/15/388).

View File

@ -0,0 +1,859 @@
/*
* 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 "stdlib.h"
#include "USBEndpointTester.h"
#include "mbed_shared_queues.h"
#include "EndpointResolver.h"
#define DEFAULT_CONFIGURATION (1)
#define NUM_PACKETS_UNTIL_ABORT 2
#define NUM_PACKETS_AFTER_ABORT 8
#define EP_ABORT_BUFF_VALUE 0xff
/* If the host ever receives a payload with any byte set to this value,
* the device does not handle abort operation correctly. The buffer
* passed to aborted operation must not be used after call to abort().
*/
#define FORBIDDEN_PAYLOAD_VALUE (NUM_PACKETS_AFTER_ABORT + 1)
#define VENDOR_TEST_CTRL_IN 1
#define VENDOR_TEST_CTRL_OUT 2
#define VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#define VENDOR_TEST_RW_RESTART 11
#define VENDOR_TEST_ABORT_BUFF_CHECK 12
#define EVENT_READY (1 << 0)
#define TEST_SIZE_EP_BULK_MAX (64)
#define TEST_SIZE_EP_BULK_MIN (8)
#define TEST_SIZE_EP_BULK_0 (16)
#define TEST_SIZE_EP_BULK_1 TEST_SIZE_EP_BULK_MAX
#define TEST_SIZE_EP_BULK_2 (32)
#define TEST_SIZE_EP_BULK_3 (16)
#define TEST_SIZE_EP_BULK_4 TEST_SIZE_EP_BULK_MIN
#define TEST_SIZE_EP_INT_MAX (64)
#define TEST_SIZE_EP_INT_MIN (1)
#define TEST_SIZE_EP_INT_0 (16)
#define TEST_SIZE_EP_INT_1 TEST_SIZE_EP_INT_MAX
#define TEST_SIZE_EP_INT_2 (32)
#define TEST_SIZE_EP_INT_3 (16)
#define TEST_SIZE_EP_INT_4 TEST_SIZE_EP_INT_MIN
/* According to USB spec, the wMaxPacketSize for FS isochronous endpoints
* is 1023 B. There are a couple of reasons this value is not used in tests:
* - some of the boards supported by Mbed OS have too little RAM dedicated
* for USB, making EndpointResolve::valid() fail when all the endpoints (2x
* bulk, 2x interrupt, 2x isochronous, 2x control) are configured to use
* the max value of wMaxPacketSize
* (e.g. NUCLEO_F207ZG has 1.25K of endpoint RAM),
* - given a test host with other USB devices on the bus, it is unlikely
* for the test device to be able to reserve the bandwidth associated with
* high wMaxPacketSize for iso endpoints.
*/
#define TEST_SIZE_EP_ISO_MAX (256)
#define TEST_SIZE_EP_ISO_MIN (1)
#define TEST_SIZE_EP_ISO_0 (0)
#define TEST_SIZE_EP_ISO_1 (0)
#define TEST_SIZE_EP_ISO_2 (0)
#define TEST_SIZE_EP_ISO_3 (0)
#define TEST_SIZE_EP_ISO_4 (0)
#define EP_BULK_OUT 0
#define EP_BULK_IN 1
#define EP_INT_OUT 2
#define EP_INT_IN 3
#define EP_ISO_OUT 4
#define EP_ISO_IN 5
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config_max[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_out) },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_in) },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_out) },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_in) },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_out) },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_MAX, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_in) },
};
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config0[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_0, NULL },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_0, NULL },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_0, NULL },
};
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config1[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_out) },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_in) },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_out) },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_in) },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_out) },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_1, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_in) },
};
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config2[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_out) },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_in) },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_out) },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_in) },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_out) },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_2, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_in) },
};
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config3[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_out) },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_in) },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_out) },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_in) },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_out) },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_3, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_in) },
};
USBEndpointTester::ep_config_t USBEndpointTester::_intf_config4[NUM_ENDPOINTS] = {
{ false, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_out) },
{ true, USB_EP_TYPE_BULK, TEST_SIZE_EP_BULK_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_bulk_in) },
{ false, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_out) },
{ true, USB_EP_TYPE_INT, TEST_SIZE_EP_INT_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_int_in) },
{ false, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_out) },
{ true, USB_EP_TYPE_ISO, TEST_SIZE_EP_ISO_4, static_cast<ep_cb_t>(&USBEndpointTester::_cb_iso_in) },
};
USBEndpointTester::USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release,
bool abort_transfer_test) :
USBDevice(phy, vendor_id, product_id, product_release), _abort_transfer_test(abort_transfer_test), _endpoint_configs(
&_intf_config_max)
{
_cnt_cb_set_conf = 0;
_cnt_cb_set_intf = 0;
_cnt_cb_bulk_out = 0;
_cnt_cb_bulk_in = 0;
_cnt_cb_int_out = 0;
_cnt_cb_int_in = 0;
_cnt_cb_iso_out = 0;
_cnt_cb_iso_in = 0;
_num_packets_bulk_out_abort = 0;
_num_packets_bulk_in_abort = 0;
_num_packets_int_out_abort = 0;
_num_packets_int_in_abort = 0;
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(64);
ep_config_t *epc = NULL;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
epc = &((*_endpoint_configs)[i]);
_endpoints[i] = resolver.next_free_endpoint(epc->dir_in, epc->type, epc->max_packet);
_endpoint_buffs[i] = (uint8_t *) calloc(epc->max_packet, sizeof(uint8_t));
MBED_ASSERT(_endpoint_buffs[i] != NULL);
}
MBED_ASSERT(resolver.valid());
queue = mbed::mbed_highprio_event_queue();
configuration_desc(0);
init();
USBDevice::connect();
flags.wait_any(EVENT_READY, osWaitForever, false);
}
USBEndpointTester::~USBEndpointTester()
{
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoint_buffs[i] != NULL) {
free(_endpoint_buffs[i]);
}
}
deinit();
}
const char *USBEndpointTester::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;
}
const char *USBEndpointTester::get_serial_desc_string()
{
return get_desc_string(string_iserial_desc());
}
void USBEndpointTester::callback_state_change(DeviceState new_state)
{
if (new_state == Configured) {
flags.set(EVENT_READY);
} else {
flags.clear(EVENT_READY);
}
}
void USBEndpointTester::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 < 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_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;
case VENDOR_TEST_RW_RESTART:
result = (_request_rw_restart(setup)) ? Success : Failure;
break;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = Send;
ctrl_buf[0] = _request_abort_buff_check(setup);
data = ctrl_buf;
size = 1;
break;
default:
result = PassThrough;
break;
}
}
complete_request(result, data, size);
}
bool USBEndpointTester::_request_rw_restart(const setup_packet_t *setup)
{
assert_locked();
ep_config_t *epc = NULL;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
epc = &((*_endpoint_configs)[i]);
endpoint_abort(_endpoints[i]);
if (epc->dir_in == false) {
// Wait for data on every OUT endpoint
read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet);
}
}
return true;
}
bool USBEndpointTester::_request_abort_buff_check(const setup_packet_t *setup)
{
assert_locked();
if (setup->bmRequestType.Recipient != ENDPOINT_RECIPIENT) {
return false;
}
size_t ep_index = NUM_ENDPOINTS;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
if (_endpoints[i] == setup->wIndex) {
ep_index = i;
break;
}
}
if (ep_index == NUM_ENDPOINTS) {
return false;
}
if (_endpoint_buffs[ep_index] == NULL) {
return false;
}
for (size_t i = 0; i < (*_endpoint_configs)[ep_index].max_packet; i++) {
if (_endpoint_buffs[ep_index][i] != EP_ABORT_BUFF_VALUE) {
return false;
}
}
return true;
}
void USBEndpointTester::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;
case VENDOR_TEST_ABORT_BUFF_CHECK:
result = true;
break;
default:
result = false;
break;
}
}
complete_request_xfer_done(result);
}
void USBEndpointTester::callback_set_configuration(uint8_t configuration)
{
_cnt_cb_set_conf++;
if (configuration != DEFAULT_CONFIGURATION) {
complete_set_configuration(false);
return;
}
// Configure endpoints > 0
bool status = _setup_interface(0, 0);
complete_set_configuration(status);
}
bool USBEndpointTester::_setup_interface(uint16_t interface, uint8_t alternate)
{
if (interface != 0) {
return false;
}
switch (alternate) {
case 0:
_endpoint_configs = &_intf_config0;
break;
case 1:
_endpoint_configs = &_intf_config1;
break;
case 2:
_endpoint_configs = &_intf_config2;
break;
case 3:
_endpoint_configs = &_intf_config3;
break;
case 4:
_endpoint_configs = &_intf_config4;
break;
default:
return false;
}
_setup_non_zero_endpoints();
if (_abort_transfer_test && alternate >= 1) {
_num_packets_bulk_out_abort = 0;
_num_packets_bulk_in_abort = 0;
_num_packets_int_out_abort = 0;
_num_packets_int_in_abort = 0;
start_ep_in_abort_test();
}
return true;
}
void USBEndpointTester::_setup_non_zero_endpoints()
{
ep_config_t *epc = NULL;
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
epc = &((*_endpoint_configs)[i]);
endpoint_add(_endpoints[i], epc->max_packet, epc->type, epc->callback);
if (epc->callback == NULL) {
continue;
}
if (epc->dir_in == false) {
// Wait for data on every OUT endpoint
read_start(_endpoints[i], _endpoint_buffs[i], epc->max_packet);
}
}
}
void USBEndpointTester::callback_set_interface(uint16_t interface, uint8_t alternate)
{
_cnt_cb_set_intf++;
if (interface != 0 || alternate > 4) {
complete_set_interface(false);
return;
}
for (size_t i = 0; i < NUM_ENDPOINTS; i++) {
endpoint_abort(_endpoints[i]);
endpoint_remove(_endpoints[i]);
}
bool status = _setup_interface(interface, alternate);
complete_set_interface(status);
}
#define CONFIG1_DESC_SIZE (CONFIGURATION_DESCRIPTOR_LENGTH \
+ 5 * (INTERFACE_DESCRIPTOR_LENGTH + NUM_ENDPOINTS * ENDPOINT_DESCRIPTOR_LENGTH) )
const uint8_t *USBEndpointTester::configuration_desc(uint8_t index)
{
static const uint8_t config_1_descriptor[] = {
// configuration descriptor
CONFIGURATION_DESCRIPTOR_LENGTH, // bLength
CONFIGURATION_DESCRIPTOR, // bDescriptorType
LSB(CONFIG1_DESC_SIZE), // wTotalLength (LSB)
MSB(CONFIG1_DESC_SIZE), // wTotalLength (MSB)
1, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes
50, // bMaxPower
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
INTERFACE_DESCRIPTOR_LENGTH,// bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
NUM_ENDPOINTS, // 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
_endpoints[EP_BULK_OUT], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_BULK_IN], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_OUT], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_IN], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_OUT], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_IN], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config0[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
INTERFACE_DESCRIPTOR_LENGTH,// bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0, // bInterfaceNumber
1, // bAlternateSetting
NUM_ENDPOINTS, // 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
_endpoints[EP_BULK_OUT], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_BULK_IN], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_OUT], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_IN], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_OUT], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_IN], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config1[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
INTERFACE_DESCRIPTOR_LENGTH,// bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0, // bInterfaceNumber
2, // bAlternateSetting
NUM_ENDPOINTS, // 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
_endpoints[EP_BULK_OUT], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_BULK_IN], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_OUT], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_IN], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_OUT], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_IN], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config2[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
INTERFACE_DESCRIPTOR_LENGTH,// bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0, // bInterfaceNumber
3, // bAlternateSetting
NUM_ENDPOINTS, // 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
_endpoints[EP_BULK_OUT], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_BULK_IN], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_OUT], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_IN], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_OUT], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_IN], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config3[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
INTERFACE_DESCRIPTOR_LENGTH,// bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0, // bInterfaceNumber
4, // bAlternateSetting
NUM_ENDPOINTS, // 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
_endpoints[EP_BULK_OUT], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_BULK_OUT].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_BULK_IN], // bEndpointAddress
E_BULK, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_BULK_IN].max_packet)), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_OUT], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_INT_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_INT_IN], // bEndpointAddress
E_INTERRUPT, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_INT_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_OUT], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_ISO_OUT].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_endpoints[EP_ISO_IN], // bEndpointAddress
E_ISOCHRONOUS, // bmAttributes
(uint8_t)(LSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (LSB)
(uint8_t)(MSB(_intf_config4[EP_ISO_IN].max_packet)), // wMaxPacketSize (MSB)
1, // bInterval
};
if (index == 0) {
return config_1_descriptor;
} else {
return NULL;
}
}
void USBEndpointTester::_cb_bulk_out()
{
_cnt_cb_bulk_out++;
uint32_t rx_size = read_finish(_endpoints[EP_BULK_OUT]);
if (_abort_transfer_test == false) {
// Send data back to host using the IN endpoint.
memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet);
memcpy(_endpoint_buffs[EP_BULK_IN], _endpoint_buffs[EP_BULK_OUT], rx_size);
write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], rx_size);
} else {
// Abort the transfer if enough data was received.
_num_packets_bulk_out_abort++;
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_BULK_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_BULK_OUT].max_packet);
}
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
if (_num_packets_bulk_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_OUT]);
}
}
}
void USBEndpointTester::_cb_bulk_in()
{
_cnt_cb_bulk_in++;
write_finish(_endpoints[EP_BULK_IN]);
if (_abort_transfer_test == false) {
// Receive more data from the host using the OUT endpoint.
read_start(_endpoints[EP_BULK_OUT], _endpoint_buffs[EP_BULK_OUT], (*_endpoint_configs)[EP_BULK_OUT].max_packet);
} else {
_num_packets_bulk_in_abort++;
if (_num_packets_bulk_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) {
return;
}
// Abort the transfer if enough data was sent.
memset(_endpoint_buffs[EP_BULK_IN], _num_packets_bulk_in_abort, (*_endpoint_configs)[EP_BULK_IN].max_packet);
write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet);
if (_num_packets_bulk_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_BULK_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_BULK_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_BULK_IN].max_packet);
}
}
}
void USBEndpointTester::_cb_int_out()
{
_cnt_cb_int_out++;
uint32_t rx_size = read_finish(_endpoints[EP_INT_OUT]);
if (_abort_transfer_test == false) {
// Send data back to host using the IN endpoint.
memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet);
memcpy(_endpoint_buffs[EP_INT_IN], _endpoint_buffs[EP_INT_OUT], rx_size);
write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], rx_size);
} else {
// Abort the transfer if enough data was received.
_num_packets_int_out_abort++;
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
// Set every byte of the buffer to a known value.
memset(_endpoint_buffs[EP_INT_OUT], EP_ABORT_BUFF_VALUE, (*_endpoint_configs)[EP_INT_OUT].max_packet);
}
read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet);
if (_num_packets_int_out_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_OUT]);
}
}
}
void USBEndpointTester::_cb_int_in()
{
_cnt_cb_int_in++;
write_finish(_endpoints[EP_INT_IN]);
if (_abort_transfer_test == false) {
// Receive more data from the host using the OUT endpoint.
read_start(_endpoints[EP_INT_OUT], _endpoint_buffs[EP_INT_OUT], (*_endpoint_configs)[EP_INT_OUT].max_packet);
} else {
_num_packets_int_in_abort++;
if (_num_packets_int_in_abort >= NUM_PACKETS_UNTIL_ABORT + NUM_PACKETS_AFTER_ABORT) {
return;
}
// Abort the transfer if enough data was sent.
memset(_endpoint_buffs[EP_INT_IN], _num_packets_int_in_abort, (*_endpoint_configs)[EP_INT_IN].max_packet);
write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet);
if (_num_packets_int_in_abort == NUM_PACKETS_UNTIL_ABORT) {
endpoint_abort(_endpoints[EP_INT_IN]);
// Verify that buffer given in write_start is not used after the
// call to endpoint_abort(), by changing the buffer contents.
// The test will fail if the host receives new buffer content.
memset(_endpoint_buffs[EP_INT_IN], FORBIDDEN_PAYLOAD_VALUE, (*_endpoint_configs)[EP_INT_IN].max_packet);
}
}
}
void USBEndpointTester::_cb_iso_out()
{
_cnt_cb_iso_out++;
uint32_t rx_size = read_finish(_endpoints[EP_ISO_OUT]);
// Send data back to host using the IN endpoint.
memset(_endpoint_buffs[EP_ISO_IN], 0, (*_endpoint_configs)[EP_ISO_IN].max_packet);
memcpy(_endpoint_buffs[EP_ISO_IN], _endpoint_buffs[EP_ISO_OUT], rx_size);
write_start(_endpoints[EP_ISO_IN], _endpoint_buffs[EP_ISO_IN], rx_size);
}
void USBEndpointTester::_cb_iso_in()
{
_cnt_cb_iso_in++;
write_finish(_endpoints[EP_ISO_IN]);
// Receive more data from the host using the OUT endpoint.
read_start(_endpoints[EP_ISO_OUT], _endpoint_buffs[EP_ISO_OUT], (*_endpoint_configs)[EP_ISO_OUT].max_packet);
}
void USBEndpointTester::start_ep_in_abort_test()
{
memset(_endpoint_buffs[EP_BULK_IN], 0, (*_endpoint_configs)[EP_BULK_IN].max_packet);
memset(_endpoint_buffs[EP_INT_IN], 0, (*_endpoint_configs)[EP_INT_IN].max_packet);
write_start(_endpoints[EP_BULK_IN], _endpoint_buffs[EP_BULK_IN], (*_endpoint_configs)[EP_BULK_IN].max_packet);
write_start(_endpoints[EP_INT_IN], _endpoint_buffs[EP_INT_IN], (*_endpoint_configs)[EP_INT_IN].max_packet);
}

View File

@ -0,0 +1,134 @@
/*
* 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_ENDPOINT_TESTER_H
#define USB_ENDPOINT_TESTER_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "EventQueue.h"
#include "EventFlags.h"
#include "USBDevice.h"
#define NUM_ENDPOINTS 6 // Not including CTRL OUT/IN
class USBEndpointTester: public USBDevice {
public:
USBEndpointTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release,
bool abort_transfer_test);
virtual ~USBEndpointTester();
const char *get_serial_desc_string();
void start_ep_in_abort_test();
uint32_t get_cnt_cb_set_conf() const
{
return _cnt_cb_set_conf;
}
uint32_t get_cnt_cb_set_intf() const
{
return _cnt_cb_set_intf;
}
uint32_t get_cnt_cb_bulk_out() const
{
return _cnt_cb_bulk_out;
}
uint32_t get_cnt_cb_bulk_in() const
{
return _cnt_cb_bulk_in;
}
uint32_t get_cnt_cb_int_out() const
{
return _cnt_cb_int_out;
}
uint32_t get_cnt_cb_int_in() const
{
return _cnt_cb_int_in;
}
uint32_t get_cnt_cb_iso_out() const
{
return _cnt_cb_iso_out;
}
uint32_t get_cnt_cb_iso_in() const
{
return _cnt_cb_iso_in;
}
struct ep_config_t {
bool dir_in;
usb_ep_type_t type;
uint32_t max_packet;
ep_cb_t callback;
};
protected:
events::EventQueue *queue;
rtos::EventFlags flags;
uint8_t ctrl_buf[2048];
bool _abort_transfer_test;
usb_ep_t _endpoints[NUM_ENDPOINTS];
uint8_t *_endpoint_buffs[NUM_ENDPOINTS];
ep_config_t (*_endpoint_configs)[NUM_ENDPOINTS];
static ep_config_t _intf_config_max[NUM_ENDPOINTS];
static ep_config_t _intf_config0[NUM_ENDPOINTS];
static ep_config_t _intf_config1[NUM_ENDPOINTS];
static ep_config_t _intf_config2[NUM_ENDPOINTS];
static ep_config_t _intf_config3[NUM_ENDPOINTS];
static ep_config_t _intf_config4[NUM_ENDPOINTS];
volatile uint32_t _cnt_cb_set_conf;
volatile uint32_t _cnt_cb_set_intf;
volatile uint32_t _cnt_cb_bulk_out;
volatile uint32_t _cnt_cb_bulk_in;
volatile uint32_t _cnt_cb_int_out;
volatile uint32_t _cnt_cb_int_in;
volatile uint32_t _cnt_cb_iso_out;
volatile uint32_t _cnt_cb_iso_in;
volatile uint32_t _num_packets_bulk_out_abort;
volatile uint32_t _num_packets_bulk_in_abort;
volatile uint32_t _num_packets_int_out_abort;
volatile uint32_t _num_packets_int_in_abort;
virtual const uint8_t *configuration_desc(uint8_t index);
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);
void _setup_non_zero_endpoints();
bool _setup_interface(uint16_t interface, uint8_t alternate);
virtual void _cb_bulk_out();
virtual void _cb_bulk_in();
virtual void _cb_int_out();
virtual void _cb_int_in();
virtual void _cb_iso_out();
virtual void _cb_iso_in();
private:
const char *get_desc_string(const uint8_t *desc);
bool _request_rw_restart(const setup_packet_t *setup);
bool _request_abort_buff_check(const setup_packet_t *setup);
};
#endif

View File

@ -0,0 +1,702 @@
/*
* 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 LAST_CONFIGURATION (2)
#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 VENDOR_TEST_CTRL_IN_SIZES 9
#define VENDOR_TEST_CTRL_OUT_SIZES 10
#define MAX_EP_SIZE 64
#define MIN_EP_SIZE 8
#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());
queue = mbed::mbed_highprio_event_queue();
configuration_desc(0);
init();
USBDevice::connect();
flags.wait_any(EVENT_READY, osWaitForever, false);
}
USBTester::~USBTester()
{
deinit();
}
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;
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;
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;
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));
}

View File

@ -0,0 +1,145 @@
/*
* 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 "EventFlags.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 product_release
*/
USBTester(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
virtual ~USBTester();
/*
*
* @returns descriptor string in ASCII
*/
const char *get_serial_desc_string();
const char *get_iproduct_desc_string();
const char *get_iinterface_desc_string();
uint32_t get_reset_count() const
{
return reset_count;
}
uint32_t get_suspend_count() const
{
return suspend_count;
}
uint32_t get_resume_count() const
{
return resume_count;
}
void clear_reset_count()
{
reset_count = 0;
}
void clear_suspend_count()
{
suspend_count = 0;
}
void clear_resume_count()
{
resume_count = 0;
}
private:
enum { NONE = -1 };
const char *get_desc_string(const uint8_t *desc);
virtual void suspend(bool suspended);
bool set_configuration(uint16_t configuration);
bool set_interface(uint16_t interface, uint16_t alternate);
bool 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)());
void remove_iterface(uint16_t interface);
int16_t interface_0_alt_set;
int16_t interface_1_alt_set;
int16_t configuration_set;
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
*
* @param index descriptor index
* @returns pointer to the configuration descriptor
*/
virtual const uint8_t *configuration_desc(uint8_t index);
protected:
uint8_t bulk_in;
uint8_t bulk_out;
uint8_t bulk_buf[64];
uint8_t int_in;
uint8_t int_out;
uint8_t int_buf[64];
events::EventQueue *queue;
rtos::EventFlags flags;
volatile uint32_t reset_count;
volatile uint32_t suspend_count;
volatile uint32_t resume_count;
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();
virtual void epint_out_callback();
virtual void callback_reset();
uint8_t ctrl_buf[2048];
};
#endif

View File

@ -0,0 +1,660 @@
/*
* 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"
#include "USBEndpointTester.h"
#include "usb_phy_api.h"
// TODO
// suspend resume test: implement host side USB suspend/resume
// sync frame test: add test on isochronous endpoint
// Uncomment/remove this when host suspend_resume_test part will be implemented
//#define SUSPEND_RESUME_TEST_SUPPORTED
// If disconnect() + connect() occur too fast the reset event will be dropped.
// At a minimum there should be a 200us delay between disconnect and connect.
// To be on the safe side I would recommend a 1ms delay, so the host controller
// has an entire USB frame to detect the disconnect.
#define MIN_DISCONNECT_TIME_US 1000
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
#error [NOT_SUPPORTED] USB Device not supported for this target
#endif
using namespace utest::v1;
// Print callback counters for endpoint tests
#define EP_DBG 0
static USBPhy *get_phy()
{
return get_usb_phy();
}
/** Control basic tests
Test device configuration/deconfiguration
Given an initialized USB (HOST <---> DUT connection established)
When device configuration is checked just after initialization
Then get_configuration returns 1 (default configuration is set)
When device is deconfigured
Then get_configuration returns 0 (no configuration is set)
When each from supported configurations is set
Then the configuration is set correctly
Test device interface setting
Given an initialized USB (HOST <---> DUT connection established)
When each altsetting from every supported configuration is set
Then the interface altsetting is set correctly
Test device/interface/endpoint status
Given an initialized USB (HOST <---> DUT connection established)
When device status is checked
Then status is within allowed values (see status bits description below)
When control endpoint status is checked
Then control endpoint status is 0
When status of each interface from every supported configuration is checked
Then interface status is 0
When status of each endpoint in every allowed device interface/configuration combination is checked
Then endpoint status is 0 (not halted)
Test set/clear feature on device/interface/endpoint
Given an initialized USB (HOST <---> DUT connection established)
When for each endpoint in every allowed interface/configuration combination the feature is set and then cleared
Then selected feature is set/cleared accordingly
Test device/configuration/interface/endpoint descriptors
Given an initialized USB (HOST <---> DUT connection established)
When device descriptor is read
Then the descriptor content is valid
When configuration descriptor is read
Then the descriptor content is valid
When interface descriptor is read
Then the error is thrown since it is not directly accessible
When endpoint descriptor is read
Then the error is thrown since it is not directly accessible
Test descriptor setting
Given an initialized USB (HOST <---> DUT connection established)
When device descriptor is to be set
Then error is thrown since descriptor setting command is not supported by Mbed
*/
void control_basic_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
char str[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
sprintf(str, "%s %d %d", serial.get_serial_desc_string(), vendor_id, product_id);
greentea_send_kv("control_basic_test", str);
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test control endpoint stall on invalid request
Given an initialized USB (HOST <---> DUT connection established)
When unsupported request to control endpoint is to be sent
Then the endpoint is stalled and error is thrown
*/
void control_stall_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
greentea_send_kv("control_stall_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test various data sizes in control transfer
Given an initialized USB (HOST <---> DUT connection established)
When control data in each tested size is sent
Then read data should match sent data
*/
void control_sizes_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
greentea_send_kv("control_sizes_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test various patterns of control transfers
Given an initialized USB (HOST <---> DUT connection established)
When stress control transfer with a data in stage is performed
Then transfer ends with success
When stress control transfer with a data out stage followed by a control transfer with a data in stage is performed
Then transfer ends with success
When stress control transfer with a data out stage is performed
Then transfer ends with success
*/
void control_stress_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
greentea_send_kv("control_stress_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test data correctness for every OUT/IN endpoint pair
*
* Given a USB device with multiple OUT/IN endpoint pairs
* When the host sends random payloads up to wMaxPacketSize in size
* to an OUT endpoint of the device,
* and then the device sends data back to host using an IN endpoint
* Then data sent and received by host is equal for every endpoint pair
*/
void ep_test_data_correctness()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false);
greentea_send_kv("ep_test_data_correctness", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test endpoint halt for every OUT/IN endpoint pair
*
* Given a USB device with multiple OUT/IN endpoint pairs
* When the host issues an endpoint halt control request at a random point
* of OUT or IN transfer
* Then the endpoint is stalled and all further transfers fail
*/
void ep_test_halt()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false);
greentea_send_kv("ep_test_halt", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test simultaneous data transfers for multiple OUT/IN endpoint pairs
*
* Given a USB device with multiple OUT/IN endpoint pairs
* When multiple OUT and IN endpoints are used to transfer random data in parallel
* Then all transfers succeed
* and for every endpoint pair, data received by host equals data sent by host
*/
void ep_test_parallel_transfers()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false);
greentea_send_kv("ep_test_parallel_transfers", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test simultaneous data transfers in parallel with control transfers
*
* Given a USB device with multiple OUT/IN endpoint pairs
* When multiple OUT and IN endpoints are used to transfer random data
* and control requests are processed in parallel
* Then all transfers succeed
* and for every endpoint pair, data received by host equals data sent by host
*/
void ep_test_parallel_transfers_ctrl()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false);
greentea_send_kv("ep_test_parallel_transfers_ctrl", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test aborting data transfer for every OUT/IN endpoint pair
*
* Given a USB device with multiple OUT/IN endpoint pairs
* When a device aborts an in progress data transfer
* Then no more data is transmitted
* and endpoint buffer is correctly released on the device end
*/
void ep_test_abort()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, true);
greentea_send_kv("ep_test_abort", serial.get_serial_desc_string());
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test data toggle reset for bulk OUT/IN endpoint pairs
*
* Given a USB device
* When an interface is set
* Then the data toggle bits for all endpoints are reset to DATA0
* When clear feature is called for an endpoint that *IS NOT* stalled
* Then the data toggle is reset to DATA0 for that endpoint
* When clear halt is called for an endpoint that *IS* stalled
* Then the data toggle is reset to DATA0 for that endpoint
*/
void ep_test_data_toggle()
{
uint16_t vendor_id = 0x0d28;
// Use a product ID different than that used in other tests,
// to help Windows hosts use the correct configuration descriptor.
uint16_t product_id = 0x0206;
uint16_t product_release = 0x0001;
char _key[11] = { };
char _value[128] = { };
{
USBEndpointTester serial(get_phy(), vendor_id, product_id, product_release, false);
greentea_send_kv("ep_test_data_toggle", serial.get_serial_desc_string());
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
#if EP_DBG
wait_ms(100);
printf("cnt_cb_set_conf = %lu\r\n", serial.get_cnt_cb_set_conf());
printf("cnt_cb_set_intf = %lu\r\n", serial.get_cnt_cb_set_intf());
printf("cnt_cb_bulk_out = %lu\r\n", serial.get_cnt_cb_bulk_out());
printf("cnt_cb_bulk_in = %lu\r\n", serial.get_cnt_cb_bulk_in());
printf("cnt_cb_int_out = %lu\r\n", serial.get_cnt_cb_int_out());
printf("cnt_cb_int_in = %lu\r\n", serial.get_cnt_cb_int_in());
printf("cnt_cb_iso_out = %lu\r\n", serial.get_cnt_cb_iso_out());
printf("cnt_cb_iso_in = %lu\r\n", serial.get_cnt_cb_iso_in());
#endif
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test USB implementation against repeated reset
Given an initialized USB (HOST <---> DUT connection established)
When USB device is reset repeatedly
Then the USB is operational with no errors
*/
void device_reset_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
greentea_send_kv("reset_support", 0);
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
if (strcmp(_value, "false") != 0) {
USBTester serial(get_phy(), vendor_id, product_id, product_release);
serial.clear_reset_count();
greentea_send_kv("device_reset_test", serial.get_serial_desc_string());
while (serial.get_reset_count() == 0);
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
while (!serial.configured());
serial.clear_reset_count();
greentea_send_kv("device_reset_test", serial.get_serial_desc_string());
while (serial.get_reset_count() == 0);
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
while (!serial.configured());
serial.clear_reset_count();
greentea_send_kv("device_reset_test", serial.get_serial_desc_string());
while (serial.get_reset_count() == 0);
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
while (!serial.configured());
greentea_send_kv("device_reset_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
/** Test USB implementation against repeated reconnection
Given an initialized USB (HOST <---> DUT connection established)
When USB device is disconnected and then connected repeatedly
Then the USB is operational with no errors
*/
void device_soft_reconnection_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
const uint32_t reconnect_try_count = 3;
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
for (int i = 0; i < reconnect_try_count; i++) {
serial.disconnect();
wait_us(MIN_DISCONNECT_TIME_US);
serial.connect();
greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
serial.disconnect();
wait_us(MIN_DISCONNECT_TIME_US);
serial.connect();
serial.disconnect();
wait_us(MIN_DISCONNECT_TIME_US);
serial.connect();
serial.disconnect();
wait_us(MIN_DISCONNECT_TIME_US);
serial.connect();
greentea_send_kv("device_soft_reconnection_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
#if SUSPEND_RESUME_TEST_SUPPORTED
/** Test USB implementation against repeated suspend and resume
Given an initialized USB (HOST <---> DUT connection established)
When USB device is suspended and then resumed repeatedly
Then the USB is operational with no errors
*/
void device_suspend_resume_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
greentea_send_kv("device_suspend_resume_test", serial.get_serial_desc_string());
printf("[1] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count());
serial.clear_suspend_count();
serial.clear_resume_count();
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
printf("[2] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count());
TEST_ASSERT_EQUAL_STRING("pass", _key);
wait_ms(5000);
printf("[3] suspend_count: %d resume_count: %d\n", serial.get_suspend_count(), serial.get_resume_count());
}
}
#endif
/** Test USB implementation against repeated initialization and deinitialization
Given an initialized USB (HOST <---> DUT connection established)
When USB device is deinitialized and then initialized repeatedly
Then the USB is operational with no errors
*/
void repeated_construction_destruction_test()
{
uint16_t vendor_id = 0x0d28;
uint16_t product_id = 0x0205;
uint16_t product_release = 0x0001;
char _key[11] = {};
char _value[128] = {};
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
}
wait_us(MIN_DISCONNECT_TIME_US);
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
}
wait_us(MIN_DISCONNECT_TIME_US);
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
}
wait_us(MIN_DISCONNECT_TIME_US);
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
wait_us(MIN_DISCONNECT_TIME_US);
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
wait_us(MIN_DISCONNECT_TIME_US);
{
USBTester serial(get_phy(), vendor_id, product_id, product_release);
TEST_ASSERT_EQUAL(true, serial.configured());
greentea_send_kv("repeated_construction_destruction_test", serial.get_serial_desc_string());
// Wait for host before terminating
greentea_parse_kv(_key, _value, sizeof(_key), sizeof(_value));
TEST_ASSERT_EQUAL_STRING("pass", _key);
}
}
Case cases[] = {
Case("usb control basic test", control_basic_test),
Case("usb control stall test", control_stall_test),
Case("usb control sizes test", control_sizes_test),
Case("usb control stress test", control_stress_test),
Case("usb device reset test", device_reset_test),
Case("usb soft reconnection test", device_soft_reconnection_test),
#if SUSPEND_RESUME_TEST_SUPPORTED
Case("usb device suspend/resume test", device_suspend_resume_test),
#endif
Case("usb repeated construction destruction test", repeated_construction_destruction_test),
Case("endpoint test data correctness", ep_test_data_correctness),
Case("endpoint test halt", ep_test_halt),
Case("endpoint test parallel transfers", ep_test_parallel_transfers),
Case("endpoint test parallel transfers ctrl", ep_test_parallel_transfers_ctrl),
Case("endpoint test abort", ep_test_abort),
Case("endpoint test data toggle reset", ep_test_data_toggle)
};
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(180, "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);
}

View File

@ -0,0 +1,18 @@
# Generic USB driver installation on Windows machines
In order to run the Mbed OS USB device test suite (`tests-usb_device-*`)
on Windows hosts you need to install generic USB drivers for two test devices.
1. Download *Zadig* application from https://zadig.akeo.ie/.
1. Unplug the Mbed device.
1. Open *Zadig*.
1. Select *Device -> Load Preset Device*.
1. Open `mbed_os-usb_test_device1.cfg`.
1. Choose `libusb-win32 (v1.2.6.0)` driver.
1. Select `Install Driver` and click it.
1. Select *Device -> Load Preset Device*.
1. Open `mbed_os-usb_test_device2.cfg`.
1. Choose `libusb-win32 (v1.2.6.0)` driver.
1. Select `Install Driver` and click it.
1. Close *Zadig*.
1. Plug both device USB interfaces (*DAPLink* and *USB device*).

View File

@ -0,0 +1,6 @@
# Zadig device configuration
# Mbed OS USB test device -- basic tests
[device]
Description = "MBED TEST DEVICE"
VID = 0x0D28
PID = 0x0205

View File

@ -0,0 +1,6 @@
# Zadig device configuration
# Mbed OS USB test device -- endpoint tests
[device]
Description = "USB DEVICE"
VID = 0x0D28
PID = 0x0206

View File

@ -0,0 +1,18 @@
# `udev` rules for Mbed USB CDC device
Before running `tests-usb_device-serial` test suite on Debian-based Linux
distros, make sure to [update the `udev` rules][1] as follows:
```bash
sudo tee /etc/udev/rules.d/99-ttyacms.rules >/dev/null <<EOF
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2013", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="1f00" ATTRS{idProduct}=="2012", ENV{ID_MM_DEVICE_IGNORE}="1"
EOF
sudo udevadm control --reload-rules
```
This will prevent the `ModemManager` daemon from automatically opening the port
and sending the `AT commands`, which it does for every new `/dev/ttyACM` device
registered in system.
[1]: https://linux-tips.com/t/prevent-modem-manager-to-capture-usb-serial-devices/284

View File

@ -0,0 +1,847 @@
/*
* 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 "USBCDC.h"
#include "USBSerial.h"
#define USB_CDC_VID 0x1f00
#define USB_CDC_PID 0x2013
#define USB_SERIAL_VID 0x1f00
#define USB_SERIAL_PID 0x2012
#define MSG_KEY_LEN 24
#define MSG_VALUE_DUMMY "0"
#define MSG_KEY_DEVICE_READY "ready"
#define MSG_KEY_SERIAL_NUMBER "usb_dev_sn"
#define MSG_KEY_PORT_OPEN_WAIT "port_open_wait"
#define MSG_KEY_PORT_OPEN_CLOSE "port_open_close"
#define MSG_KEY_SEND_BYTES_SINGLE "send_single"
#define MSG_KEY_SEND_BYTES_MULTIPLE "send_multiple"
#define MSG_KEY_LOOPBACK "loopback"
#define MSG_KEY_CHANGE_LINE_CODING "change_lc"
#define TX_BUFF_SIZE 32
#define RX_BUFF_SIZE 32
// The size of every data chunk the host sends (for each value from a
// known sequence) during 'CDC RX multiple' test cases is
// HOST_RX_BUFF_SIZE_RATIO times the size of RX_BUFF_SIZE input buffer.
// This way the device has to correctly handle data bigger that its buffer.
#define HOST_RX_BUFF_SIZE_RATIO 64
// A DTR line is used to signal that the host has configured a terminal and
// is ready to transmit and receive data from the USB CDC/Serial device.
// When this test suite is run with the use of a Linux host, a workaround has
// to be used to overcome some platform specific DTR line behavior.
// Every time the serial port file descriptor is opened, the DTR line is
// asserted until the terminal attributes are set.
// As a consequence, the device receives a premature DTR signal with a
// duration of 200-500 us before the correct, long-lasting DTR signal set by
// the host-side test script. (tested on the Linux kernel 4.15.0)
//
// Online references:
// https://github.com/pyserial/pyserial/issues/124#issuecomment-227235402
//
// The solution is to wait for the first DTR spike, ignore it, and wait for
// the correct DTR signal again.
#define LINUX_HOST_DTR_FIX 1
#define LINUX_HOST_DTR_FIX_DELAY_MS 1
#define CDC_LOOPBACK_REPS 1200
#define SERIAL_LOOPBACK_REPS 100
#define USB_RECONNECT_DELAY_MS 1
#define LINE_CODING_STRLEN 13 // 6 + 2 + 1 + 1 + 3 * comma
#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;
typedef struct LineCoding {
// bits per second
int baud;
// 5, 6, 7, 8 or 16
int bits;
// 0 -- None,
// 1 -- Odd,
// 2 -- Even,
// 3 -- Mark,
// 4 -- Space
int parity;
// 0 -- 1 Stop bit,
// 1 -- 1.5 Stop bits,
// 2 -- 2 Stop bits
int stop;
int get_num_diffs(LineCoding const &other) const
{
int diffs = 0;
if (baud != other.baud) {
diffs++;
}
if (bits != other.bits) {
diffs++;
}
if (parity != other.parity) {
diffs++;
}
if (stop != other.stop) {
diffs++;
}
return diffs;
}
} line_coding_t;
line_coding_t default_lc = { 9600, 8, 0, 0 };
// There is no POSIX support for 1.5 stop bits.
// Do not set stop bits to 1.5 to keep tests compatible with all supported
// host systems.
line_coding_t test_codings[] = {
{ 9600, 5, 0, 2 },
{ 4800, 7, 2, 0 },
{ 19200, 8, 0, 2 },
{ 115200, 8, 0, 0 },
{ 38400, 8, 1, 0 },
{ 1200, 8, 0, 0 },
{ 19200, 8, 0, 0 },
{ 2400, 7, 2, 0 },
{ 9600, 8, 0, 0 },
{ 57600, 8, 0, 0 },
};
Mail<line_coding_t, 8> lc_mail;
#define EF_SEND (1ul << 0)
EventFlags event_flags;
/**
* 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 TestUSBCDC: public USBCDC {
private:
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
public:
TestUSBCDC(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001,
const char *serial_number = default_serial_num) :
USBCDC(get_usb_phy(), vendor_id, product_id, product_release)
{
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 ~TestUSBCDC()
{
deinit();
}
virtual const uint8_t *string_iserial_desc()
{
return (const uint8_t *) _serial_num_descriptor;
}
};
class TestUSBSerial: public USBSerial {
private:
uint8_t _serial_num_descriptor[USB_DEV_SN_DESC_SIZE];
public:
TestUSBSerial(uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001,
const char *serial_number = default_serial_num) :
USBSerial(get_usb_phy(), vendor_id, product_id, product_release)
{
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 ~TestUSBSerial()
{
deinit();
}
virtual const uint8_t *string_iserial_desc()
{
return (const uint8_t *) _serial_num_descriptor;
}
};
/** Test CDC USB reconnect
*
* Given the host has successfully opened the port of a USB CDC device
* When the USB device disconnects and connects again
* Then the host is able to successfully open the port again
*/
void test_cdc_usb_reconnect()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
TEST_ASSERT_FALSE(usb_cdc.configured());
TEST_ASSERT_FALSE(usb_cdc.ready());
// Connect the USB device.
usb_cdc.connect();
// Wait for the USB enumeration to complete.
while (!usb_cdc.configured()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_cdc.configured());
TEST_ASSERT_FALSE(usb_cdc.ready());
greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY);
// Wait for the host to open the port.
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
TEST_ASSERT_TRUE(usb_cdc.configured());
TEST_ASSERT_TRUE(usb_cdc.ready());
// Disconnect the USB device.
usb_cdc.disconnect();
TEST_ASSERT_FALSE(usb_cdc.configured());
TEST_ASSERT_FALSE(usb_cdc.ready());
wait_ms(USB_RECONNECT_DELAY_MS);
// Connect the USB device again.
usb_cdc.connect();
// Wait for the USB enumeration to complete.
while (!usb_cdc.configured()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_cdc.configured());
TEST_ASSERT_FALSE(usb_cdc.ready());
greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY);
// Wait for the host to open the port again.
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
TEST_ASSERT_TRUE(usb_cdc.configured());
TEST_ASSERT_TRUE(usb_cdc.ready());
// Disconnect the USB device again.
usb_cdc.disconnect();
TEST_ASSERT_FALSE(usb_cdc.configured());
TEST_ASSERT_FALSE(usb_cdc.ready());
}
/** Test CDC receive single bytes
*
* Given the USB CDC device connected to a host
* When the host transmits a known sequence one byte at a time
* Then every byte received by the device matches the sequence
*/
void test_cdc_rx_single_bytes()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
usb_cdc.connect();
greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
uint8_t buff = 0x01;
for (int expected = 0xff; expected >= 0; expected--) {
TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL));
TEST_ASSERT_EQUAL_UINT8(expected, buff);
}
for (int expected = 0; expected <= 0xff; expected++) {
TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL));
TEST_ASSERT_EQUAL_UINT8(expected, buff);
}
// Wait for the host to close its port.
while (usb_cdc.ready()) {
wait_ms(1);
}
usb_cdc.disconnect();
}
void tx_thread_fun(USBCDC *usb_cdc)
{
uint8_t buff_val = 0;
uint8_t buff[TX_BUFF_SIZE] = { 0 };
while (event_flags.get() & EF_SEND) {
if (!usb_cdc->send(buff, TX_BUFF_SIZE)) {
wait_ms(1);
continue;
}
buff_val++;
memset(buff, buff_val, TX_BUFF_SIZE);
}
}
/** Test CDC receive single bytes concurrently
*
* Given the USB CDC device connected to a host
* When the host transmits a known sequence one byte at a time
* and at the same time the device transmits data to host
* Then every byte received by the device matches the sequence
*/
void test_cdc_rx_single_bytes_concurrent()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
usb_cdc.connect();
greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
Thread tx_thread;
event_flags.set(EF_SEND);
tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc));
uint8_t buff = 0x01;
for (int expected = 0xff; expected >= 0; expected--) {
TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL));
TEST_ASSERT_EQUAL_UINT8(expected, buff);
}
for (int expected = 0; expected <= 0xff; expected++) {
TEST_ASSERT(usb_cdc.receive(&buff, 1, NULL));
TEST_ASSERT_EQUAL_UINT8(expected, buff);
}
event_flags.clear(EF_SEND);
tx_thread.join();
// Wait for the host to close its port.
while (usb_cdc.ready()) {
wait_ms(1);
}
usb_cdc.disconnect();
}
/** Test CDC receive multiple bytes
*
* Given the USB CDC device connected to a host
* When the host transmits chunks of data following a known sequence
* Then every chunk received by the device matches the sequence
*/
void test_cdc_rx_multiple_bytes()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
usb_cdc.connect();
greentea_send_kv(MSG_KEY_SEND_BYTES_MULTIPLE, HOST_RX_BUFF_SIZE_RATIO);
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
uint8_t buff[RX_BUFF_SIZE] = { 0 };
uint8_t expected_buff[RX_BUFF_SIZE] = { 0 };
for (int expected = 0xff; expected >= 0; expected--) {
for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) {
memset(expected_buff, expected, RX_BUFF_SIZE);
TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL));
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE);
}
}
for (int expected = 0; expected <= 0xff; expected++) {
for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) {
memset(expected_buff, expected, RX_BUFF_SIZE);
TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL));
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE);
}
}
// Wait for the host to close its port.
while (usb_cdc.ready()) {
wait_ms(1);
}
usb_cdc.disconnect();
}
/** Test CDC receive multiple bytes concurrently
*
* Given the USB CDC device connected to a host
* When the host transmits chunks of data following a known sequence
* and at the same time the device transmits data to host
* Then every chunk received by the device matches the sequence
*/
void test_cdc_rx_multiple_bytes_concurrent()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
usb_cdc.connect();
greentea_send_kv(MSG_KEY_SEND_BYTES_MULTIPLE, HOST_RX_BUFF_SIZE_RATIO);
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
Thread tx_thread;
event_flags.set(EF_SEND);
tx_thread.start(mbed::callback(tx_thread_fun, &usb_cdc));
uint8_t buff[RX_BUFF_SIZE] = { 0 };
uint8_t expected_buff[RX_BUFF_SIZE] = { 0 };
for (int expected = 0xff; expected >= 0; expected--) {
for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) {
memset(expected_buff, expected, RX_BUFF_SIZE);
TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL));
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE);
}
}
for (int expected = 0; expected <= 0xff; expected++) {
for (int chunk = 0; chunk < HOST_RX_BUFF_SIZE_RATIO; chunk++) {
memset(expected_buff, expected, RX_BUFF_SIZE);
TEST_ASSERT(usb_cdc.receive(buff, RX_BUFF_SIZE, NULL));
TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_buff, buff, RX_BUFF_SIZE);
}
}
event_flags.clear(EF_SEND);
tx_thread.join();
// Wait for the host to close its port.
while (usb_cdc.ready()) {
wait_ms(1);
}
usb_cdc.disconnect();
}
/** Test CDC loopback
*
* Given the USB CDC device connected to a host
* When the device transmits random bytes to host
* and the host transmits them back to the device
* Then every byte received by the device is equal to byte preciously sent
*/
void test_cdc_loopback()
{
TestUSBCDC usb_cdc(USB_CDC_VID, USB_CDC_PID, 1, usb_dev_sn);
usb_cdc.connect();
greentea_send_kv(MSG_KEY_LOOPBACK, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_cdc.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_cdc.wait_ready();
uint8_t rx_buff, tx_buff;
for (int i = 0; i < CDC_LOOPBACK_REPS; i++) {
tx_buff = (uint8_t)(rand() % 0x100);
rx_buff = (uint8_t)(tx_buff + 1);
TEST_ASSERT(usb_cdc.send(&tx_buff, 1));
TEST_ASSERT(usb_cdc.receive(&rx_buff, 1, NULL));
TEST_ASSERT_EQUAL_UINT8(tx_buff, rx_buff);
}
// Wait for the host to close its port.
while (usb_cdc.ready()) {
wait_ms(1);
}
usb_cdc.disconnect();
}
/** Test Serial USB reconnect
*
* Given the host has successfully opened the port of a USB Serial device
* When the USB device disconnects and connects again
* Then the host is able to successfully open the port again
*/
void test_serial_usb_reconnect()
{
TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn);
TEST_ASSERT_FALSE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
// Connect the USB device.
usb_serial.connect();
// Wait for the USB enumeration to complete.
while (!usb_serial.configured()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY);
// Wait for the host to open the port.
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
while (!usb_serial.connected()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_TRUE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
// Disconnect the USB device.
usb_serial.disconnect();
TEST_ASSERT_FALSE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
wait_ms(USB_RECONNECT_DELAY_MS);
// Connect the USB device again.
usb_serial.connect();
// Wait for the USB enumeration to complete.
while (!usb_serial.configured()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
greentea_send_kv(MSG_KEY_PORT_OPEN_WAIT, MSG_VALUE_DUMMY);
// Wait for the host to open the port again.
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
while (!usb_serial.connected()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_TRUE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
// Disconnect the USB device again.
usb_serial.disconnect();
TEST_ASSERT_FALSE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
}
/** Test Serial terminal reopen
*
* Given the host has successfully opened the port of a USB Serial device
* When the host closes its port
* Then the host is able to successfully open the port again
*/
void test_serial_term_reopen()
{
TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn);
usb_serial.connect();
greentea_send_kv(MSG_KEY_PORT_OPEN_CLOSE, MSG_VALUE_DUMMY);
// Wait for the host to open the terminal.
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
while (!usb_serial.connected()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_TRUE(usb_serial.ready());
TEST_ASSERT_TRUE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
// Wait for the host to close the terminal.
while (usb_serial.ready()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.ready());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
greentea_send_kv(MSG_KEY_PORT_OPEN_CLOSE, MSG_VALUE_DUMMY);
// Wait for the host to open the terminal again.
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
while (!usb_serial.connected()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_TRUE(usb_serial.ready());
TEST_ASSERT_TRUE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
// Wait for the host to close the terminal again.
while (usb_serial.ready()) {
wait_ms(1);
}
TEST_ASSERT_TRUE(usb_serial.configured());
TEST_ASSERT_FALSE(usb_serial.ready());
TEST_ASSERT_FALSE(usb_serial.connected());
TEST_ASSERT_EQUAL_INT(0, usb_serial.readable());
usb_serial.disconnect();
}
/** Test Serial getc
*
* Given the USB Serial device connected to a host
* When the host transmits a known sequence one byte at a time
* Then every byte received by the device matches the sequence
*/
void test_serial_getc()
{
TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn);
usb_serial.connect();
greentea_send_kv(MSG_KEY_SEND_BYTES_SINGLE, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_serial.wait_ready();
for (int expected = 0xff; expected >= 0; expected--) {
TEST_ASSERT_EQUAL_INT(expected, usb_serial.getc());
}
for (int expected = 0; expected <= 0xff; expected++) {
TEST_ASSERT_EQUAL_INT(expected, usb_serial.getc());
}
// Wait for the host to close its port.
while (usb_serial.ready()) {
wait_ms(1);
}
usb_serial.disconnect();
}
/** Test Serial printf & scanf
*
* Given the USB Serial device connected to a host
* When the device transmits a formatted string with a random value
* using the printf method
* and the host sends it back to the device
* Then the device can successfully read the value using scanf method
* and the value received is equal value sent
*/
void test_serial_printf_scanf()
{
TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn);
usb_serial.connect();
greentea_send_kv(MSG_KEY_LOOPBACK, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_serial.wait_ready();
static const char fmt[] = "Formatted\nstring %i.";
int tx_val, rx_val, rc;
for (int i = 0; i < SERIAL_LOOPBACK_REPS; i++) {
tx_val = rand();
rx_val = tx_val + 1;
rc = usb_serial.printf(fmt, tx_val);
TEST_ASSERT(rc > 0);
rc = usb_serial.scanf(fmt, &rx_val);
TEST_ASSERT(rc == 1);
TEST_ASSERT_EQUAL_INT(tx_val, rx_val);
}
// Wait for the host to close its port.
while (usb_serial.ready()) {
wait_ms(1);
}
usb_serial.disconnect();
}
void line_coding_changed_cb(int baud, int bits, int parity, int stop)
{
line_coding_t *lc = lc_mail.alloc();
lc->baud = baud;
lc->bits = bits;
lc->parity = parity;
lc->stop = stop;
lc_mail.put(lc);
}
/** Test Serial / CDC line coding change
*
* Given the device transmits a set of line coding params to host
* When the host updates serial port settings
* Then line_coding_changed() callback is called
* and the line coding is set as expected
*/
void test_serial_line_coding_change()
{
TestUSBSerial usb_serial(USB_SERIAL_VID, USB_SERIAL_PID, 1, usb_dev_sn);
usb_serial.connect();
greentea_send_kv(MSG_KEY_CHANGE_LINE_CODING, MSG_VALUE_DUMMY);
#if LINUX_HOST_DTR_FIX
usb_serial.wait_ready();
wait_ms(LINUX_HOST_DTR_FIX_DELAY_MS);
#endif
usb_serial.wait_ready();
usb_serial.attach(line_coding_changed_cb);
size_t num_line_codings = sizeof test_codings / sizeof test_codings[0];
line_coding_t *lc_prev = &default_lc;
line_coding_t *lc_expected = NULL;
line_coding_t *lc_actual = NULL;
int num_expected_callbacks, rc;
for (size_t i = 0; i < num_line_codings; i++) {
lc_expected = &(test_codings[i]);
num_expected_callbacks = lc_prev->get_num_diffs(*lc_expected);
rc = usb_serial.printf("%06i,%02i,%01i,%01i", lc_expected->baud, lc_expected->bits, lc_expected->parity,
lc_expected->stop);
TEST_ASSERT_EQUAL_INT(LINE_CODING_STRLEN, rc);
// The pyserial Python module does not update all line coding params
// at once. It updates params one by one instead, and since every
// update is followed by port reconfiguration we get multiple
// calls to line_coding_changed callback on the device.
while (num_expected_callbacks > 0) {
num_expected_callbacks--;
osEvent event = lc_mail.get();
TEST_ASSERT_EQUAL_UINT32(osEventMail, event.status);
lc_actual = (line_coding_t *) event.value.p;
if (lc_expected->get_num_diffs(*lc_actual) == 0) {
break;
} else if (num_expected_callbacks > 0) {
// Discard lc_actual only if there is still a chance to get new
// set of params.
lc_mail.free(lc_actual);
}
}
TEST_ASSERT_EQUAL_INT(lc_expected->baud, lc_actual->baud);
TEST_ASSERT_EQUAL_INT(lc_expected->bits, lc_actual->bits);
TEST_ASSERT_EQUAL_INT(lc_expected->parity, lc_actual->parity);
TEST_ASSERT_EQUAL_INT(lc_expected->stop, lc_actual->stop);
lc_mail.free(lc_actual);
lc_prev = lc_expected;
}
// Wait for the host to close its port.
while (usb_serial.ready()) {
wait_ms(1);
}
usb_serial.disconnect();
}
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(45, "usb_device_serial");
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("CDC USB reconnect", test_cdc_usb_reconnect),
Case("CDC RX single bytes", test_cdc_rx_single_bytes),
Case("CDC RX single bytes concurrent", test_cdc_rx_single_bytes_concurrent),
Case("CDC RX multiple bytes", test_cdc_rx_multiple_bytes),
Case("CDC RX multiple bytes concurrent", test_cdc_rx_multiple_bytes_concurrent),
Case("CDC loopback", test_cdc_loopback),
Case("Serial USB reconnect", test_serial_usb_reconnect),
Case("Serial terminal reopen", test_serial_term_reopen),
Case("Serial getc", test_serial_getc),
Case("Serial printf/scanf", test_serial_printf_scanf),
Case("Serial line coding change", test_serial_line_coding_change),
};
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
int main()
{
return !Harness::run(specification);
}

View File

@ -22,3 +22,4 @@ pyelftools>=0.24,<=0.25
git+https://github.com/armmbed/manifest-tool.git@v1.4.6
icetea>=1.2.1,<1.3
pycryptodome>=3.7.2,<=3.7.3
pyusb>=1.0.0,<2.0.0

View File

@ -17,9 +17,9 @@
/**
* This file configures the system clock as follows:
*-------------------------------------------------------------------------------------------
* System clock source | 1- PLL_HSE_EXTC / CLOCK_SOURCE_USB=1 | 3- PLL_HSI / CLOCK_SOURCE_USB=1
* System clock source | 1- PLL_HSE_EXTC / DEVICE_USBDEVICE | 3- PLL_HSI / DEVICE_USBDEVICE
* | (external 8 MHz clock) | (internal 8 MHz)
* | 2- PLL_HSE_XTAL / CLOCK_SOURCE_USB=1 |
* | 2- PLL_HSE_XTAL / DEVICE_USBDEVICE |
* | (external 8 MHz xtal) |
*-------------------------------------------------------------------------------------------
* SYSCLK(MHz) | 72 / 72 | 64 / 48
@ -30,8 +30,6 @@
*-------------------------------------------------------------------------------------------
* APB2CLK (MHz) | 72 / 72 | 64 / 48
*-------------------------------------------------------------------------------------------
* USB capable (48 MHz precise clock) | NO / YES | NO / YES
*-------------------------------------------------------------------------------------------
*/
#include "stm32f1xx.h"
@ -164,9 +162,9 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInit;
#endif /* CLOCK_SOURCE_USB */
#endif /* DEVICE_USBDEVICE */
/* Enable HSE oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
@ -193,12 +191,12 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
return 0; // FAIL
}
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
/* USB clock selection */
RCC_PeriphCLKInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
RCC_PeriphCLKInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInit);
#endif /* CLOCK_SOURCE_USB */
#endif /* DEVICE_USBDEVICE */
/* Output clock on MCO1 pin(PA8) for debugging purpose */
//HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSE, RCC_MCODIV_1); // 8 MHz
@ -215,9 +213,9 @@ uint8_t SetSysClock_PLL_HSI(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInit;
#endif /* CLOCK_SOURCE_USB */
#endif /* DEVICE_USBDEVICE */
/* Enable HSI oscillator and activate PLL with HSI as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI | RCC_OSCILLATORTYPE_HSE;
@ -226,21 +224,21 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL12; // 48 MHz (8 MHz/2 * 12)
#else /* CLOCK_SOURCE_USB */
#else /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16; // 64 MHz (8 MHz/2 * 16)
#endif /* CLOCK_SOURCE_USB */
#endif /* DEVICE_USBDEVICE */
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
/* USB clock selection */
RCC_PeriphCLKInit.PeriphClockSelection = RCC_PERIPHCLK_USB;
RCC_PeriphCLKInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInit);
#endif /* CLOCK_SOURCE_USB */
#endif /* DEVICE_USBDEVICE */
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);

View File

@ -104,6 +104,7 @@
* @{
*/
static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum);
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd);
/**
* @}
*/
@ -314,7 +315,6 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
uint32_t i = 0U, ep_intr = 0U, epint = 0U, epnum = 0U;
uint32_t fifoemptymsk = 0U, temp = 0U;
USB_OTG_EPTypeDef *ep;
uint32_t hclk = 120000000U;
/* ensure that we are in device mode */
@ -366,6 +366,11 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
}
}
if (( epint & USB_OTG_DOEPINT_EPDISD) == USB_OTG_DOEPINT_EPDISD)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_EPDISD);
}
if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP)
{
/* Inform the upper layer that a setup packet is available */
@ -596,27 +601,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
/* Handle RxQLevel Interrupt */
if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
PCD_ReadRxFifo(hpcd);
}
/* Handle SOF Interrupt */
@ -1043,6 +1028,84 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
return HAL_OK;
}
/**
* @brief Abort a transaction.
* @param hpcd: PCD handle
* @param ep_addr: endpoint address
* @retval HAL status
*/
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
HAL_StatusTypeDef ret = HAL_OK;
USB_OTG_EPTypeDef *ep;
if ((0x80 & ep_addr) == 0x80)
{
ep = &hpcd->IN_ep[ep_addr & 0x7F];
}
else
{
ep = &hpcd->OUT_ep[ep_addr];
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
USB_EPSetNak(hpcd->Instance, ep);
if ((0x80 & ep_addr) == 0x80)
{
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_OK)
{
ret = USB_FlushTxFifo(hpcd->Instance, ep_addr & 0x7F);
}
}
else
{
/* Set global NAK */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_SGONAK;
/* Read all entries from the fifo so global NAK takes effect */
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
/* Stop the transfer */
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_BUSY)
{
/* If USB_EPStopXfer returns HAL_BUSY then a setup packet
* arrived after the rx fifo was processed but before USB_EPStopXfer
* was called. Process the rx fifo one more time to read the
* setup packet.
*
* Note - after the setup packet has been received no further
* packets will be received over USB. This is because the next
* phase (data or status) of the control transfer started by
* the setup packet will be naked until global nak is cleared.
*/
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
ret = USB_EPStopXfer(hpcd->Instance , ep);
}
/* Clear global nak */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK;
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
return ret;
}
/**
* @brief Set a STALL condition over an endpoint.
* @param hpcd: PCD handle
@ -1256,6 +1319,42 @@ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t
return HAL_OK;
}
/**
* @brief Process the next RX fifo entry
* @param hpcd: PCD handle
* @retval HAL status
*/
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
USB_OTG_EPTypeDef *ep;
uint32_t temp = 0;
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
return HAL_OK;
}
/**
* @}
*/

View File

@ -271,6 +271,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
uint16_t HAL_PCD_EP_GetRxCount(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);

View File

@ -430,7 +430,7 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0U)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U ) |\
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U ) |\
((ep->num) << 22U ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -441,7 +441,7 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0U)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U ) |\
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U ) |\
(USB_OTG_DIEPCTL_SD0PID_SEVNFRM)| (USB_OTG_DOEPCTL_USBAEP));
}
}
@ -462,7 +462,7 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0U)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
((ep->num) << 22U) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -476,7 +476,7 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0U)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
((ep->num) << 22U) | (USB_OTG_DOEPCTL_USBAEP));
debug = (uint32_t)(((uint32_t )USBx) + USB_OTG_OUT_ENDPOINT_BASE + (0U)*USB_OTG_EP_REG_SIZE);
@ -746,6 +746,96 @@ HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeD
return HAL_OK;
}
/**
* @brief USB_EPStoptXfer : stop transfer on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
* @note IN endpoints must have NAK enabled before calling this function
* @note OUT endpoints must have global out NAK enabled before calling this
* function. Furthermore, the RX fifo must be empty or the status
* HAL_BUSY will be returned.
*/
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
HAL_StatusTypeDef ret = HAL_OK;
uint32_t count = 0U;
uint32_t epint, fifoemptymsk;
/* IN endpoint */
if (ep->is_in == 1U)
{
/* EP enable, IN data in FIFO */
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA);
}
/* Clear transfer complete interrupt */
epint = USB_ReadDevInEPInterrupt(USBx, ep->num);
if((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC)
{
CLEAR_IN_EP_INTR(ep->num, USB_OTG_DIEPINT_XFRC);
}
/* Mask fifo empty interrupt */
fifoemptymsk = 0x1U << ep->num;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
}
else /* OUT endpoint */
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
if ((USBx->GINTSTS & USB_OTG_GINTSTS_RXFLVL) == USB_OTG_GINTSTS_RXFLVL)
{
/* Although not mentioned in the Reference Manual, it appears that the
* rx fifo must be empty for an OUT endpoint to be disabled. Typically
* this will happen when setting the global OUT nak (required by Reference
* Manual) as this requires processing the rx fifo. This is not guaranteed
* though, as a setup packet can arrive even while global OUT nak is set.
*
* During testing this event was observed and prevented endpoint disabling
* from completing until the rx fifo was empty. To address this problem
* return HAL_BUSY if the rx fifo is not empty to give higher level code
* a chance to clear the fifo and retry the operation.
*
*/
return HAL_BUSY;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA);
}
/* Clear interrupt */
epint = USB_ReadDevOutEPInterrupt(USBx, ep->num);
if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
{
CLEAR_OUT_EP_INTR(ep->num, USB_OTG_DOEPINT_XFRC);
}
}
return ret;
}
/**
* @brief USB_WritePacket : Writes a packet into the Tx FIFO associated
* with the EP/channel
@ -855,6 +945,82 @@ HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDe
return HAL_OK;
}
/**
* @brief USB_EPSetNak : stop transfer and nak all tokens on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) != USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
/**
* @brief USB_EPSetNak : resume transfer and stop naking on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) == USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) == USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
/**
* @brief USB_StopDevice : Stop the usb device mode
* @param USBx : Selected device

View File

@ -404,11 +404,14 @@ HAL_StatusTypeDef USB_DeactivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EP
HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_DeactivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t dma);
void * USB_ReadPacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *dest, uint16_t len);
HAL_StatusTypeDef USB_EPSetStall(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPClearStall(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_SetDevAddress (USB_OTG_GlobalTypeDef *USBx, uint8_t address);
HAL_StatusTypeDef USB_DevConnect (USB_OTG_GlobalTypeDef *USBx);
HAL_StatusTypeDef USB_DevDisconnect (USB_OTG_GlobalTypeDef *USBx);

View File

@ -17,7 +17,7 @@
/**
* This file configures the system clock as follows:
*-----------------------------------------------------------------------------
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) | CLOCK_SOURCE_USB=1
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) | DEVICE_USBDEVICE=1
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) |
* | 3- USE_PLL_HSI (internal 16 MHz) |
*-----------------------------------------------------------------------------
@ -147,13 +147,13 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4; // VCO input clock = 2 MHz (8 MHz / 4)
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 192; // VCO output clock = 384 MHz (2 MHz * 192)
#else /* CLOCK_SOURCE_USB */
#else /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLN = 200; // VCO output clock = 400 MHz (2 MHz * 200)
#endif /* CLOCK_SOURCE_USB */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on CLOCK_SOURCE_USB)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (CLOCK_SOURCE_USB=1)
#endif /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (DEVICE_USBDEVICE=1)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
@ -201,13 +201,13 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8; // VCO input clock = 2 MHz (16 MHz / 8)
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 192; // VCO output clock = 384 MHz (2 MHz * 192)
#else /* CLOCK_SOURCE_USB */
#else /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLN = 200; // VCO output clock = 400 MHz (2 MHz * 200)
#endif /* CLOCK_SOURCE_USB */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on CLOCK_SOURCE_USB)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (CLOCK_SOURCE_USB=1)
#endif /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (DEVICE_USBDEVICE=1)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}

View File

@ -17,7 +17,7 @@
/**
* This file configures the system clock as follows:
*-----------------------------------------------------------------------------
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) | CLOCK_SOURCE_USB=1
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) | DEVICE_USBDEVICE=1
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) |
* | 3- USE_PLL_HSI (internal 16 MHz) |
*-----------------------------------------------------------------------------
@ -162,13 +162,13 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 4; // VCO input clock = 2 MHz (8 MHz / 4)
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 192; // VCO output clock = 384 MHz (2 MHz * 192)
#else /* CLOCK_SOURCE_USB */
#else /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLN = 200; // VCO output clock = 400 MHz (2 MHz * 200)
#endif /* CLOCK_SOURCE_USB */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on CLOCK_SOURCE_USB)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (CLOCK_SOURCE_USB=1)
#endif /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (DEVICE_USBDEVICE=1)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
@ -216,13 +216,13 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8; // VCO input clock = 2 MHz (16 MHz / 8)
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 192; // VCO output clock = 384 MHz (2 MHz * 192)
#else /* CLOCK_SOURCE_USB */
#else /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLN = 200; // VCO output clock = 400 MHz (2 MHz * 200)
#endif /* CLOCK_SOURCE_USB */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on CLOCK_SOURCE_USB)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (CLOCK_SOURCE_USB=1)
#endif /* DEVICE_USBDEVICE */
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; // PLLCLK = 100 MHz or 96 MHz (depending on DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLQ = 8; // USB clock = 48 MHz (DEVICE_USBDEVICE=1)
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}

View File

@ -18,7 +18,7 @@
* This file configures the system clock as follows:
*-----------------------------------------------------------------------------------
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) |
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | CLOCK_SOURCE_USB=1
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | DEVICE_USBDEVICE=1
* | 3- USE_PLL_HSI (internal 16 MHz clock) |
*-----------------------------------------------------------------------------------
* SYSCLK(MHz) | 180 | 168
@ -152,13 +152,13 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 336;
#else
RCC_OscInitStruct.PLL.PLLN = 360;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
@ -207,13 +207,13 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 168;
#else
RCC_OscInitStruct.PLL.PLLN = 180;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}

View File

@ -18,7 +18,7 @@
* This file configures the system clock as follows:
*-----------------------------------------------------------------------------------
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) |
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | CLOCK_SOURCE_USB=1
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | DEVICE_USBDEVICE=1
* | 3- USE_PLL_HSI (internal 16 MHz clock) |
*-----------------------------------------------------------------------------------
* SYSCLK(MHz) | 180 | 168
@ -152,13 +152,13 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 336;
#else
RCC_OscInitStruct.PLL.PLLN = 360;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
@ -207,13 +207,13 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 168;
#else
RCC_OscInitStruct.PLL.PLLN = 180;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}

View File

@ -18,7 +18,7 @@
* This file configures the system clock as follows:
*-----------------------------------------------------------------------------------
* System clock source | 1- USE_PLL_HSE_EXTC (external 8 MHz clock) |
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | CLOCK_SOURCE_USB=1
* | 2- USE_PLL_HSE_XTAL (external 8 MHz xtal) | DEVICE_USBDEVICE=1
* | 3- USE_PLL_HSI (internal 16 MHz clock) |
*-----------------------------------------------------------------------------------
* SYSCLK(MHz) | 180 | 168
@ -152,13 +152,13 @@ uint8_t SetSysClock_PLL_HSE(uint8_t bypass)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 336;
#else
RCC_OscInitStruct.PLL.PLLN = 360;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}
@ -207,13 +207,13 @@ uint8_t SetSysClock_PLL_HSI(void)
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
#if (CLOCK_SOURCE_USB)
#if (DEVICE_USBDEVICE)
RCC_OscInitStruct.PLL.PLLN = 168;
#else
RCC_OscInitStruct.PLL.PLLN = 180;
#endif
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if CLOCK_SOURCE_USB defined
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; // 180 MHz or 168 MHz if DEVICE_USBDEVICE defined
RCC_OscInitStruct.PLL.PLLQ = 7; // 48 MHz if DEVICE_USBDEVICE defined
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
return 0; // FAIL
}

View File

@ -106,6 +106,7 @@
* @{
*/
static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum);
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd); // MBED PATCH
/**
* @}
*/
@ -394,6 +395,13 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
}
}
// MBED PATCH
if (( epint & USB_OTG_DOEPINT_EPDISD) == USB_OTG_DOEPINT_EPDISD)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_EPDISD);
}
// MBED PATCH
if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP)
{
/* Inform the upper layer that a setup packet is available */
@ -667,27 +675,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
/* Handle RxQLevel Interrupt */
if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
PCD_ReadRxFifo(hpcd); // MBED PATCH
}
/* Handle SOF Interrupt */
@ -1131,6 +1119,86 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
return HAL_OK;
}
// MBED PATCH
/**
* @brief Abort a transaction.
* @param hpcd: PCD handle
* @param ep_addr: endpoint address
* @retval HAL status
*/
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
HAL_StatusTypeDef ret = HAL_OK;
USB_OTG_EPTypeDef *ep;
if ((0x80 & ep_addr) == 0x80)
{
ep = &hpcd->IN_ep[ep_addr & 0x7F];
}
else
{
ep = &hpcd->OUT_ep[ep_addr];
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
USB_EPSetNak(hpcd->Instance, ep);
if ((0x80 & ep_addr) == 0x80)
{
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_OK)
{
ret = USB_FlushTxFifo(hpcd->Instance, ep_addr & 0x7F);
}
}
else
{
/* Set global NAK */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_SGONAK;
/* Read all entries from the fifo so global NAK takes effect */
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
/* Stop the transfer */
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_BUSY)
{
/* If USB_EPStopXfer returns HAL_BUSY then a setup packet
* arrived after the rx fifo was processed but before USB_EPStopXfer
* was called. Process the rx fifo one more time to read the
* setup packet.
*
* Note - after the setup packet has been received no further
* packets will be received over USB. This is because the next
* phase (data or status) of the control transfer started by
* the setup packet will be naked until global nak is cleared.
*/
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
ret = USB_EPStopXfer(hpcd->Instance , ep);
}
/* Clear global nak */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK;
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
return ret;
}
// MBED PATCH
/**
* @brief Set a STALL condition over an endpoint.
* @param hpcd PCD handle
@ -1356,6 +1424,44 @@ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t
return HAL_OK;
}
// MBED PATCH
/**
* @brief Process the next RX fifo entry
* @param hpcd: PCD handle
* @retval HAL status
*/
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
USB_OTG_EPTypeDef *ep;
uint32_t temp = 0;
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
return HAL_OK;
}
// MBED PATCH
/**
* @}
*/

View File

@ -300,6 +300,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); // MBED PATCH
uint16_t HAL_PCD_EP_GetRxCount(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);

View File

@ -450,7 +450,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0U)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
((ep->num) << 22U) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
}
@ -460,7 +461,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0U)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
(USB_OTG_DIEPCTL_SD0PID_SEVNFRM)| (USB_OTG_DOEPCTL_USBAEP));
}
}
@ -481,7 +483,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0U)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18U) |\
((ep->num) << 22U) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -495,7 +498,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0U)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18U) |\
((ep->num) << 22U) | (USB_OTG_DOEPCTL_USBAEP));
debug = (uint32_t)(((uint32_t )USBx) + USB_OTG_OUT_ENDPOINT_BASE + (0U)*USB_OTG_EP_REG_SIZE);
@ -865,6 +869,174 @@ HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeD
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_EPStoptXfer : stop transfer on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
* @note IN endpoints must have NAK enabled before calling this function
* @note OUT endpoints must have global out NAK enabled before calling this
* function. Furthermore, the RX fifo must be empty or the status
* HAL_BUSY will be returned.
*/
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
HAL_StatusTypeDef ret = HAL_OK;
uint32_t count = 0U;
uint32_t epint, fifoemptymsk;
/* IN endpoint */
if (ep->is_in == 1U)
{
/* EP enable, IN data in FIFO */
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA);
}
/* Clear transfer complete interrupt */
epint = USB_ReadDevInEPInterrupt(USBx, ep->num);
if((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC)
{
CLEAR_IN_EP_INTR(ep->num, USB_OTG_DIEPINT_XFRC);
}
/* Mask fifo empty interrupt */
fifoemptymsk = 0x1U << ep->num;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
}
else /* OUT endpoint */
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
if ((USBx->GINTSTS & USB_OTG_GINTSTS_RXFLVL) == USB_OTG_GINTSTS_RXFLVL)
{
/* Although not mentioned in the Reference Manual, it appears that the
* rx fifo must be empty for an OUT endpoint to be disabled. Typically
* this will happen when setting the global OUT nak (required by Reference
* Manual) as this requires processing the rx fifo. This is not guaranteed
* though, as a setup packet can arrive even while global OUT nak is set.
*
* During testing this event was observed and prevented endpoint disabling
* from completing until the rx fifo was empty. To address this problem
* return HAL_BUSY if the rx fifo is not empty to give higher level code
* a chance to clear the fifo and retry the operation.
*
*/
return HAL_BUSY;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA);
}
/* Clear interrupt */
epint = USB_ReadDevOutEPInterrupt(USBx, ep->num);
if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
{
CLEAR_OUT_EP_INTR(ep->num, USB_OTG_DOEPINT_XFRC);
}
}
return ret;
}
/**
* @brief USB_EPSetNak : stop transfer and nak all tokens on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) != USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
/**
* @brief USB_EPSetNak : resume transfer and stop naking on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) == USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) == USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_WritePacket : Writes a packet into the Tx FIFO associated
* with the EP/channel

View File

@ -416,6 +416,9 @@ HAL_StatusTypeDef USB_DeactivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EP
HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_DeactivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t dma);
void * USB_ReadPacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *dest, uint16_t len);

View File

@ -102,6 +102,7 @@
* @{
*/
static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum);
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd); // MBED PATCH
/**
* @}
*/
@ -148,8 +149,9 @@ HAL_StatusTypeDef HAL_PCD_Init(PCD_HandleTypeDef *hpcd)
{
/* Allocate lock resource and initialize it */
hpcd->Lock = HAL_UNLOCKED;
for (i = 0; i < hpcd->Init.dev_endpoints ; i++)
hpcd->EPLock[i].Lock = HAL_UNLOCKED;
for (i = 0; i < hpcd->Init.dev_endpoints ; i++) { // MBED PATCH
hpcd->EPLock[i].Lock = HAL_UNLOCKED;
}
/* Init the low level hardware : GPIO, CLOCK, NVIC... */
HAL_PCD_MspInit(hpcd);
}
@ -300,10 +302,10 @@ __weak void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd)
*/
HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
{
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
USB_DevConnect (hpcd->Instance);
__HAL_PCD_ENABLE(hpcd);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
return HAL_OK;
}
@ -314,11 +316,11 @@ HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
*/
HAL_StatusTypeDef HAL_PCD_Stop(PCD_HandleTypeDef *hpcd)
{
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
__HAL_PCD_DISABLE(hpcd);
USB_StopDevice(hpcd->Instance);
USB_DevDisconnect (hpcd->Instance);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
return HAL_OK;
}
@ -393,6 +395,13 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
}
}
// MBED PATCH
if (( epint & USB_OTG_DOEPINT_EPDISD) == USB_OTG_DOEPINT_EPDISD)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_EPDISD);
}
// MBED PATCH
if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP)
{
/* setup/out transaction management for Core ID >= 310A */
@ -440,7 +449,8 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
if(( epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC)
{
fifoemptymsk = 0x1 << epnum;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED: changed
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED PATCH
CLEAR_IN_EP_INTR(epnum, USB_OTG_DIEPINT_XFRC);
@ -662,25 +672,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
/* Handle RxQLevel Interrupt */
if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
PCD_ReadRxFifo(hpcd); // MBED PATCH
}
/* Handle SOF Interrupt */
@ -988,9 +980,9 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
ep->data_pid_start = 0;
}
__HAL_LOCK(hpcd);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
USB_ActivateEndpoint(hpcd->Instance , ep);
__HAL_UNLOCK(hpcd);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
return ret;
}
@ -1017,9 +1009,9 @@ HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->is_in = (0x80 & ep_addr) != 0;
__HAL_LOCK(hpcd);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
USB_DeactivateEndpoint(hpcd->Instance , ep);
__HAL_UNLOCK(hpcd);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
return HAL_OK;
}
@ -1050,7 +1042,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, u
ep->dma_addr = (uint32_t)pBuf;
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED:added
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x7F) == 0)
{
@ -1061,7 +1053,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, u
USB_EPStartXfer(hpcd->Instance, ep, hpcd->Init.dma_enable);
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: added
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1102,7 +1094,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
ep->dma_addr = (uint32_t)pBuf;
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: added
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x7F) == 0)
{
@ -1113,11 +1105,91 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
USB_EPStartXfer(hpcd->Instance, ep, hpcd->Init.dma_enable);
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: added
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
// MBED PATCH
/**
* @brief Abort a transaction.
* @param hpcd: PCD handle
* @param ep_addr: endpoint address
* @retval HAL status
*/
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
HAL_StatusTypeDef ret = HAL_OK;
USB_OTG_EPTypeDef *ep;
if ((0x80 & ep_addr) == 0x80)
{
ep = &hpcd->IN_ep[ep_addr & 0x7F];
}
else
{
ep = &hpcd->OUT_ep[ep_addr];
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
USB_EPSetNak(hpcd->Instance, ep);
if ((0x80 & ep_addr) == 0x80)
{
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_OK)
{
ret = USB_FlushTxFifo(hpcd->Instance, ep_addr & 0x7F);
}
}
else
{
/* Set global NAK */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_SGONAK;
/* Read all entries from the fifo so global NAK takes effect */
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
/* Stop the transfer */
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_BUSY)
{
/* If USB_EPStopXfer returns HAL_BUSY then a setup packet
* arrived after the rx fifo was processed but before USB_EPStopXfer
* was called. Process the rx fifo one more time to read the
* setup packet.
*
* Note - after the setup packet has been received no further
* packets will be received over USB. This is because the next
* phase (data or status) of the control transfer started by
* the setup packet will be naked until global nak is cleared.
*/
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
ret = USB_EPStopXfer(hpcd->Instance , ep);
}
/* Clear global nak */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK;
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
return ret;
}
// MBED PATCH
/**
* @brief Set a STALL condition over an endpoint.
* @param hpcd PCD handle
@ -1146,8 +1218,7 @@ HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: changed
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
USB_EPSetStall(hpcd->Instance , ep);
if((ep_addr & 0x7F) == 0)
@ -1155,7 +1226,7 @@ HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
USB_EP0_OutStart(hpcd->Instance, hpcd->Init.dma_enable, (uint8_t *)hpcd->Setup);
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: changed
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1188,9 +1259,11 @@ HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: changed
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
USB_EPClearStall(hpcd->Instance , ep);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: changed
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1203,7 +1276,7 @@ HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
*/
HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: changed
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x80) == 0x80)
{
@ -1214,7 +1287,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
USB_FlushRxFifo(hpcd->Instance);
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED: change
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1337,13 +1410,51 @@ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t
if (ep->xfer_count >= ep->xfer_len)
{
fifoemptymsk = 0x1 << epnum;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED: changed
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED PATCH
}
return HAL_OK;
}
// MBED PATCH
/**
* @brief Process the next RX fifo entry
* @param hpcd: PCD handle
* @retval HAL status
*/
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
USB_OTG_EPTypeDef *ep;
uint32_t temp = 0;
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
return HAL_OK;
}
// MBED PATCH
/**
* @}
*/

View File

@ -286,6 +286,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); // MBED PATCH
uint16_t HAL_PCD_EP_GetRxCount(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);

View File

@ -479,7 +479,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -490,7 +491,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
(USB_OTG_DIEPCTL_SD0PID_SEVNFRM)| (USB_OTG_DOEPCTL_USBAEP));
}
}
@ -511,7 +513,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -525,7 +528,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DOEPCTL_USBAEP));
debug = (uint32_t)(((uint32_t )USBx) + USB_OTG_OUT_ENDPOINT_BASE + (0)*USB_OTG_EP_REG_SIZE);
@ -639,7 +643,7 @@ HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDe
/* Enable the Tx FIFO Empty Interrupt for this EP */
if (ep->xfer_len > 0)
{
atomic_set_u32(&USBx_DEVICE->DIEPEMPMSK, 1 << ep->num); // MBED: changed
atomic_set_u32(&USBx_DEVICE->DIEPEMPMSK, 1 << ep->num); // MBED PATCH
}
}
}
@ -793,6 +797,174 @@ HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeD
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_EPStoptXfer : stop transfer on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
* @note IN endpoints must have NAK enabled before calling this function
* @note OUT endpoints must have global out NAK enabled before calling this
* function. Furthermore, the RX fifo must be empty or the status
* HAL_BUSY will be returned.
*/
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
HAL_StatusTypeDef ret = HAL_OK;
uint32_t count = 0U;
uint32_t epint, fifoemptymsk;
/* IN endpoint */
if (ep->is_in == 1U)
{
/* EP enable, IN data in FIFO */
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA);
}
/* Clear transfer complete interrupt */
epint = USB_ReadDevInEPInterrupt(USBx, ep->num);
if((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC)
{
CLEAR_IN_EP_INTR(ep->num, USB_OTG_DIEPINT_XFRC);
}
/* Mask fifo empty interrupt */
fifoemptymsk = 0x1U << ep->num;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
}
else /* OUT endpoint */
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
if ((USBx->GINTSTS & USB_OTG_GINTSTS_RXFLVL) == USB_OTG_GINTSTS_RXFLVL)
{
/* Although not mentioned in the Reference Manual, it appears that the
* rx fifo must be empty for an OUT endpoint to be disabled. Typically
* this will happen when setting the global OUT nak (required by Reference
* Manual) as this requires processing the rx fifo. This is not guaranteed
* though, as a setup packet can arrive even while global OUT nak is set.
*
* During testing this event was observed and prevented endpoint disabling
* from completing until the rx fifo was empty. To address this problem
* return HAL_BUSY if the rx fifo is not empty to give higher level code
* a chance to clear the fifo and retry the operation.
*
*/
return HAL_BUSY;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA);
}
/* Clear interrupt */
epint = USB_ReadDevOutEPInterrupt(USBx, ep->num);
if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
{
CLEAR_OUT_EP_INTR(ep->num, USB_OTG_DOEPINT_XFRC);
}
}
return ret;
}
/**
* @brief USB_EPSetNak : stop transfer and nak all tokens on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) != USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
/**
* @brief USB_EPSetNak : resume transfer and stop naking on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) == USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) == USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_WritePacket : Writes a packet into the Tx FIFO associated
* with the EP/channel
@ -1635,7 +1807,7 @@ HAL_StatusTypeDef USB_HC_StartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_HCTypeDe
/* Write packet into the Tx FIFO. */
USB_WritePacket(USBx, hc->xfer_buff, hc->ch_num, hc->xfer_len, 0);
hc->xfer_count = hc->xfer_len; // MBED: added
hc->xfer_count = hc->xfer_len; // MBED PATCH
}
}

View File

@ -415,6 +415,9 @@ HAL_StatusTypeDef USB_DeactivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EP
HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_DeactivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t dma);
void * USB_ReadPacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *dest, uint16_t len);

View File

@ -113,6 +113,7 @@
*/
#if defined (USB_OTG_FS)
static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t epnum);
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd); // MBED PATCH
#endif /* USB_OTG_FS */
#if defined (USB)
static HAL_StatusTypeDef PCD_EP_ISR_Handler(PCD_HandleTypeDef *hpcd);
@ -162,11 +163,9 @@ HAL_StatusTypeDef HAL_PCD_Init(PCD_HandleTypeDef *hpcd)
{
/* Allocate lock resource and initialize it */
hpcd->Lock = HAL_UNLOCKED;
// Added for MBED PR #3062
for (index = 0; index < hpcd->Init.dev_endpoints ; index++)
hpcd->EPLock[index].Lock = HAL_UNLOCKED;
for (index = 0; index < hpcd->Init.dev_endpoints ; index++) { // MBED PATCH
hpcd->EPLock[index].Lock = HAL_UNLOCKED;
}
/* Init the low level hardware : GPIO, CLOCK, NVIC... */
HAL_PCD_MspInit(hpcd);
}
@ -311,10 +310,10 @@ __weak void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd)
*/
HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
{
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
USB_DevConnect (hpcd->Instance);
__HAL_PCD_ENABLE(hpcd);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
return HAL_OK;
}
@ -325,13 +324,14 @@ HAL_StatusTypeDef HAL_PCD_Start(PCD_HandleTypeDef *hpcd)
*/
HAL_StatusTypeDef HAL_PCD_Stop(PCD_HandleTypeDef *hpcd)
{
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
__HAL_PCD_DISABLE(hpcd);
USB_StopDevice(hpcd->Instance);
USB_DevDisconnect (hpcd->Instance);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
return HAL_OK;
}
#if defined (USB_OTG_FS)
/**
* @brief Handles PCD interrupt request.
@ -431,6 +431,13 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
}
}
// MBED PATCH
if (( epint & USB_OTG_DOEPINT_EPDISD) == USB_OTG_DOEPINT_EPDISD)
{
CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_EPDISD);
}
// MBED PATCH
if(( epint & USB_OTG_DOEPINT_STUP) == USB_OTG_DOEPINT_STUP)
{
/* Inform the upper layer that a setup packet is available */
@ -473,8 +480,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{
fifoemptymsk = 0x1 << epnum;
// Added for MBED PR #3062
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED PATCH
CLEAR_IN_EP_INTR(epnum, USB_OTG_DIEPINT_XFRC);
@ -690,27 +696,7 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
/* Handle RxQLevel Interrupt */
if(__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
PCD_ReadRxFifo(hpcd); // MBED PATCH
}
/* Handle SOF Interrupt */
@ -755,6 +741,86 @@ void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
}
}
// MBED PATCH
/**
* @brief Abort a transaction.
* @param hpcd: PCD handle
* @param ep_addr: endpoint address
* @retval HAL status
*/
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
HAL_StatusTypeDef ret = HAL_OK;
USB_OTG_EPTypeDef *ep;
if ((0x80 & ep_addr) == 0x80)
{
ep = &hpcd->IN_ep[ep_addr & 0x7F];
}
else
{
ep = &hpcd->OUT_ep[ep_addr];
}
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
USB_EPSetNak(hpcd->Instance, ep);
if ((0x80 & ep_addr) == 0x80)
{
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_OK)
{
ret = USB_FlushTxFifo(hpcd->Instance, ep_addr & 0x7F);
}
}
else
{
/* Set global NAK */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_SGONAK;
/* Read all entries from the fifo so global NAK takes effect */
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
/* Stop the transfer */
ret = USB_EPStopXfer(hpcd->Instance , ep);
if (ret == HAL_BUSY)
{
/* If USB_EPStopXfer returns HAL_BUSY then a setup packet
* arrived after the rx fifo was processed but before USB_EPStopXfer
* was called. Process the rx fifo one more time to read the
* setup packet.
*
* Note - after the setup packet has been received no further
* packets will be received over USB. This is because the next
* phase (data or status) of the control transfer started by
* the setup packet will be naked until global nak is cleared.
*/
while (__HAL_PCD_GET_FLAG(hpcd, USB_OTG_GINTSTS_RXFLVL))
{
PCD_ReadRxFifo(hpcd);
}
ret = USB_EPStopXfer(hpcd->Instance , ep);
}
/* Clear global nak */
USBx_DEVICE->DCTL |= USB_OTG_DCTL_CGONAK;
}
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
return ret;
}
// MBED PATCH
#endif /* USB_OTG_FS */
#if defined (USB)
@ -1135,9 +1201,11 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
ep->maxpacket = ep_mps;
ep->type = ep_type;
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
USB_ActivateEndpoint(hpcd->Instance , ep);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
return ret;
}
@ -1165,9 +1233,11 @@ HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->is_in = (0x80 & ep_addr) != 0;
__HAL_LOCK(hpcd);
// MBED PATCH __HAL_LOCK(hpcd);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
USB_DeactivateEndpoint(hpcd->Instance , ep);
__HAL_UNLOCK(hpcd);
// MBED PATCH __HAL_UNLOCK(hpcd);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7FU]); // MBED PATCH
return HAL_OK;
}
@ -1193,8 +1263,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, u
ep->is_in = 0;
ep->num = ep_addr & 0x7F;
// Added for MBED PR #3062
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x7F) == 0 )
{
@ -1205,8 +1274,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, u
USB_EPStartXfer(hpcd->Instance, ep, hpcd->Init.dma_enable);
}
// Added for MBED PR #3062
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1242,9 +1310,8 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
ep->is_in = 1;
ep->num = ep_addr & 0x7F;
// Added for MBED PR #3062
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x7F) == 0 )
{
USB_EP0StartXfer(hpcd->Instance,ep, hpcd->Init.dma_enable);
@ -1254,9 +1321,8 @@ HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr,
USB_EPStartXfer(hpcd->Instance, ep, hpcd->Init.dma_enable);
}
// Added for MBED PR #3062
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1283,8 +1349,7 @@ HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
// Added for MBED PR #3062
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
USB_EPSetStall(hpcd->Instance , ep);
if((ep_addr & 0x7F) == 0)
@ -1292,8 +1357,7 @@ HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
USB_EP0_OutStart(hpcd->Instance, hpcd->Init.dma_enable, (uint8_t *)hpcd->Setup);
}
// Added for MBED PR #3062
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1321,13 +1385,11 @@ HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
ep->num = ep_addr & 0x7F;
ep->is_in = ((ep_addr & 0x80) == 0x80);
// Added for MBED PR #3062
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
USB_EPClearStall(hpcd->Instance , ep);
// Added for MBED PR #3062
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1340,8 +1402,7 @@ HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
*/
HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
{
// Added for MBED PR #3062
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_LOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
if ((ep_addr & 0x80) == 0x80)
{
@ -1352,9 +1413,8 @@ HAL_StatusTypeDef HAL_PCD_EP_Flush(PCD_HandleTypeDef *hpcd, uint8_t ep_addr)
USB_FlushRxFifo(hpcd->Instance);
}
// Added for MBED PR #3062
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]);
__HAL_UNLOCK(&hpcd->EPLock[ep_addr & 0x7F]); // MBED PATCH
return HAL_OK;
}
@ -1465,13 +1525,50 @@ static HAL_StatusTypeDef PCD_WriteEmptyTxFifo(PCD_HandleTypeDef *hpcd, uint32_t
if (ep->xfer_count >= ep->xfer_len)
{
fifoemptymsk = 0x1 << epnum;
// Added for MBED PR #3062
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk); // MBED PATCH
}
return HAL_OK;
}
// MBED PATCH
/**
* @brief Process the next RX fifo entry
* @param hpcd: PCD handle
* @retval HAL status
*/
static HAL_StatusTypeDef PCD_ReadRxFifo(PCD_HandleTypeDef *hpcd)
{
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
USB_OTG_EPTypeDef *ep;
uint32_t temp = 0;
USB_MASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
temp = USBx->GRXSTSP;
ep = &hpcd->OUT_ep[temp & USB_OTG_GRXSTSP_EPNUM];
if(((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_DATA_UPDT)
{
if((temp & USB_OTG_GRXSTSP_BCNT) != 0U)
{
USB_ReadPacket(USBx, ep->xfer_buff, (temp & USB_OTG_GRXSTSP_BCNT) >> 4U);
ep->xfer_buff += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
}
else if (((temp & USB_OTG_GRXSTSP_PKTSTS) >> 17U) == STS_SETUP_UPDT)
{
USB_ReadPacket(USBx, (uint8_t *)hpcd->Setup, 8U);
ep->xfer_count += (temp & USB_OTG_GRXSTSP_BCNT) >> 4U;
}
USB_UNMASK_INTERRUPT(hpcd->Instance, USB_OTG_GINTSTS_RXFLVL);
return HAL_OK;
}
// MBED PATCH
#endif /* USB_OTG_FS */
#if defined (USB)

View File

@ -329,6 +329,7 @@ HAL_StatusTypeDef HAL_PCD_EP_Open(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint
HAL_StatusTypeDef HAL_PCD_EP_Close(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Transmit(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
HAL_StatusTypeDef HAL_PCD_EP_Abort(PCD_HandleTypeDef *hpcd, uint8_t ep_addr); // MBED PATCH
uint16_t HAL_PCD_EP_GetRxCount(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_SetStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);
HAL_StatusTypeDef HAL_PCD_EP_ClrStall(PCD_HandleTypeDef *hpcd, uint8_t ep_addr);

View File

@ -417,7 +417,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -428,7 +429,8 @@ HAL_StatusTypeDef USB_ActivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTy
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
(USB_OTG_DIEPCTL_SD0PID_SEVNFRM)| (USB_OTG_DOEPCTL_USBAEP));
}
}
@ -449,7 +451,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_USBAEP) == 0)
{
USBx_INEP(ep->num)->DIEPCTL |= ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_INEP(ep->num)->DIEPCTL = ((ep->maxpacket & USB_OTG_DIEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DIEPCTL_SD0PID_SEVNFRM) | (USB_OTG_DIEPCTL_USBAEP));
}
@ -463,7 +466,8 @@ HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_USBAEP) == 0)
{
USBx_OUTEP(ep->num)->DOEPCTL |= ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
// MBED PATCH
USBx_OUTEP(ep->num)->DOEPCTL = ((ep->maxpacket & USB_OTG_DOEPCTL_MPSIZ ) | (ep->type << 18 ) |\
((ep->num) << 22 ) | (USB_OTG_DOEPCTL_USBAEP));
debug = (uint32_t)(((uint32_t )USBx) + USB_OTG_OUT_ENDPOINT_BASE + (0)*USB_OTG_EP_REG_SIZE);
@ -711,6 +715,174 @@ HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeD
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_EPStoptXfer : stop transfer on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
* @note IN endpoints must have NAK enabled before calling this function
* @note OUT endpoints must have global out NAK enabled before calling this
* function. Furthermore, the RX fifo must be empty or the status
* HAL_BUSY will be returned.
*/
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
HAL_StatusTypeDef ret = HAL_OK;
uint32_t count = 0U;
uint32_t epint, fifoemptymsk;
/* IN endpoint */
if (ep->is_in == 1U)
{
/* EP enable, IN data in FIFO */
if (((USBx_INEP(ep->num)->DIEPCTL) & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_EPENA) == USB_OTG_DIEPCTL_EPENA);
}
/* Clear transfer complete interrupt */
epint = USB_ReadDevInEPInterrupt(USBx, ep->num);
if((epint & USB_OTG_DIEPINT_XFRC) == USB_OTG_DIEPINT_XFRC)
{
CLEAR_IN_EP_INTR(ep->num, USB_OTG_DIEPINT_XFRC);
}
/* Mask fifo empty interrupt */
fifoemptymsk = 0x1U << ep->num;
atomic_clr_u32(&USBx_DEVICE->DIEPEMPMSK, fifoemptymsk);
}
else /* OUT endpoint */
{
if (((USBx_OUTEP(ep->num)->DOEPCTL) & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA)
{
/* Disable this endpoint */
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_EPDIS;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
if ((USBx->GINTSTS & USB_OTG_GINTSTS_RXFLVL) == USB_OTG_GINTSTS_RXFLVL)
{
/* Although not mentioned in the Reference Manual, it appears that the
* rx fifo must be empty for an OUT endpoint to be disabled. Typically
* this will happen when setting the global OUT nak (required by Reference
* Manual) as this requires processing the rx fifo. This is not guaranteed
* though, as a setup packet can arrive even while global OUT nak is set.
*
* During testing this event was observed and prevented endpoint disabling
* from completing until the rx fifo was empty. To address this problem
* return HAL_BUSY if the rx fifo is not empty to give higher level code
* a chance to clear the fifo and retry the operation.
*
*/
return HAL_BUSY;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_EPENA) == USB_OTG_DOEPCTL_EPENA);
}
/* Clear interrupt */
epint = USB_ReadDevOutEPInterrupt(USBx, ep->num);
if(( epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
{
CLEAR_OUT_EP_INTR(ep->num, USB_OTG_DOEPINT_XFRC);
}
}
return ret;
}
/**
* @brief USB_EPSetNak : stop transfer and nak all tokens on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) != USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) != USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
/**
* @brief USB_EPSetNak : resume transfer and stop naking on this endpoint
* @param USBx : Selected device
* @param ep: pointer to endpoint structure
* @retval HAL status
*/
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep)
{
uint32_t count = 0;
if (ep->is_in == 1U)
{
USBx_INEP(ep->num)->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_INEP(ep->num)->DIEPCTL & USB_OTG_DIEPCTL_NAKSTS) == USB_OTG_DIEPCTL_NAKSTS);
}
else
{
USBx_OUTEP(ep->num)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
count = 0;
do
{
if (++count > 200000U)
{
return HAL_TIMEOUT;
}
}
while ((USBx_OUTEP(ep->num)->DOEPCTL & USB_OTG_DOEPCTL_NAKSTS) == USB_OTG_DOEPCTL_NAKSTS);
}
return HAL_OK;
}
// MBED PATCH
/**
* @brief USB_WritePacket : Writes a packet into the Tx FIFO associated
* with the EP/channel

View File

@ -518,6 +518,9 @@ HAL_StatusTypeDef USB_DeactivateEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EP
HAL_StatusTypeDef USB_ActivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_DeactivateDedicatedEndpoint(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep);
HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_EPStopXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPSetNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EPClearNak(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep); // MBED PATCH
HAL_StatusTypeDef USB_EP0StartXfer(USB_OTG_GlobalTypeDef *USBx , USB_OTG_EPTypeDef *ep, uint8_t dma);
HAL_StatusTypeDef USB_WritePacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *src, uint8_t ch_ep_num, uint16_t len, uint8_t dma);
void * USB_ReadPacket(USB_OTG_GlobalTypeDef *USBx, uint8_t *dest, uint16_t len);

View File

@ -600,7 +600,8 @@
"SPISLAVE",
"STDIO_MESSAGES",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"release_versions": ["2", "5"],
"device_name": "LPC1768",
@ -647,7 +648,8 @@
"SPISLAVE",
"STDIO_MESSAGES",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"release_versions": ["2", "5"],
"device_name": "LPC1768",
@ -1490,7 +1492,8 @@
"STDIO_MESSAGES",
"STORAGE",
"TRNG",
"FLASH"
"FLASH",
"USBDEVICE"
],
"features": ["STORAGE"],
"release_versions": ["2", "5"],
@ -2182,11 +2185,6 @@
"help": "Mask value : USE_PLL_HSE_EXTC (SYSCLK=72 MHz) | USE_PLL_HSE_XTAL (need HW patch) | USE_PLL_HSI (SYSCLK=64 MHz)",
"value": "USE_PLL_HSE_EXTC|USE_PLL_HSI",
"macro_name": "CLOCK_SOURCE"
},
"clock_source_usb": {
"help": "In case of HSI clock source, to get 48 Mhz USB, SYSCLK has to be reduced from 64 to 48 MHz (set 0 for the max SYSCLK value)",
"value": "0",
"macro_name": "CLOCK_SOURCE_USB"
}
},
"detect_code": ["0700"],
@ -2226,7 +2224,8 @@
"SERIAL_ASYNCH",
"FLASH",
"TRNG",
"MPU"
"MPU",
"USBDEVICE"
],
"device_has_remove": ["LPTICKER"],
"release_versions": ["2", "5"],
@ -2437,11 +2436,6 @@
"help": "Mask value : USE_PLL_HSE_EXTC | USE_PLL_HSE_XTAL (need HW patch) | USE_PLL_HSI",
"value": "USE_PLL_HSE_EXTC|USE_PLL_HSI",
"macro_name": "CLOCK_SOURCE"
},
"clock_source_usb": {
"help": "As 48 Mhz clock is configured for USB, SYSCLK has to be reduced from 100 to 96 MHz (set 0 for the max SYSCLK value)",
"value": "0",
"macro_name": "CLOCK_SOURCE_USB"
}
},
"macros_add": ["USB_STM_HAL", "USBHOST_OTHER"],
@ -2473,7 +2467,8 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"release_versions": ["2", "5"],
"device_name": "STM32F412ZG",
@ -2602,7 +2597,8 @@
"TRNG",
"FLASH",
"QSPI",
"MPU"
"MPU",
"USBDEVICE"
],
"bootloader_supported": true,
"release_versions": ["2", "5"],
@ -2642,7 +2638,8 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"bootloader_supported": true,
"release_versions": ["2", "5"],
@ -2676,11 +2673,6 @@
"help": "Mask value : USE_PLL_HSE_EXTC | USE_PLL_HSE_XTAL (need HW patch) | USE_PLL_HSI",
"value": "USE_PLL_HSE_EXTC|USE_PLL_HSI",
"macro_name": "CLOCK_SOURCE"
},
"clock_source_usb": {
"help": "As 48 Mhz clock is configured for USB, SYSCLK has to be reduced from 180 to 168 MHz (set 0 for the max SYSCLK value)",
"value": "1",
"macro_name": "CLOCK_SOURCE_USB"
}
},
"extra_labels_add": [
@ -2705,7 +2697,8 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"detect_code": ["0796"],
"release_versions": ["2", "5"],
@ -2729,11 +2722,6 @@
"help": "Mask value : USE_PLL_HSE_EXTC | USE_PLL_HSE_XTAL (need HW patch) | USE_PLL_HSI",
"value": "USE_PLL_HSE_EXTC|USE_PLL_HSI",
"macro_name": "CLOCK_SOURCE"
},
"clock_source_usb": {
"help": "As 48 Mhz clock is configured for USB, SYSCLK has to be reduced from 180 to 168 MHz (set 0 for the max SYSCLK value)",
"value": "1",
"macro_name": "CLOCK_SOURCE_USB"
}
},
"extra_labels_add": [
@ -2810,7 +2798,8 @@
"CAN",
"SERIAL_ASYNCH",
"FLASH",
"MPU"
"MPU",
"USBDEVICE"
],
"release_versions": ["2", "5"],
"device_name": "STM32F446ZE"
@ -2871,6 +2860,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -2922,6 +2912,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -2976,6 +2967,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -3649,11 +3641,6 @@
"help": "Mask value : USE_PLL_HSE_EXTC (need HW patch) | USE_PLL_HSE_XTAL | USE_PLL_HSI",
"value": "USE_PLL_HSE_XTAL|USE_PLL_HSI",
"macro_name": "CLOCK_SOURCE"
},
"clock_source_usb": {
"help": "As 48 Mhz clock is configured for USB, SYSCLK has to be reduced from 180 to 168 MHz (set 0 for the max SYSCLK value)",
"value": "1",
"macro_name": "CLOCK_SOURCE_USB"
}
},
"overrides": { "lse_available": 0 },
@ -3697,7 +3684,8 @@
"TRNG",
"FLASH",
"QSPI",
"MPU"
"MPU",
"USBDEVICE"
],
"release_versions": ["2", "5"],
"device_name": "STM32F469NI",
@ -3830,6 +3818,7 @@
"TRNG",
"FLASH",
"QSPI",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -3880,6 +3869,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU",
"QSPI"
],
@ -3921,6 +3911,7 @@
"TRNG",
"FLASH",
"QSPI",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -3985,6 +3976,7 @@
"TRNG",
"FLASH",
"QSPI",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -5203,7 +5195,7 @@
"supported_form_factors": ["ARDUINO"],
"extra_labels_add": ["RZA1H", "MBRZA1H", "RZ_A1_EMAC"],
"components_add": ["SD"],
"device_has_add": ["EMAC", "FLASH", "LPTICKER"],
"device_has_add": ["USBDEVICE", "EMAC", "FLASH", "LPTICKER"],
"release_versions": ["2", "5"],
"device_name": "R7S72100",
"bootloader_supported": true
@ -5220,7 +5212,7 @@
"supported_form_factors": ["ARDUINO"],
"extra_labels_add": ["RZA1UL", "MBRZA1LU"],
"components_add": ["SD"],
"device_has_add": ["TRNG", "FLASH", "LPTICKER"],
"device_has_add": ["USBDEVICE", "TRNG", "FLASH", "LPTICKER"],
"device_has_remove": ["ETHERNET"],
"release_versions": ["2", "5"],
"device_name": "R7S72103",
@ -7394,6 +7386,7 @@
"TRNG",
"FLASH",
"MPU",
"USBDEVICE",
"QSPI"
],
"release_versions": ["2", "5"],
@ -7428,6 +7421,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],
@ -7466,6 +7460,7 @@
"SERIAL_ASYNCH",
"TRNG",
"FLASH",
"USBDEVICE",
"MPU"
],
"release_versions": ["2", "5"],

View File

@ -27,7 +27,7 @@ from argparse import ArgumentParser
################################################################################
# Configure builds here
# "libs" can contain "dsp", "usb"
# "libs" can contain "dsp"
build_list = [
{
@ -44,29 +44,29 @@ build_list = [
{ "target": "NUCLEO_F072RB", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F091RC", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F103RB", "toolchains": "GCC_ARM" },
{ "target": "NUCLEO_F207ZG", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F207ZG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F302R8", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F303K8", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F303RE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F303ZE", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F303ZE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F334R8", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F401RE", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F401RE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "STEVAL_3DP001V1", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F410RB", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F411RE", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F411RE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F412ZG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F413ZH", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F413ZH", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L432KC", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "MTB_ADV_WISE_1510", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L476RG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L011K4", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L031K6", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L073RZ", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F429ZI", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F446RE", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F446ZE", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F746ZG", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F767ZI", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUCLEO_F429ZI", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F446RE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F446ZE", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F746ZG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_F767ZI", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUCLEO_L496ZG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "MOTE_L152RC", "toolchains": "GCC_ARM", "libs": ["dsp"] },
@ -83,15 +83,15 @@ build_list = [
{ "target": "DISCO_F334C8", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F401VC", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F407VG", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "DISCO_F407VG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F413ZH", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F429ZI", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F469NI", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F746NG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_F769NI", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_L475VG_IOT01A", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "DISCO_L476VG", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "DISCO_L072CZ_LRWAN1", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "DISCO_L475VG_IOT01A", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_L476VG", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "DISCO_L072CZ_LRWAN1", "toolchains": "GCC_ARM", "libs": ["dsp"] },
# module manufacturer : muRata
{ "target": "MTB_MURATA_ABZ", "toolchains": "GCC_ARM", "libs": [] },
@ -101,7 +101,7 @@ build_list = [
{
"NXP":
(
{ "target": "LPC1768", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "LPC1768", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "LPC11U24", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "OC_MBUINO", "toolchains": "GCC_ARM", "libs": [] },
@ -114,15 +114,15 @@ build_list = [
{ "target": "LPC11U37H_401", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "LPC1549", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "KL05Z", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "KL25Z", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "KL27Z", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "KL43Z", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "KL46Z", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "KL25Z", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "KL27Z", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "KL43Z", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "KL46Z", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "K20D50M", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "TEENSY3_1", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "K64F", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "K22F", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "LPC4088", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "K64F", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "K22F", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "LPC4088", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "ARCH_PRO", "toolchains": "GCC_ARM", "libs": ["dsp"] },
)
},
@ -141,10 +141,10 @@ build_list = [
"SILICON_LABS":
(
{ "target": "EFM32ZG_STK3200", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "EFM32HG_STK3400", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "EFM32LG_STK3600", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "EFM32GG_STK3700", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "EFM32WG_STK3800", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "EFM32HG_STK3400", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "EFM32LG_STK3600", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "EFM32GG_STK3700", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "EFM32WG_STK3800", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "EFM32PG_STK3401", "toolchains": "GCC_ARM", "libs": ["dsp"] },
)
},
@ -163,9 +163,9 @@ build_list = [
{
"NUVOTON":
(
{ "target": "NUMAKER_PFM_NUC472", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUMAKER_PFM_M453", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUMAKER_PFM_M487", "toolchains": "GCC_ARM", "libs": ["dsp", "usb"] },
{ "target": "NUMAKER_PFM_NUC472", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUMAKER_PFM_M453", "toolchains": "GCC_ARM", "libs": ["dsp"] },
{ "target": "NUMAKER_PFM_M487", "toolchains": "GCC_ARM", "libs": ["dsp"] },
)
},
@ -188,25 +188,21 @@ linking_list = [
{"target": "LPC1768",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_15", "MBED_16", "MBED_17"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "K64F",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "K22F",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "KL43Z",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
)
@ -217,25 +213,21 @@ linking_list = [
{"target": "NUCLEO_F446RE",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F446ZE",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F401RE",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F411RE",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F412ZG",
@ -246,31 +238,26 @@ linking_list = [
{"target": "NUCLEO_F413ZH",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F429ZI",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F207ZG",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F746ZG",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_F767ZI",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUCLEO_L476RG",
@ -286,7 +273,6 @@ linking_list = [
{"target": "DISCO_F407VG",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "DISCO_F413ZH",
@ -297,19 +283,16 @@ linking_list = [
{"target": "NUCLEO_F303ZE",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "DISCO_L475VG_IOT01A",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "DISCO_L476VG",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "DISCO_L072CZ_LRWAN1",
@ -329,19 +312,16 @@ linking_list = [
{"target": "NUMAKER_PFM_NUC472",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUMAKER_PFM_M453",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
},
{"target": "NUMAKER_PFM_M487",
"toolchains": "GCC_ARM",
"tests": {"" : ["MBED_2", "MBED_10", "MBED_11", "MBED_16"],
"usb" : ["USB_1", "USB_2" ,"USB_3"],
}
}
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBAudio_H
#define USBAudio_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "Callback.h"
#include "OperationList.h"
#include "ByteBuffer.h"
#include "rtos/EventFlags.h"
/**
* USBAudio example
*
* @code
* #include "mbed.h"
* #include "USBAudio.h"
*
* // Audio loopback example use:
* // 1. Select "Mbed Audio" as your sound device
* // 2. Play a song or audio file
* // 3. Record the output using a program such as Audacity
*
* int main() {
*
* USBAudio audio(true, 44100, 2, 44100, 2);
*
* printf("Looping audio\r\n");
* static uint8_t buf[128];
* while (true) {
* if (!audio.read(buf, sizeof(buf))) {
* memset(buf, 0, sizeof(buf));
* }
* audio.write(buf, sizeof(buf));
* }
* }
* @endcode
*/
class USBAudio: protected USBDevice {
public:
enum AudioEvent {
Start,
Transfer,
End
};
/**
* Basic constructor
*
* Construct this object optionally connecting.
*
* @note Do not use this constructor in derived classes.
*
* @param connect Call connect on initialization
* @param frequency_rx frequency in Hz (default: 48000)
* @param channel_count_rx channel number (1 or 2) (default: 1)
* @param frequency_tx frequency in Hz (default: 8000)
* @param channel_count_tx channel number (1 or 2) (default: 1)
* @param buffer_ms time audio can be buffered without overflowing in milliseconds
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBAudio(bool connect = true, uint32_t frequency_rx = 48000, uint8_t channel_count_rx = 1, uint32_t frequency_tx = 8000, uint8_t channel_count_tx = 1, uint32_t buffer_ms = 10, uint16_t vendor_id = 0x7bb8, uint16_t product_id = 0x1111, uint16_t product_release = 0x0100);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param frequency_rx frequency in Hz (default: 48000)
* @param channel_count_rx channel number (1 or 2) (default: 1)
* @param frequency_tx frequency in Hz (default: 8000)
* @param channel_count_tx channel number (1 or 2) (default: 1)
* @param buffer_ms time audio can be buffered without overflowing in milliseconds
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBAudio(USBPhy *phy, uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBAudio();
/**
* Connect USBAudio
*/
void connect();
/**
* Disconnect USBAudio
*
* This unblocks all calls to read_ready and write_ready.
*/
void disconnect();
/**
* Read audio data
*
* @param buf pointer on a buffer which will be filled with audio data
* @param size size to read
*
* @returns true if successful
*/
bool read(uint8_t *buf, uint32_t size);
/**
* Nonblocking audio data read
*
* Read the available audio data.
*
* @param buf pointer on a buffer which will be filled with audio data
* @param size size to read
* @param actual size actually read
* @note This function is safe to call from USBAudio callbacks.
*/
void read_nb(uint8_t *buf, uint32_t size, uint32_t *actual);
/**
* Return the number read packets dropped due to overflow
*
* @param clear Reset the overflow count back to 0
* @return Number of packets dropped due to overflow
*/
uint32_t read_overflows(bool clear = false);
/**
* Check if the audio read channel is open
*
* @return true if the audio read channel open, false otherwise
*/
bool read_ready();
/**
* Wait until the audio read channel is open
*/
void read_wait_ready();
/**
* Write audio data
*
* @param buf pointer to audio data to write
* @param size size to write
*
* @returns true if successful
*/
bool write(uint8_t *buf, uint32_t size);
/**
* Nonblocking audio data write
*
* Write the available audio data.
*
* @param buf pointer to audio data to write
* @param size size to write
* @param actual actual size written
* @note This function is safe to call from USBAudio callbacks.
*/
void write_nb(uint8_t *buf, uint32_t size, uint32_t *actual);
/**
* Return the number write packets not sent due to underflow
*
* @param clear Reset the underflow count back to 0
* @return Number of packets that should have been
* sent but weren't due to overflow
*/
uint32_t write_underflows(bool clear = false);
/**
* Check if the audio write channel is open
*
* @return true if the audio write channel open, false otherwise
*/
bool write_ready();
/**
* Wait until the audio write channel is open
*/
void write_wait_ready();
/**
* Get current volume between 0.0 and 1.0
*
* @returns volume
*/
float get_volume();
/** Attach a Callback to update the volume
*
* @param cb Callback to attach
*
*/
void attach(mbed::Callback<void()> &cb);
/** attach a Callback to Tx Done
*
* @param cb Callback to attach
*
*/
void attach_tx(mbed::Callback<void(AudioEvent)> &cb);
/** attach a Callback to Rx Done
*
* @param cb Callback to attach
*
*/
void attach_rx(mbed::Callback<void(AudioEvent)> &cb);
protected:
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 const uint8_t *string_iproduct_desc();
virtual const uint8_t *string_iinterface_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
private:
class AsyncWrite;
class AsyncRead;
enum ChannelState {
Powerdown,
Closed,
Opened
};
void _init(uint32_t frequency_rx, uint8_t channel_count_rx, uint32_t frequency_tx, uint8_t channel_count_tx, uint32_t buffer_ms);
/*
* Call to rebuild the configuration descriptor
*
* This function should be called on creation or when any
* value that is part of the configuration descriptor
* changes.
* @note This function uses ~200 bytes of stack so
* make sure your stack is big enough for it.
*/
void _build_configuration_desc();
void _receive_change(ChannelState new_state);
void _receive_isr();
void _send_change(ChannelState new_state);
void _send_isr_start();
void _send_isr_next_sync();
void _send_isr();
// has connect been called
bool _connected;
// audio volume
float _volume;
// mute state
uint8_t _mute;
// Volume Current Value
uint16_t _vol_cur;
// Volume Minimum Value
uint16_t _vol_min;
// Volume Maximum Value
uint16_t _vol_max;
// Volume Resolution
uint16_t _vol_res;
// callback to update volume
mbed::Callback<void()> _update_vol;
// callback transmit Done
mbed::Callback<void(AudioEvent)> _tx_done;
// callback receive Done
mbed::Callback<void(AudioEvent)> _rx_done;
// Number of times data was dropped due to an overflow
uint32_t _rx_overflow;
// Number of times data was not sent due to an underflow
uint32_t _tx_underflow;
// frequency in Hz
uint32_t _tx_freq;
uint32_t _rx_freq;
// mono, stereo,...
uint8_t _rx_channel_count;
uint8_t _tx_channel_count;
bool _tx_idle;
uint16_t _tx_frame_fract;
uint16_t _tx_whole_frames_per_xfer;
uint16_t _tx_fract_frames_per_xfer;
// size of the maximum packet for the isochronous endpoint
uint16_t _tx_packet_size_max;
uint16_t _rx_packet_size_max;
// Buffer used for the isochronous transfer
uint8_t *_tx_packet_buf;
uint8_t *_rx_packet_buf;
// Holding buffer
ByteBuffer _tx_queue;
ByteBuffer _rx_queue;
// State of the audio channels
ChannelState _tx_state;
ChannelState _rx_state;
// sample - a single PCM audio sample
// frame - a group of samples from each channel
// packet - a group of frames sent over USB in one transfer
// Blocking primitives
OperationList<AsyncWrite> _write_list;
OperationList<AsyncRead> _read_list;
rtos::EventFlags _flags;
// endpoint numbers
usb_ep_t _episo_out; // rx endpoint
usb_ep_t _episo_in; // tx endpoint
// channel config in the configuration descriptor: master, left, right
uint8_t _channel_config_rx;
uint8_t _channel_config_tx;
// configuration descriptor
uint8_t _config_descriptor[183];
// buffer for control requests
uint8_t _control_receive[2];
};
#endif

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBAUDIO_TYPES_H
#define USBAUDIO_TYPES_H
#define DEFAULT_CONFIGURATION (1)
// Audio Request Codes
#define REQUEST_SET_CUR 0x01
#define REQUEST_GET_CUR 0x81
#define REQUEST_SET_MIN 0x02
#define REQUEST_GET_MIN 0x82
#define REQUEST_SET_MAX 0x03
#define REQUEST_GET_MAX 0x83
#define REQUEST_SET_RES 0x04
#define REQUEST_GET_RES 0x84
#define MUTE_CONTROL 0x01
#define VOLUME_CONTROL 0x02
// Audio Descriptor Sizes
#define CONTROL_INTERFACE_DESCRIPTOR_LENGTH 0x09
#define STREAMING_INTERFACE_DESCRIPTOR_LENGTH 0x07
#define INPUT_TERMINAL_DESCRIPTOR_LENGTH 0x0C
#define OUTPUT_TERMINAL_DESCRIPTOR_LENGTH 0x09
#define FEATURE_UNIT_DESCRIPTOR_LENGTH 0x09
#define STREAMING_ENDPOINT_DESCRIPTOR_LENGTH 0x07
// Audio Format Type Descriptor Sizes
#define FORMAT_TYPE_I_DESCRIPTOR_LENGTH 0x0b
#define AUDIO_CLASS 0x01
#define SUBCLASS_AUDIOCONTROL 0x01
#define SUBCLASS_AUDIOSTREAMING 0x02
// Audio Descriptor Types
#define INTERFACE_DESCRIPTOR_TYPE 0x24
#define ENDPOINT_DESCRIPTOR_TYPE 0x25
// Audio Control Interface Descriptor Subtypes
#define CONTROL_HEADER 0x01
#define CONTROL_INPUT_TERMINAL 0x02
#define CONTROL_OUTPUT_TERMINAL 0x03
#define CONTROL_FEATURE_UNIT 0x06
// USB Terminal Types
#define TERMINAL_USB_STREAMING 0x0101
// Predefined Audio Channel Configuration Bits
// Mono
#define CHANNEL_M 0x0000
#define CHANNEL_L 0x0001 /* Left Front */
#define CHANNEL_R 0x0002 /* Right Front */
// Feature Unit Control Bits
#define CONTROL_MUTE 0x0001
#define CONTROL_VOLUME 0x0002
// Input Terminal Types
#define TERMINAL_MICROPHONE 0x0201
// Output Terminal Types
#define TERMINAL_SPEAKER 0x0301
#define TERMINAL_HEADPHONES 0x0302
// Audio Streaming Interface Descriptor Subtypes
#define STREAMING_GENERAL 0x01
#define STREAMING_FORMAT_TYPE 0x02
// Audio Data Format Type I Codes
#define FORMAT_PCM 0x0001
// Audio Format Types
#define FORMAT_TYPE_I 0x01
// Audio Endpoint Descriptor Subtypes
#define ENDPOINT_GENERAL 0x01
#endif

View File

@ -0,0 +1,132 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "EndpointResolver.h"
static uint32_t logical_to_index(uint32_t logical, bool in_not_out)
{
return (logical << 1) | (in_not_out ? 1 : 0);
}
static uint32_t index_to_logical(uint32_t index)
{
return index >> 1;
}
EndpointResolver::EndpointResolver(const usb_ep_table_t *table) : _table(table), _cost(0), _used(0), _valid(true)
{
// Do nothing
}
EndpointResolver::~EndpointResolver()
{
// Do nothing
}
void EndpointResolver::endpoint_ctrl(uint32_t size)
{
endpoint_in(USB_EP_TYPE_CTRL, size);
endpoint_out(USB_EP_TYPE_CTRL, size);
}
usb_ep_t EndpointResolver::next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size)
{
int index = next_index(type, in_not_out);
if (index < 0) {
_valid = false;
return 0;
}
const usb_ep_entry_t &entry = _table->table[index_to_logical(index)];
_cost += entry.base_cost + entry.byte_cost * size;
_used |= 1 << index;
return index_to_endpoint(index);
}
usb_ep_t EndpointResolver::endpoint_in(usb_ep_type_t type, uint32_t size)
{
return next_free_endpoint(true, type, size);
}
usb_ep_t EndpointResolver::endpoint_out(usb_ep_type_t type, uint32_t size)
{
return next_free_endpoint(false, type, size);
}
bool EndpointResolver::valid()
{
return _valid && (_cost <= _table->resources);
}
void EndpointResolver::reset()
{
_cost = 0;
_used = 0;
_valid = true;
}
usb_ep_t EndpointResolver::index_to_endpoint(int index)
{
return index_to_logical(index) | ((index & 1) ? 0x80 : 0);
}
int EndpointResolver::next_index(usb_ep_type_t type, bool in_not_out)
{
for (int logical = 0; logical < (int)(sizeof(_table->table) / sizeof(_table->table[0])); logical++) {
uint32_t index = logical_to_index(logical, in_not_out);
uint32_t other = logical_to_index(logical, !in_not_out);
const usb_ep_entry_t &entry = _table->table[logical];
usb_ep_attr_t dir = entry.attributes & USB_EP_ATTR_DIR_MASK;
bool in_allowed = dir != USB_EP_ATTR_DIR_OUT;
bool out_allowed = dir != USB_EP_ATTR_DIR_IN;
bool shared = dir == USB_EP_ATTR_DIR_IN_OR_OUT;
if (!(entry.attributes & (1 << type))) {
// This type is not supported
continue;
}
if (in_not_out && !in_allowed) {
// In endpoint not supported
continue;
}
if (!in_not_out && !out_allowed) {
// Out endpoint not supported
continue;
}
if (_used & (1 << index)) {
// This endpoint is in use
continue;
}
if (shared && (_used & (1 << other))) {
// This endpoint can only be one direction at a time and is in
// use by the other direction
continue;
}
return index;
}
// Not found
return -1;
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 ENDPOINT_RESOLVER_H
#define ENDPOINT_RESOLVER_H
#include "USBPhy.h"
/**
* Utility class for resolving endpoints
*
* This class is intended to make the process of
* selecting the correct endpoint from a device endpoint
* table easier. It also provides a verification function
* to check if the device has enough resources for the
* given configuration.
*
* @ingroup usb_device_core
*/
class EndpointResolver {
public:
EndpointResolver(const usb_ep_table_t *table);
~EndpointResolver();
/**
* Add control endpoint size
*
* @param size Space reserved for control in and control out
*/
void endpoint_ctrl(uint32_t size);
/**
* Return a free IN endpoint of the given size
*
* @param type Desired endpoint type
* @param size Space to reserve for this endpoint
* @return Endpoint index or 0 if there are not enough resources
*/
usb_ep_t endpoint_in(usb_ep_type_t type, uint32_t size);
/**
* Return a free OUT endpoint of the given size
*
* @param type Desired endpoint type
* @param size Space to reserve for this endpoint
* @return Endpoint index or 0 if there are not enough resources
*/
usb_ep_t endpoint_out(usb_ep_type_t type, uint32_t size);
/**
* Get next free endpoint
*/
usb_ep_t next_free_endpoint(bool in_not_out, usb_ep_type_t type, uint32_t size);
/**
* Check if the endpoint configuration created so far is valid
*
* @return true if all endpoint sizes are available and fit, false otherwise
*/
bool valid();
/**
* Reset this class's state to when it was constructed
*/
void reset();
private:
usb_ep_t index_to_endpoint(int index);
int next_index(usb_ep_type_t type, bool in_not_out);
const usb_ep_table_t *_table;
uint32_t _cost;
uint32_t _used;
bool _valid;
};
#endif

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBDESCRIPTOR_H
#define USBDESCRIPTOR_H
/* Standard descriptor types */
#define DEVICE_DESCRIPTOR (1)
#define CONFIGURATION_DESCRIPTOR (2)
#define STRING_DESCRIPTOR (3)
#define INTERFACE_DESCRIPTOR (4)
#define ENDPOINT_DESCRIPTOR (5)
#define QUALIFIER_DESCRIPTOR (6)
/* Standard descriptor lengths */
#define DEVICE_DESCRIPTOR_LENGTH (0x12)
#define CONFIGURATION_DESCRIPTOR_LENGTH (0x09)
#define INTERFACE_DESCRIPTOR_LENGTH (0x09)
#define ENDPOINT_DESCRIPTOR_LENGTH (0x07)
/*string offset*/
#define STRING_OFFSET_LANGID (0)
#define STRING_OFFSET_IMANUFACTURER (1)
#define STRING_OFFSET_IPRODUCT (2)
#define STRING_OFFSET_ISERIAL (3)
#define STRING_OFFSET_ICONFIGURATION (4)
#define STRING_OFFSET_IINTERFACE (5)
/* USB Specification Release Number */
#define USB_VERSION_2_0 (0x0200)
/* Least/Most significant byte of short integer */
#define LSB(n) ((n)&0xff)
#define MSB(n) (((n)&0xff00)>>8)
/* bmAttributes in configuration descriptor */
/* C_RESERVED must always be set */
#define C_RESERVED (1U<<7)
#define C_SELF_POWERED (1U<<6)
#define C_REMOTE_WAKEUP (1U<<5)
/* bMaxPower in configuration descriptor */
#define C_POWER(mA) ((mA)/2)
/* bmAttributes in endpoint descriptor */
#define E_CONTROL (0x00)
#define E_ISOCHRONOUS (0x01)
#define E_BULK (0x02)
#define E_INTERRUPT (0x03)
/* For isochronous endpoints only: */
#define E_NO_SYNCHRONIZATION (0x00)
#define E_ASYNCHRONOUS (0x04)
#define E_ADAPTIVE (0x08)
#define E_SYNCHRONOUS (0x0C)
#define E_DATA (0x00)
#define E_FEEDBACK (0x10)
#define E_IMPLICIT_FEEDBACK (0x20)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,619 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBDEVICE_H
#define USBDEVICE_H
#include "USBDevice_Types.h"
#include "USBPhy.h"
#include "mbed_critical.h"
/**
* \defgroup usb_device USB Device
*
*/
/**
* \defgroup usb_device_core Core
*
* @ingroup usb_device
*/
/**
* Core USB Device driver
*
* USB driver which wraps and provides synchronization for a USBPhy object.
*
* @ingroup usb_device_core
*/
class USBDevice: public USBPhyEvents {
public:
typedef void (USBDevice::*ep_cb_t)();
enum RequestResult {
Receive = 0,
Send = 1,
Success = 2,
Failure = 3,
PassThrough = 4,
};
enum DeviceState {
Attached,
Powered,
Default,
Address,
Configured
};
struct setup_packet_t {
struct {
uint8_t dataTransferDirection;
uint8_t Type;
uint8_t Recipient;
} bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
};
/**
* Instantiate a new USBDevice with the given parameters
*
* @param phy The USBPhy providing physical USB access
* @param vendor_id The USB vendor ID
* @param product_id The USB product ID
* @param product_release The device release number
*/
USBDevice(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Cleanup this USBDevice
*
* This USBDevice must be uninitialized when the destructor is
* called or the behavior is undefined.
*/
virtual ~USBDevice();
/**
* Initialize this instance
*
* This function must be called before calling
* any other functions of this class, unless specifically
*/
void init();
/**
* Power down this instance
*
* Disable interrupts and stop sending events.
*/
void deinit();
/**
* Check if the device is configured
*
* @returns true if configured, false otherwise
*/
bool configured();
/**
* Connect a device
*/
void connect();
/**
* Disconnect a device
*/
void disconnect();
/**
* Enable the start of frame interrupt
*
* Call USBDevice::callback_sof on every frame.
*/
void sof_enable();
/**
* Disable the start of frame interrupt
*
* Stop calling USBDevice::callback_sof.
*/
void sof_disable();
/**
* Add an endpoint
*
* @param endpoint Endpoint to enable
* @param max_packet Maximum size of a packet which can be sent or received on this endpoint
* @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO
* @param callback Method pointer to be called when a packet is transferred
* @returns true if successful, false otherwise
*/
bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, ep_cb_t callback = NULL);
/**
* Add an endpoint
*
* @param endpoint Endpoint to enable
* @param max_packet Maximum size of a packet which can be sent or received on this endpoint
* @param type Endpoint type - USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO
* @param callback Method pointer to be called when a packet is transferred
* @returns true if successful, false otherwise
*/
template<typename T>
bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type, void (T::*callback)())
{
return endpoint_add(endpoint, max_packet, type, static_cast<ep_cb_t>(callback));
}
/**
* Remove an endpoint
*
* @param endpoint Endpoint to disable
* @note This endpoint must already have been setup with endpoint_add
*/
void endpoint_remove(usb_ep_t endpoint);
/**
* Remove all non-zero endpoints
*/
void endpoint_remove_all();
/**
* Stall an endpoint
*
* If there is an ongoing transfer on this endpoint then it will
* be aborted.
*
* @param endpoint Endpoint to stall
* @note You cannot stall endpoint 0 with this function
* @note This endpoint must already have been setup with endpoint_add
*/
void endpoint_stall(usb_ep_t endpoint);
/**
* Unstall an endpoint
*
* Unstalling an endpoint resets data toggle back to DATA0.
* Additionally, if there is an ongoing transfer on this endpoint
* it will be aborted.
*
* @param endpoint Endpoint to unstall
* @note This endpoint must already have been setup with endpoint_add
*/
void endpoint_unstall(usb_ep_t endpoint);
/**
* Get the current maximum size for this endpoint
*
* Return the currently configured maximum packet size, wMaxPacketSize,
* for this endpoint.
* @note This endpoint must already have been setup with endpoint_add
*/
uint32_t endpoint_max_packet_size(usb_ep_t endpoint);
/**
* Abort the current transfer on this endpoint
*
* @param endpoint endpoint with transfer to abort
* @note This endpoint must already have been setup with endpoint_add
*/
void endpoint_abort(usb_ep_t endpoint);
/**
* start a read on the given endpoint
*
* Start a read on the given endpoint. The data buffer must remain
* unchanged until the transfer either completes or is aborted.
*
* @param endpoint endpoint to read data from
* @param buffer buffer to fill with read data
* @param size The size of data to read. This must be greater than or equal
* to the max packet size for this endpoint
* @return true if the read was completed, otherwise false
* @note This endpoint must already have been setup with endpoint_add
*/
bool read_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size);
/**
* Get the status of a read
*
* @param endpoint endpoint to get the status of
* @return number of bytes read by this endpoint
*/
uint32_t read_finish(usb_ep_t endpoint);
/**
* Write a data to the given endpoint
*
* Write data to an endpoint. The data sent must remain unchanged until
* the transfer either completes or is aborted.
*
* @param endpoint endpoint to write data to
* @param buffer data to write
* @param size the size of data to send. This must be less than or equal to the
* max packet size of this endpoint
* @note This endpoint must already have been setup with endpoint_add
*/
bool write_start(usb_ep_t endpoint, uint8_t *buffer, uint32_t size);
/**
* Get the status of a write
*
* @param endpoint endpoint to get the status of
* @return number of bytes sent by this endpoint
*/
uint32_t write_finish(usb_ep_t endpoint);
/*
* Get device descriptor.
*
* @returns pointer to the device descriptor
*/
virtual const uint8_t *device_desc();
/*
* Get configuration descriptor
*
* @param index descriptor index
* @returns pointer to the configuration descriptor
*/
virtual const uint8_t *configuration_desc(uint8_t index) = 0;
/*
* Get string lang id descriptor
*
* @return pointer to the string lang id descriptor
*/
virtual const uint8_t *string_langid_desc();
/*
* Get string manufacturer descriptor
*
* @returns pointer to the string manufacturer descriptor
*/
virtual const uint8_t *string_imanufacturer_desc();
/*
* Get string product descriptor
*
* @returns pointer to the string product descriptor
*/
virtual const uint8_t *string_iproduct_desc();
/*
* Get string serial descriptor
*
* @returns pointer to the string serial descriptor
*/
virtual const uint8_t *string_iserial_desc();
/*
* Get string configuration descriptor
*
* @returns pointer to the string configuration descriptor
*/
virtual const uint8_t *string_iconfiguration_desc();
/*
* Get string interface descriptor
*
* @returns pointer to the string interface descriptor
*/
virtual const uint8_t *string_iinterface_desc();
/*
* Get the length of the report descriptor
*
* @returns length of the report descriptor
*/
virtual uint16_t report_desc_dength()
{
return 0;
};
protected:
/**
* Called by USBDevice layer on power state change.
*
* @param powered true if device is powered, false otherwise
*
* Warning: Called in ISR context
*/
virtual void callback_power(bool powered)
{
}
/**
* Called by USBDevice layer on each new USB frame.
*
* Callbacks are enabled and disabled by calling sof_enable
* and sof_disable.
*
* @param frame_number The current frame number
*
* Warning: Called in ISR context
*/
virtual void callback_sof(int frame_number)
{
}
/**
* Called by USBDevice layer on bus reset.
*
* complete_reset must be called after
* the device is fully reset.
*
* Warning: Called in ISR context
*/
virtual void callback_reset()
{
}
/**
* Called when USB changes state
*
* @param new_state The new state of the USBDevice
*
* Warning: Called in ISR context
*/
virtual void callback_state_change(DeviceState new_state) = 0;
/**
* Called by USBDevice on Endpoint0 request.
*
* This is used to handle extensions to standard requests
* and class specific requests. The function complete_request
* must be always be called in response to this callback.
*
* Warning: Called in ISR context
*/
virtual void callback_request(const setup_packet_t *setup) = 0;
/**
* Called to complete the setup stage of a callback request
*
* Possible options that can be passed as a result are:
* - Receive - Start the data OUT phase of this control transfer
* - Send - Start the data IN phase of this control transfer
* - Success - Operation was a success so start the status phase
* - Failure - Operation failed or is unsupported so send a stall
* - PassThrough - Pass on the request for standard processing
*
* @param result The result of the setup phase.
* @param data Buffer to send or receive if the result is Send or Receive
* @param size Size to transfer if the result is Send or Receive
*/
void complete_request(RequestResult result, uint8_t *data = NULL, uint32_t size = 0);
/**
* Called by USBDevice on data stage completion
*
* The function complete_request_xfer_done must be always be called
* in response to this callback.
*
* @param setup Setup packet of the current request
* @param aborted false if the operation was aborted, true otherwise
*
* Warning: Called in ISR context
*/
virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted) = 0;
/**
* Called to complete the data stage of a callback request
*
* @param success true if the operation was successful, false otherwise
*/
void complete_request_xfer_done(bool success);
/*
* Called by USBDevice layer in response to set_configuration.
*
* Upon reception of this command endpoints of the previous configuration
* if any must be removed with endpoint_remove and new endpoint added with
* endpoint_add.
*
* @param configuration Number of the configuration
*
* Warning: Called in ISR context
*/
virtual void callback_set_configuration(uint8_t configuration) = 0;
/**
* Called to complete a set configuration command
*
* @param success true if the configuration was set, false otherwise
*/
void complete_set_configuration(bool success);
/*
* Called by USBDevice layer in response to set_interface.
*
* Upon reception of this command endpoints of any previous interface
* if any must be removed with endpoint_remove and new endpoint added with
* endpoint_add.
*
* @param configuration Number of the configuration
*
* Warning: Called in ISR context
*/
virtual void callback_set_interface(uint16_t interface, uint8_t alternate) = 0;
/**
* Called to complete a set interface command
*
* @param success true if the interface was set, false otherwise
*/
void complete_set_interface(bool success);
/**
* Find a descriptor type inside the configuration descriptor
*
* @param descriptor_type Type of descriptor to find
* @param index Configuration descriptor index ( 0 if only one configuration present )
* @return A descriptor of the given type or NULL if none were found
*/
uint8_t *find_descriptor(uint8_t descriptor_type, uint8_t index = 0);
/**
* Get the endpoint table of this device
*
* @return Endpoint table of the USBPhy attached to this USBDevice
*/
const usb_ep_table_t *endpoint_table();
/**
* Callback called to indicate the USB processing needs to be done
*/
virtual void start_process();
/**
* Acquire exclusive access to this instance USBDevice
*/
virtual void lock();
/**
* Release exclusive access to this instance USBDevice
*/
virtual void unlock();
/**
* Assert that the current thread of execution holds the lock
*
*/
virtual void assert_locked();
uint16_t vendor_id;
uint16_t product_id;
uint16_t product_release;
uint8_t device_descriptor[18];
private:
// USBPhyEvents
virtual void power(bool powered);
virtual void suspend(bool suspended);
virtual void sof(int frame_number);
virtual void reset();
virtual void ep0_setup();
virtual void ep0_out();
virtual void ep0_in();
virtual void out(usb_ep_t endpoint);
virtual void in(usb_ep_t endpoint);
bool _request_get_descriptor();
bool _control_out();
bool _control_in();
bool _request_set_address();
bool _request_set_configuration();
bool _request_set_feature();
bool _request_clear_feature();
bool _request_get_status();
bool _request_setup();
void _control_setup();
void _control_abort();
void _control_abort_start();
void _control_setup_continue();
void _decode_setup_packet(uint8_t *data, setup_packet_t *packet);
bool _request_get_configuration();
bool _request_get_interface();
bool _request_set_interface();
void _change_state(DeviceState state);
void _run_later(void (USBDevice::*function)());
void _complete_request();
void _complete_request_xfer_done();
void _complete_set_configuration();
void _complete_set_interface();
struct endpoint_info_t {
ep_cb_t callback;
uint16_t max_packet_size;
uint16_t transfer_size;
uint8_t flags;
uint8_t pending;
};
struct usb_device_t {
volatile DeviceState state;
uint8_t configuration;
bool suspended;
};
enum ControlState {
Setup,
DataOut,
DataIn,
Status
};
enum UserCallback {
None,
Request,
RequestXferDone,
SetConfiguration,
SetInterface
};
struct complete_request_t {
RequestResult result;
uint8_t *data;
uint32_t size;
};
union complete_args_t {
complete_request_t request;
bool status;
};
struct control_transfer_t {
setup_packet_t setup;
uint8_t *ptr;
uint32_t remaining;
uint8_t direction;
bool zlp;
bool notify;
ControlState stage;
UserCallback user_callback;
complete_args_t args;
};
endpoint_info_t _endpoint_info[32 - 2];
USBPhy *_phy;
bool _initialized;
bool _connected;
bool _endpoint_add_remove_allowed;
control_transfer_t _transfer;
usb_device_t _device;
uint32_t _max_packet_size_ep0;
void (USBDevice::*_post_process)();
bool _setup_ready;
bool _abort_control;
uint16_t _current_interface;
uint8_t _current_alternate;
uint32_t _locked;
};
#endif

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBDEVICE_TYPES_H
#define USBDEVICE_TYPES_H
/* Standard requests */
#define GET_STATUS (0)
#define CLEAR_FEATURE (1)
#define SET_FEATURE (3)
#define SET_ADDRESS (5)
#define GET_DESCRIPTOR (6)
#define SET_DESCRIPTOR (7)
#define GET_CONFIGURATION (8)
#define SET_CONFIGURATION (9)
#define GET_INTERFACE (10)
#define SET_INTERFACE (11)
/* bmRequestType.dataTransferDirection */
#define HOST_TO_DEVICE (0)
#define DEVICE_TO_HOST (1)
/* bmRequestType.Type*/
#define STANDARD_TYPE (0)
#define CLASS_TYPE (1)
#define VENDOR_TYPE (2)
#define RESERVED_TYPE (3)
/* bmRequestType.Recipient */
#define DEVICE_RECIPIENT (0)
#define INTERFACE_RECIPIENT (1)
#define ENDPOINT_RECIPIENT (2)
#define OTHER_RECIPIENT (3)
/* Descriptors */
#define DESCRIPTOR_TYPE(wValue) (wValue >> 8)
#define DESCRIPTOR_INDEX(wValue) (wValue & 0xff)
#endif

View File

@ -0,0 +1,508 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBHID.h"
#include "EndpointResolver.h"
#include "usb_phy_api.h"
class USBHID::AsyncSend: public AsyncOp {
public:
AsyncSend(USBHID *hid, const HID_REPORT *report): hid(hid), report(report), result(false)
{
}
virtual ~AsyncSend()
{
}
virtual bool process()
{
if (!hid->configured()) {
result = false;
return true;
}
if (hid->send_nb(report)) {
result = true;
return true;
}
return false;
}
USBHID *hid;
const HID_REPORT *report;
bool result;
};
class USBHID::AsyncRead: public AsyncOp {
public:
AsyncRead(USBHID *hid, HID_REPORT *report): hid(hid), report(report), result(false)
{
}
virtual ~AsyncRead()
{
}
virtual bool process()
{
if (!hid->configured()) {
result = false;
return true;
}
if (hid->read_nb(report)) {
result = true;
return true;
}
return false;
}
USBHID *hid;
HID_REPORT *report;
bool result;
};
class USBHID::AsyncWait: public AsyncOp {
public:
AsyncWait(USBHID *hid): hid(hid)
{
}
virtual ~AsyncWait()
{
}
virtual bool process()
{
if (hid->configured()) {
return true;
}
return false;
}
USBHID *hid;
};
USBHID::USBHID(bool connect_blocking, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
{
_init(output_report_length, input_report_length);
if (connect_blocking) {
connect();
wait_ready();
} else {
init();
}
}
USBHID::USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init(output_report_length, input_report_length);
}
USBHID::~USBHID()
{
deinit();
}
void USBHID::_init(uint8_t output_report_length, uint8_t input_report_length)
{
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(64);
_int_in = resolver.endpoint_in(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE);
_int_out = resolver.endpoint_out(USB_EP_TYPE_INT, MAX_HID_REPORT_SIZE);
MBED_ASSERT(resolver.valid());
_send_idle = true;
_read_idle = true;
_output_length = output_report_length;
_input_length = input_report_length;
}
bool USBHID::ready()
{
return configured();
}
void USBHID::wait_ready()
{
lock();
AsyncWait wait_op(this);
_connect_list.add(&wait_op);
unlock();
wait_op.wait(NULL);
}
bool USBHID::send(const HID_REPORT *report)
{
lock();
AsyncSend send_op(this, report);
_send_list.add(&send_op);
unlock();
send_op.wait(NULL);
return send_op.result;
}
bool USBHID::send_nb(const HID_REPORT *report)
{
lock();
if (!configured()) {
unlock();
return false;
}
bool success = false;
if (_send_idle) {
memcpy(&_input_report, report, sizeof(_input_report));
write_start(_int_in, _input_report.data, _input_report.length);
_send_idle = false;
success = true;
}
unlock();
return success;
}
bool USBHID::read(HID_REPORT *report)
{
lock();
AsyncRead read_op(this, report);
_read_list.add(&read_op);
unlock();
read_op.wait(NULL);
return read_op.result;
}
bool USBHID::read_nb(HID_REPORT *report)
{
lock();
if (!configured()) {
unlock();
return false;
}
bool success = false;
if (_read_idle) {
memcpy(report, &_output_report, sizeof(_output_report));
read_start(_int_out, _output_report.data, MAX_HID_REPORT_SIZE);
_read_idle = false;
success = true;
}
unlock();
return success;
}
void USBHID::_send_isr()
{
assert_locked();
write_finish(_int_in);
_send_idle = true;
_send_list.process();
if (_send_idle) {
report_tx();
}
}
void USBHID::_read_isr()
{
assert_locked();
_output_report.length = read_finish(_int_out);
_read_idle = true;
_read_list.process();
if (_read_idle) {
report_rx();
}
}
uint16_t USBHID::report_desc_length()
{
report_desc();
return reportLength;
}
void USBHID::callback_state_change(DeviceState new_state)
{
if (new_state != Configured) {
if (!_send_idle) {
endpoint_abort(_int_in);
_send_idle = true;
}
if (!_read_idle) {
endpoint_abort(_int_out);
_read_idle = true;
}
}
_send_list.process();
_read_list.process();
_connect_list.process();
}
//
// Route callbacks from lower layers to class(es)
//
// Called in ISR context
// Called by USBDevice on Endpoint0 request
// This is used to handle extensions to standard requests
// and class specific requests
// Return true if class handles this request
void USBHID::callback_request(const setup_packet_t *setup)
{
uint8_t *hidDescriptor;
RequestResult result = PassThrough;
uint8_t *data = NULL;
uint32_t size = 0;
// Process additional standard requests
if ((setup->bmRequestType.Type == STANDARD_TYPE)) {
switch (setup->bRequest) {
case GET_DESCRIPTOR:
switch (DESCRIPTOR_TYPE(setup->wValue)) {
case REPORT_DESCRIPTOR:
if ((report_desc() != NULL) \
&& (report_desc_length() != 0)) {
size = report_desc_length();
data = (uint8_t *)report_desc();
result = Send;
}
break;
case HID_DESCRIPTOR:
// Find the HID descriptor, after the configuration descriptor
hidDescriptor = find_descriptor(HID_DESCRIPTOR);
if (hidDescriptor != NULL) {
size = HID_DESCRIPTOR_LENGTH;
data = hidDescriptor;
result = Send;
}
break;
default:
break;
}
break;
default:
break;
}
}
// Process class-specific requests
if (setup->bmRequestType.Type == CLASS_TYPE) {
switch (setup->bRequest) {
case SET_REPORT:
// First byte will be used for report ID
_output_report.data[0] = setup->wValue & 0xff;
_output_report.length = setup->wLength + 1;
size = sizeof(_output_report.data) - 1;
data = &_output_report.data[1];
result = Send;
break;
default:
break;
}
}
complete_request(result, data, size);
}
void USBHID::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
(void)aborted;
complete_request_xfer_done(true);
}
#define DEFAULT_CONFIGURATION (1)
// Called in ISR context
// Set configuration. Return false if the
// configuration is not supported
void USBHID::callback_set_configuration(uint8_t configuration)
{
if (configuration == DEFAULT_CONFIGURATION) {
complete_set_configuration(false);
}
// Configure endpoints > 0
endpoint_add(_int_in, MAX_HID_REPORT_SIZE, USB_EP_TYPE_INT, &USBHID::_send_isr);
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_idle = false;
complete_set_configuration(true);
}
void USBHID::callback_set_interface(uint16_t interface, uint8_t alternate)
{
assert_locked();
complete_set_interface(true);
}
const uint8_t *USBHID::string_iinterface_desc()
{
static const uint8_t stringIinterfaceDescriptor[] = {
0x08, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'H', 0, 'I', 0, 'D', 0, //bString iInterface - HID
};
return stringIinterfaceDescriptor;
}
const uint8_t *USBHID::string_iproduct_desc()
{
static const uint8_t stringIproductDescriptor[] = {
0x16, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0 //bString iProduct - HID device
};
return stringIproductDescriptor;
}
const uint8_t *USBHID::report_desc()
{
uint8_t reportDescriptorTemp[] = {
USAGE_PAGE(2), LSB(0xFFAB), MSB(0xFFAB),
USAGE(2), LSB(0x0200), MSB(0x0200),
COLLECTION(1), 0x01, // Collection (Application)
REPORT_SIZE(1), 0x08, // 8 bits
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0xFF,
REPORT_COUNT(1), _input_length,
USAGE(1), 0x01,
INPUT(1), 0x02, // Data, Var, Abs
REPORT_COUNT(1), _output_length,
USAGE(1), 0x02,
OUTPUT(1), 0x02, // Data, Var, Abs
END_COLLECTION(0),
};
reportLength = sizeof(reportDescriptor);
MBED_ASSERT(sizeof(reportDescriptorTemp) == sizeof(reportDescriptor));
memcpy(reportDescriptor, reportDescriptorTemp, sizeof(reportDescriptor));
return reportDescriptor;
}
#define DEFAULT_CONFIGURATION (1)
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
+ (1 * INTERFACE_DESCRIPTOR_LENGTH) \
+ (1 * HID_DESCRIPTOR_LENGTH) \
+ (2 * ENDPOINT_DESCRIPTOR_LENGTH))
const uint8_t *USBHID::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t configurationDescriptorTemp[] = {
CONFIGURATION_DESCRIPTOR_LENGTH, // bLength
CONFIGURATION_DESCRIPTOR, // bDescriptorType
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
0x01, // bNumInterfaces
DEFAULT_CONFIGURATION, // bConfigurationValue
0x00, // iConfiguration
C_RESERVED | C_SELF_POWERED, // bmAttributes
C_POWER(0), // bMaxPower
INTERFACE_DESCRIPTOR_LENGTH, // bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
HID_CLASS, // bInterfaceClass
HID_SUBCLASS_NONE, // bInterfaceSubClass
HID_PROTOCOL_NONE, // bInterfaceProtocol
0x00, // iInterface
HID_DESCRIPTOR_LENGTH, // bLength
HID_DESCRIPTOR, // bDescriptorType
LSB(HID_VERSION_1_11), // bcdHID (LSB)
MSB(HID_VERSION_1_11), // bcdHID (MSB)
0x00, // bCountryCode
0x01, // bNumDescriptors
REPORT_DESCRIPTOR, // bDescriptorType
(uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB)
(uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_in, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_out, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
};
MBED_ASSERT(sizeof(configurationDescriptorTemp) == sizeof(_configuration_descriptor));
memcpy(_configuration_descriptor, configurationDescriptorTemp, sizeof(_configuration_descriptor));
return _configuration_descriptor;
}

271
usb/device/USBHID/USBHID.h Normal file
View File

@ -0,0 +1,271 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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_HID_H
#define USB_HID_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice.h"
#include "USBHID_Types.h"
#include "OperationList.h"
/**
* USBHID example
* @code
* #include "mbed.h"
* #include "USBHID.h"
*
* USBHID hid;
* HID_REPORT recv;
* BusOut leds(LED1,LED2,LED3,LED4);
*
* int main(void) {
* while (1) {
* hid.read(&recv);
* leds = recv.data[0];
* }
* }
* @endcode
*/
class USBHID: public USBDevice {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param output_report_length Maximum length of a sent report (up to 64 bytes)
* @param input_report_length Maximum length of a received report (up to 64 bytes)
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBHID(bool connect_blocking = true, uint8_t output_report_length = 64, uint8_t input_report_length = 64, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0006, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param output_report_length Maximum length of a sent report (up to 64 bytes)
* @param input_report_length Maximum length of a received report (up to 64 bytes)
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBHID(USBPhy *phy, uint8_t output_report_length, uint8_t input_report_length, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBHID();
/**
* Check if this class is ready
*
* @return true if the device is in the configured state
*/
bool ready();
/**
* Block until this HID device is in the configured state
*/
void wait_ready();
/**
* Send a Report. warning: blocking
*
* @param report Report which will be sent (a report is defined by all data and the length)
* @returns true if successful
*/
bool send(const HID_REPORT *report);
/**
* Send a Report. warning: non blocking
*
* @param report Report which will be sent (a report is defined by all data and the length)
* @returns true if successful
*/
bool send_nb(const HID_REPORT *report);
/**
* Read a report: blocking
*
* @param report pointer to the report to fill
* @returns true if successful
*/
bool read(HID_REPORT *report);
/**
* Read a report: non blocking
*
* @param report pointer to the report to fill
* @returns true if successful
*/
bool read_nb(HID_REPORT *report);
protected:
uint16_t reportLength;
uint8_t reportDescriptor[27];
/*
* Get the Report descriptor
*
* @returns pointer to the report descriptor
*/
virtual const uint8_t *report_desc();
/*
* Get the length of the report descriptor
*
* @returns the length of the report descriptor
*/
virtual uint16_t report_desc_length();
/*
* 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(uint8_t index);
/*
* HID Report received by SET_REPORT request. Warning: Called in ISR context
* First byte of data will be the report ID
*
* @param report Data and length received
*/
virtual void HID_callbackSetReport(HID_REPORT *report) {};
/**
* Called when USB changes state
*
* @param new_state The new state of the USBDevice
*
* Warning: Called in ISR context
*/
virtual void callback_state_change(DeviceState new_state);
/*
* This is used to handle extensions to standard requests
* and class specific requests
*/
virtual void callback_request(const setup_packet_t *setup);
/*
* This is used to handle extensions to standard requests
* and class specific requests with a data phase
*/
virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted);
/*
* Called by USBDevice layer. Set configuration of the device.
* For instance, you can add all endpoints that you need on this function.
*
* @param configuration Number of the configuration
* @returns true if class handles this request
*/
virtual void callback_set_configuration(uint8_t configuration);
/*
* Called by USBDevice layer in response to set_interface.
*
* Upon reception of this command endpoints of any previous interface
* if any must be removed with endpoint_remove and new endpoint added with
* endpoint_add.
*
* @param configuration Number of the configuration
*
* Warning: Called in ISR context
*/
virtual void callback_set_interface(uint16_t interface, uint8_t alternate);
/*
* Called when there is a hid report that can be read
*/
virtual void report_rx() {}
/*
* Called when there is space to send a hid report
*/
virtual void report_tx() {}
protected:
usb_ep_t _int_in;
usb_ep_t _int_out;
private:
void _init(uint8_t output_report_length, uint8_t input_report_length);
void _send_isr();
void _read_isr();
class AsyncSend;
class AsyncRead;
class AsyncWait;
OperationList<AsyncWait> _connect_list;
OperationList<AsyncSend> _send_list;
bool _send_idle;
OperationList<AsyncRead> _read_list;
bool _read_idle;
uint8_t _configuration_descriptor[41];
HID_REPORT _input_report;
HID_REPORT _output_report;
uint8_t _output_length;
uint8_t _input_length;
};
#endif

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBCLASS_HID_TYPES
#define USBCLASS_HID_TYPES
#include <stdint.h>
/* */
#define HID_VERSION_1_11 (0x0111)
/* HID Class */
#define HID_CLASS (3)
#define HID_SUBCLASS_NONE (0)
#define HID_SUBCLASS_BOOT (1)
#define HID_PROTOCOL_NONE (0)
#define HID_PROTOCOL_KEYBOARD (1)
#define HID_PROTOCOL_MOUSE (2)
/* Descriptors */
#define HID_DESCRIPTOR (33)
#define HID_DESCRIPTOR_LENGTH (0x09)
#define REPORT_DESCRIPTOR (34)
/* Class requests */
#define GET_REPORT (0x1)
#define GET_IDLE (0x2)
#define SET_REPORT (0x9)
#define SET_IDLE (0xa)
/* HID Class Report Descriptor */
/* Short items: size is 0, 1, 2 or 3 specifying 0, 1, 2 or 4 (four) bytes */
/* of data as per HID Class standard */
/* Main items */
#define INPUT(size) (0x80 | size)
#define OUTPUT(size) (0x90 | size)
#define FEATURE(size) (0xb0 | size)
#define COLLECTION(size) (0xa0 | size)
#define END_COLLECTION(size) (0xc0 | size)
/* Global items */
#define USAGE_PAGE(size) (0x04 | size)
#define LOGICAL_MINIMUM(size) (0x14 | size)
#define LOGICAL_MAXIMUM(size) (0x24 | size)
#define PHYSICAL_MINIMUM(size) (0x34 | size)
#define PHYSICAL_MAXIMUM(size) (0x44 | size)
#define UNIT_EXPONENT(size) (0x54 | size)
#define UNIT(size) (0x64 | size)
#define REPORT_SIZE(size) (0x74 | size)
#define REPORT_ID(size) (0x84 | size)
#define REPORT_COUNT(size) (0x94 | size)
#define PUSH(size) (0xa4 | size)
#define POP(size) (0xb4 | size)
/* Local items */
#define USAGE(size) (0x08 | size)
#define USAGE_MINIMUM(size) (0x18 | size)
#define USAGE_MAXIMUM(size) (0x28 | size)
#define DESIGNATOR_INDEX(size) (0x38 | size)
#define DESIGNATOR_MINIMUM(size) (0x48 | size)
#define DESIGNATOR_MAXIMUM(size) (0x58 | size)
#define STRING_INDEX(size) (0x78 | size)
#define STRING_MINIMUM(size) (0x88 | size)
#define STRING_MAXIMUM(size) (0x98 | size)
#define DELIMITER(size) (0xa8 | size)
/* HID Report */
/* Where report IDs are used the first byte of 'data' will be the */
/* report ID and 'length' will include this report ID byte. */
#define MAX_HID_REPORT_SIZE (64)
typedef struct {
uint32_t length;
uint8_t data[MAX_HID_REPORT_SIZE];
} HID_REPORT;
#endif

View File

@ -0,0 +1,605 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBKeyboard.h"
#include "usb_phy_api.h"
#define REPORT_ID_KEYBOARD 1
#define REPORT_ID_VOLUME 3
typedef struct {
unsigned char usage;
unsigned char modifier;
} KEYMAP;
#ifdef US_KEYBOARD
/* US keyboard (as HID standard) */
#define KEYMAP_SIZE (152)
const KEYMAP keymap[KEYMAP_SIZE] = {
{0, 0}, /* NUL */
{0, 0}, /* SOH */
{0, 0}, /* STX */
{0, 0}, /* ETX */
{0, 0}, /* EOT */
{0, 0}, /* ENQ */
{0, 0}, /* ACK */
{0, 0}, /* BEL */
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
{0, 0}, /* VT */
{0, 0}, /* FF */
{0, 0}, /* CR */
{0, 0}, /* SO */
{0, 0}, /* SI */
{0, 0}, /* DEL */
{0, 0}, /* DC1 */
{0, 0}, /* DC2 */
{0, 0}, /* DC3 */
{0, 0}, /* DC4 */
{0, 0}, /* NAK */
{0, 0}, /* SYN */
{0, 0}, /* ETB */
{0, 0}, /* CAN */
{0, 0}, /* EM */
{0, 0}, /* SUB */
{0, 0}, /* ESC */
{0, 0}, /* FS */
{0, 0}, /* GS */
{0, 0}, /* RS */
{0, 0}, /* US */
{0x2c, 0}, /* */
{0x1e, KEY_SHIFT}, /* ! */
{0x34, KEY_SHIFT}, /* " */
{0x20, KEY_SHIFT}, /* # */
{0x21, KEY_SHIFT}, /* $ */
{0x22, KEY_SHIFT}, /* % */
{0x24, KEY_SHIFT}, /* & */
{0x34, 0}, /* ' */
{0x26, KEY_SHIFT}, /* ( */
{0x27, KEY_SHIFT}, /* ) */
{0x25, KEY_SHIFT}, /* * */
{0x2e, KEY_SHIFT}, /* + */
{0x36, 0}, /* , */
{0x2d, 0}, /* - */
{0x37, 0}, /* . */
{0x38, 0}, /* / */
{0x27, 0}, /* 0 */
{0x1e, 0}, /* 1 */
{0x1f, 0}, /* 2 */
{0x20, 0}, /* 3 */
{0x21, 0}, /* 4 */
{0x22, 0}, /* 5 */
{0x23, 0}, /* 6 */
{0x24, 0}, /* 7 */
{0x25, 0}, /* 8 */
{0x26, 0}, /* 9 */
{0x33, KEY_SHIFT}, /* : */
{0x33, 0}, /* ; */
{0x36, KEY_SHIFT}, /* < */
{0x2e, 0}, /* = */
{0x37, KEY_SHIFT}, /* > */
{0x38, KEY_SHIFT}, /* ? */
{0x1f, KEY_SHIFT}, /* @ */
{0x04, KEY_SHIFT}, /* A */
{0x05, KEY_SHIFT}, /* B */
{0x06, KEY_SHIFT}, /* C */
{0x07, KEY_SHIFT}, /* D */
{0x08, KEY_SHIFT}, /* E */
{0x09, KEY_SHIFT}, /* F */
{0x0a, KEY_SHIFT}, /* G */
{0x0b, KEY_SHIFT}, /* H */
{0x0c, KEY_SHIFT}, /* I */
{0x0d, KEY_SHIFT}, /* J */
{0x0e, KEY_SHIFT}, /* K */
{0x0f, KEY_SHIFT}, /* L */
{0x10, KEY_SHIFT}, /* M */
{0x11, KEY_SHIFT}, /* N */
{0x12, KEY_SHIFT}, /* O */
{0x13, KEY_SHIFT}, /* P */
{0x14, KEY_SHIFT}, /* Q */
{0x15, KEY_SHIFT}, /* R */
{0x16, KEY_SHIFT}, /* S */
{0x17, KEY_SHIFT}, /* T */
{0x18, KEY_SHIFT}, /* U */
{0x19, KEY_SHIFT}, /* V */
{0x1a, KEY_SHIFT}, /* W */
{0x1b, KEY_SHIFT}, /* X */
{0x1c, KEY_SHIFT}, /* Y */
{0x1d, KEY_SHIFT}, /* Z */
{0x2f, 0}, /* [ */
{0x31, 0}, /* \ */
{0x30, 0}, /* ] */
{0x23, KEY_SHIFT}, /* ^ */
{0x2d, KEY_SHIFT}, /* _ */
{0x35, 0}, /* ` */
{0x04, 0}, /* a */
{0x05, 0}, /* b */
{0x06, 0}, /* c */
{0x07, 0}, /* d */
{0x08, 0}, /* e */
{0x09, 0}, /* f */
{0x0a, 0}, /* g */
{0x0b, 0}, /* h */
{0x0c, 0}, /* i */
{0x0d, 0}, /* j */
{0x0e, 0}, /* k */
{0x0f, 0}, /* l */
{0x10, 0}, /* m */
{0x11, 0}, /* n */
{0x12, 0}, /* o */
{0x13, 0}, /* p */
{0x14, 0}, /* q */
{0x15, 0}, /* r */
{0x16, 0}, /* s */
{0x17, 0}, /* t */
{0x18, 0}, /* u */
{0x19, 0}, /* v */
{0x1a, 0}, /* w */
{0x1b, 0}, /* x */
{0x1c, 0}, /* y */
{0x1d, 0}, /* z */
{0x2f, KEY_SHIFT}, /* { */
{0x31, KEY_SHIFT}, /* | */
{0x30, KEY_SHIFT}, /* } */
{0x35, KEY_SHIFT}, /* ~ */
{0, 0}, /* DEL */
{0x3a, 0}, /* F1 */
{0x3b, 0}, /* F2 */
{0x3c, 0}, /* F3 */
{0x3d, 0}, /* F4 */
{0x3e, 0}, /* F5 */
{0x3f, 0}, /* F6 */
{0x40, 0}, /* F7 */
{0x41, 0}, /* F8 */
{0x42, 0}, /* F9 */
{0x43, 0}, /* F10 */
{0x44, 0}, /* F11 */
{0x45, 0}, /* F12 */
{0x46, 0}, /* PRINT_SCREEN */
{0x47, 0}, /* SCROLL_LOCK */
{0x39, 0}, /* CAPS_LOCK */
{0x53, 0}, /* NUM_LOCK */
{0x49, 0}, /* INSERT */
{0x4a, 0}, /* HOME */
{0x4b, 0}, /* PAGE_UP */
{0x4e, 0}, /* PAGE_DOWN */
{0x4f, 0}, /* RIGHT_ARROW */
{0x50, 0}, /* LEFT_ARROW */
{0x51, 0}, /* DOWN_ARROW */
{0x52, 0}, /* UP_ARROW */
};
#else
/* UK keyboard */
#define KEYMAP_SIZE (152)
const KEYMAP keymap[KEYMAP_SIZE] = {
{0, 0}, /* NUL */
{0, 0}, /* SOH */
{0, 0}, /* STX */
{0, 0}, /* ETX */
{0, 0}, /* EOT */
{0, 0}, /* ENQ */
{0, 0}, /* ACK */
{0, 0}, /* BEL */
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
{0, 0}, /* VT */
{0, 0}, /* FF */
{0, 0}, /* CR */
{0, 0}, /* SO */
{0, 0}, /* SI */
{0, 0}, /* DEL */
{0, 0}, /* DC1 */
{0, 0}, /* DC2 */
{0, 0}, /* DC3 */
{0, 0}, /* DC4 */
{0, 0}, /* NAK */
{0, 0}, /* SYN */
{0, 0}, /* ETB */
{0, 0}, /* CAN */
{0, 0}, /* EM */
{0, 0}, /* SUB */
{0, 0}, /* ESC */
{0, 0}, /* FS */
{0, 0}, /* GS */
{0, 0}, /* RS */
{0, 0}, /* US */
{0x2c, 0}, /* */
{0x1e, KEY_SHIFT}, /* ! */
{0x1f, KEY_SHIFT}, /* " */
{0x32, 0}, /* # */
{0x21, KEY_SHIFT}, /* $ */
{0x22, KEY_SHIFT}, /* % */
{0x24, KEY_SHIFT}, /* & */
{0x34, 0}, /* ' */
{0x26, KEY_SHIFT}, /* ( */
{0x27, KEY_SHIFT}, /* ) */
{0x25, KEY_SHIFT}, /* * */
{0x2e, KEY_SHIFT}, /* + */
{0x36, 0}, /* , */
{0x2d, 0}, /* - */
{0x37, 0}, /* . */
{0x38, 0}, /* / */
{0x27, 0}, /* 0 */
{0x1e, 0}, /* 1 */
{0x1f, 0}, /* 2 */
{0x20, 0}, /* 3 */
{0x21, 0}, /* 4 */
{0x22, 0}, /* 5 */
{0x23, 0}, /* 6 */
{0x24, 0}, /* 7 */
{0x25, 0}, /* 8 */
{0x26, 0}, /* 9 */
{0x33, KEY_SHIFT}, /* : */
{0x33, 0}, /* ; */
{0x36, KEY_SHIFT}, /* < */
{0x2e, 0}, /* = */
{0x37, KEY_SHIFT}, /* > */
{0x38, KEY_SHIFT}, /* ? */
{0x34, KEY_SHIFT}, /* @ */
{0x04, KEY_SHIFT}, /* A */
{0x05, KEY_SHIFT}, /* B */
{0x06, KEY_SHIFT}, /* C */
{0x07, KEY_SHIFT}, /* D */
{0x08, KEY_SHIFT}, /* E */
{0x09, KEY_SHIFT}, /* F */
{0x0a, KEY_SHIFT}, /* G */
{0x0b, KEY_SHIFT}, /* H */
{0x0c, KEY_SHIFT}, /* I */
{0x0d, KEY_SHIFT}, /* J */
{0x0e, KEY_SHIFT}, /* K */
{0x0f, KEY_SHIFT}, /* L */
{0x10, KEY_SHIFT}, /* M */
{0x11, KEY_SHIFT}, /* N */
{0x12, KEY_SHIFT}, /* O */
{0x13, KEY_SHIFT}, /* P */
{0x14, KEY_SHIFT}, /* Q */
{0x15, KEY_SHIFT}, /* R */
{0x16, KEY_SHIFT}, /* S */
{0x17, KEY_SHIFT}, /* T */
{0x18, KEY_SHIFT}, /* U */
{0x19, KEY_SHIFT}, /* V */
{0x1a, KEY_SHIFT}, /* W */
{0x1b, KEY_SHIFT}, /* X */
{0x1c, KEY_SHIFT}, /* Y */
{0x1d, KEY_SHIFT}, /* Z */
{0x2f, 0}, /* [ */
{0x64, 0}, /* \ */
{0x30, 0}, /* ] */
{0x23, KEY_SHIFT}, /* ^ */
{0x2d, KEY_SHIFT}, /* _ */
{0x35, 0}, /* ` */
{0x04, 0}, /* a */
{0x05, 0}, /* b */
{0x06, 0}, /* c */
{0x07, 0}, /* d */
{0x08, 0}, /* e */
{0x09, 0}, /* f */
{0x0a, 0}, /* g */
{0x0b, 0}, /* h */
{0x0c, 0}, /* i */
{0x0d, 0}, /* j */
{0x0e, 0}, /* k */
{0x0f, 0}, /* l */
{0x10, 0}, /* m */
{0x11, 0}, /* n */
{0x12, 0}, /* o */
{0x13, 0}, /* p */
{0x14, 0}, /* q */
{0x15, 0}, /* r */
{0x16, 0}, /* s */
{0x17, 0}, /* t */
{0x18, 0}, /* u */
{0x19, 0}, /* v */
{0x1a, 0}, /* w */
{0x1b, 0}, /* x */
{0x1c, 0}, /* y */
{0x1d, 0}, /* z */
{0x2f, KEY_SHIFT}, /* { */
{0x64, KEY_SHIFT}, /* | */
{0x30, KEY_SHIFT}, /* } */
{0x32, KEY_SHIFT}, /* ~ */
{0, 0}, /* DEL */
{0x3a, 0}, /* F1 */
{0x3b, 0}, /* F2 */
{0x3c, 0}, /* F3 */
{0x3d, 0}, /* F4 */
{0x3e, 0}, /* F5 */
{0x3f, 0}, /* F6 */
{0x40, 0}, /* F7 */
{0x41, 0}, /* F8 */
{0x42, 0}, /* F9 */
{0x43, 0}, /* F10 */
{0x44, 0}, /* F11 */
{0x45, 0}, /* F12 */
{0x46, 0}, /* PRINT_SCREEN */
{0x47, 0}, /* SCROLL_LOCK */
{0x39, 0}, /* CAPS_LOCK */
{0x53, 0}, /* NUM_LOCK */
{0x49, 0}, /* INSERT */
{0x4a, 0}, /* HOME */
{0x4b, 0}, /* PAGE_UP */
{0x4e, 0}, /* PAGE_DOWN */
{0x4f, 0}, /* RIGHT_ARROW */
{0x50, 0}, /* LEFT_ARROW */
{0x51, 0}, /* DOWN_ARROW */
{0x52, 0}, /* UP_ARROW */
};
#endif
USBKeyboard::USBKeyboard(bool connect, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release)
{
_lock_status = 0;
if (connect) {
USBDevice::connect();
wait_ready();
} else {
init();
}
}
USBKeyboard::USBKeyboard(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(phy, 0, 0, vendor_id, product_id, product_release)
{
_lock_status = 0;
// User or child responsible for calling connect or init
}
USBKeyboard::~USBKeyboard()
{
deinit();
}
const uint8_t *USBKeyboard::report_desc()
{
static const uint8_t reportDescriptor[] = {
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x06, // Keyboard
COLLECTION(1), 0x01, // Application
REPORT_ID(1), REPORT_ID_KEYBOARD,
USAGE_PAGE(1), 0x07, // Key Codes
USAGE_MINIMUM(1), 0xE0,
USAGE_MAXIMUM(1), 0xE7,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x08,
INPUT(1), 0x02, // Data, Variable, Absolute
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x08,
INPUT(1), 0x01, // Constant
REPORT_COUNT(1), 0x05,
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x08, // LEDs
USAGE_MINIMUM(1), 0x01,
USAGE_MAXIMUM(1), 0x05,
OUTPUT(1), 0x02, // Data, Variable, Absolute
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x03,
OUTPUT(1), 0x01, // Constant
REPORT_COUNT(1), 0x06,
REPORT_SIZE(1), 0x08,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x65,
USAGE_PAGE(1), 0x07, // Key Codes
USAGE_MINIMUM(1), 0x00,
USAGE_MAXIMUM(1), 0x65,
INPUT(1), 0x00, // Data, Array
END_COLLECTION(0),
// Media Control
USAGE_PAGE(1), 0x0C,
USAGE(1), 0x01,
COLLECTION(1), 0x01,
REPORT_ID(1), REPORT_ID_VOLUME,
USAGE_PAGE(1), 0x0C,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x07,
USAGE(1), 0xB5, // Next Track
USAGE(1), 0xB6, // Previous Track
USAGE(1), 0xB7, // Stop
USAGE(1), 0xCD, // Play / Pause
USAGE(1), 0xE2, // Mute
USAGE(1), 0xE9, // Volume Up
USAGE(1), 0xEA, // Volume Down
INPUT(1), 0x02, // Input (Data, Variable, Absolute)
REPORT_COUNT(1), 0x01,
INPUT(1), 0x01,
END_COLLECTION(0),
};
reportLength = sizeof(reportDescriptor);
return reportDescriptor;
}
void USBKeyboard::report_rx()
{
assert_locked();
HID_REPORT report;
read_nb(&report);
// we take [1] because [0] is the report ID
_lock_status = report.data[1] & 0x07;
}
uint8_t USBKeyboard::lock_status()
{
return _lock_status;
}
int USBKeyboard::_putc(int c)
{
return key_code(c, keymap[c].modifier);
}
bool USBKeyboard::key_code(uint8_t key, uint8_t modifier)
{
_mutex.lock();
// Send a simulated keyboard keypress. Returns true if successful.
HID_REPORT report;
report.data[0] = REPORT_ID_KEYBOARD;
report.data[1] = modifier;
report.data[2] = 0;
report.data[3] = keymap[key].usage;
report.data[4] = 0;
report.data[5] = 0;
report.data[6] = 0;
report.data[7] = 0;
report.data[8] = 0;
report.length = 9;
if (!send(&report)) {
_mutex.unlock();
return false;
}
report.data[1] = 0;
report.data[3] = 0;
if (!send(&report)) {
_mutex.unlock();
return false;
}
_mutex.unlock();
return true;
}
bool USBKeyboard::media_control(MEDIA_KEY key)
{
_mutex.lock();
HID_REPORT report;
report.data[0] = REPORT_ID_VOLUME;
report.data[1] = (1 << key) & 0x7f;
report.length = 2;
if (!send(&report)) {
_mutex.unlock();
return false;
}
report.data[0] = REPORT_ID_VOLUME;
report.data[1] = 0;
report.length = 2;
if (!send(&report)) {
_mutex.unlock();
return false;
}
_mutex.unlock();
return true;
}
#define DEFAULT_CONFIGURATION (1)
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
+ (1 * INTERFACE_DESCRIPTOR_LENGTH) \
+ (1 * HID_DESCRIPTOR_LENGTH) \
+ (2 * ENDPOINT_DESCRIPTOR_LENGTH))
const uint8_t *USBKeyboard::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t configuration_descriptor_temp[] = {
CONFIGURATION_DESCRIPTOR_LENGTH, // bLength
CONFIGURATION_DESCRIPTOR, // bDescriptorType
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
0x01, // bNumInterfaces
DEFAULT_CONFIGURATION, // bConfigurationValue
0x00, // iConfiguration
C_RESERVED | C_SELF_POWERED, // bmAttributes
C_POWER(0), // bMaxPower
INTERFACE_DESCRIPTOR_LENGTH, // bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
HID_CLASS, // bInterfaceClass
HID_SUBCLASS_BOOT, // bInterfaceSubClass
HID_PROTOCOL_KEYBOARD, // bInterfaceProtocol
0x00, // iInterface
HID_DESCRIPTOR_LENGTH, // bLength
HID_DESCRIPTOR, // bDescriptorType
LSB(HID_VERSION_1_11), // bcdHID (LSB)
MSB(HID_VERSION_1_11), // bcdHID (MSB)
0x00, // bCountryCode
0x01, // bNumDescriptors
REPORT_DESCRIPTOR, // bDescriptorType
(uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB)
(uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_in, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_out, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
};
MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor));
memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor));
return _configuration_descriptor;
}
int USBKeyboard::_getc()
{
return -1;
}

View File

@ -0,0 +1,212 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBKEYBOARD_H
#define USBKEYBOARD_H
#include "USBHID.h"
#include "Stream.h"
#include "PlatformMutex.h"
/* Modifiers, left keys then right keys. */
enum MODIFIER_KEY {
KEY_CTRL = 0x01,
KEY_SHIFT = 0x02,
KEY_ALT = 0x04,
KEY_LOGO = 0x08,
KEY_RCTRL = 0x10,
KEY_RSHIFT = 0x20,
KEY_RALT = 0x40,
KEY_RLOGO = 0x80,
};
enum MEDIA_KEY {
KEY_NEXT_TRACK, /*!< next Track Button */
KEY_PREVIOUS_TRACK, /*!< Previous track Button */
KEY_STOP, /*!< Stop Button */
KEY_PLAY_PAUSE, /*!< Play/Pause Button */
KEY_MUTE, /*!< Mute Button */
KEY_VOLUME_UP, /*!< Volume Up Button */
KEY_VOLUME_DOWN, /*!< Volume Down Button */
};
enum FUNCTION_KEY {
KEY_F1 = 128, /* F1 key */
KEY_F2, /* F2 key */
KEY_F3, /* F3 key */
KEY_F4, /* F4 key */
KEY_F5, /* F5 key */
KEY_F6, /* F6 key */
KEY_F7, /* F7 key */
KEY_F8, /* F8 key */
KEY_F9, /* F9 key */
KEY_F10, /* F10 key */
KEY_F11, /* F11 key */
KEY_F12, /* F12 key */
KEY_PRINT_SCREEN, /* Print Screen key */
KEY_SCROLL_LOCK, /* Scroll lock */
KEY_CAPS_LOCK, /* caps lock */
KEY_NUM_LOCK, /* num lock */
KEY_INSERT, /* Insert key */
KEY_HOME, /* Home key */
KEY_PAGE_UP, /* Page Up key */
KEY_PAGE_DOWN, /* Page Down key */
RIGHT_ARROW, /* Right arrow */
LEFT_ARROW, /* Left arrow */
DOWN_ARROW, /* Down arrow */
UP_ARROW, /* Up arrow */
};
/**
* USBKeyboard example
* @code
*
* #include "mbed.h"
* #include "USBKeyboard.h"
*
* USBKeyboard key;
*
* int main(void)
* {
* while (1) {
* key.printf("Hello World\r\n");
* wait(1);
* }
* }
*
* @endcode
*
* @note Synchronization level: Thread safe
*/
class USBKeyboard: public USBHID, public mbed::Stream {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBKeyboard(bool connect_blocking = true, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBKeyboard(USBPhy *phy, uint16_t vendor_id = 0x1235, uint16_t product_id = 0x0050, uint16_t product_release = 0x0001);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBKeyboard();
/**
* To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key
*
* @code
* //To send CTRL + s (save)
* keyboard.key_code('s', KEY_CTRL);
* @endcode
*
* @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0)
* @param key character to send
* @returns true if there is no error, false otherwise
*/
bool key_code(uint8_t key, uint8_t modifier = 0);
/**
* Send a character
*
* @param c character to be sent
* @returns true if there is no error, false otherwise
*/
virtual int _putc(int c);
/**
* Control media keys
*
* @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN)
* @returns true if there is no error, false otherwise
*/
bool media_control(MEDIA_KEY key);
/*
* To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
*
* @returns pointer to the report descriptor
*/
virtual const uint8_t *report_desc();
/*
* Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys
*/
virtual void report_rx();
/**
* Read status of lock keys. Useful to switch-on/off leds according to key pressed. Only the first three bits of the result is important:
* - First bit: NUM_LOCK
* - Second bit: CAPS_LOCK
* - Third bit: SCROLL_LOCK
*
* @returns status of lock keys
*/
uint8_t lock_status();
protected:
/*
* Get configuration descriptor
*
* @returns pointer to the configuration descriptor
*/
virtual const uint8_t *configuration_desc(uint8_t index);
private:
//dummy otherwise it doesn't compile (we must define all methods of an abstract class)
virtual int _getc();
uint8_t _configuration_descriptor[41];
uint8_t _lock_status;
PlatformMutex _mutex;
};
#endif

View File

@ -0,0 +1,350 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBMouse.h"
#include "PlatformMutex.h"
#include "usb_phy_api.h"
#include "mbed_wait_api.h"
USBMouse::USBMouse(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release)
{
_button = 0;
_mouse_type = mouse_type;
if (connect_blocking) {
USBDevice::connect();
wait_ready();
} else {
init();
}
}
USBMouse::USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release)
{
_button = 0;
_mouse_type = mouse_type;
}
USBMouse::~USBMouse()
{
deinit();
}
bool USBMouse::update(int16_t x, int16_t y, uint8_t button, int8_t z)
{
bool ret;
switch (_mouse_type) {
case REL_MOUSE:
_mutex.lock();
while (x > 127) {
if (!mouse_send(127, 0, button, z)) {
_mutex.unlock();
return false;
}
x = x - 127;
}
while (x < -128) {
if (!mouse_send(-128, 0, button, z)) {
_mutex.unlock();
return false;
}
x = x + 128;
}
while (y > 127) {
if (!mouse_send(0, 127, button, z)) {
_mutex.unlock();
return false;
}
y = y - 127;
}
while (y < -128) {
if (!mouse_send(0, -128, button, z)) {
_mutex.unlock();
return false;
}
y = y + 128;
}
ret = mouse_send(x, y, button, z);
_mutex.unlock();
return ret;
case ABS_MOUSE:
_mutex.lock();
HID_REPORT report;
report.data[0] = x & 0xff;
report.data[1] = (x >> 8) & 0xff;
report.data[2] = y & 0xff;
report.data[3] = (y >> 8) & 0xff;
report.data[4] = -z;
report.data[5] = button & 0x07;
report.length = 6;
ret = send(&report);
_mutex.unlock();
return ret;
default:
return false;
}
}
bool USBMouse::mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z)
{
_mutex.lock();
HID_REPORT report;
report.data[0] = buttons & 0x07;
report.data[1] = x;
report.data[2] = y;
report.data[3] = -z; // >0 to scroll down, <0 to scroll up
report.length = 4;
bool ret = send(&report);
_mutex.unlock();
return ret;
}
bool USBMouse::move(int16_t x, int16_t y)
{
_mutex.lock();
bool ret = update(x, y, _button, 0);
_mutex.unlock();
return ret;
}
bool USBMouse::scroll(int8_t z)
{
_mutex.lock();
bool ret = update(0, 0, _button, z);
_mutex.unlock();
return ret;
}
bool USBMouse::double_click()
{
_mutex.lock();
if (!click(MOUSE_LEFT)) {
_mutex.unlock();
return false;
}
wait(0.1);
bool ret = click(MOUSE_LEFT);
_mutex.unlock();
return ret;
}
bool USBMouse::click(uint8_t button)
{
_mutex.lock();
if (!update(0, 0, button, 0)) {
_mutex.unlock();
return false;
}
wait(0.01);
bool ret = update(0, 0, 0, 0);
_mutex.unlock();
return ret;
}
bool USBMouse::press(uint8_t button)
{
_mutex.lock();
_button = button & 0x07;
bool ret = update(0, 0, _button, 0);
_mutex.unlock();
return ret;
}
bool USBMouse::release(uint8_t button)
{
_mutex.lock();
_button = (_button & (~button)) & 0x07;
bool ret = update(0, 0, _button, 0);
_mutex.unlock();
return ret;
}
const uint8_t *USBMouse::report_desc()
{
if (_mouse_type == REL_MOUSE) {
static const uint8_t report_descriptor[] = {
USAGE_PAGE(1), 0x01, // Genric Desktop
USAGE(1), 0x02, // Mouse
COLLECTION(1), 0x01, // Application
USAGE(1), 0x01, // Pointer
COLLECTION(1), 0x00, // Physical
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x09, // Buttons
USAGE_MINIMUM(1), 0x1,
USAGE_MAXIMUM(1), 0x3,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
INPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x05,
INPUT(1), 0x01,
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x08,
USAGE_PAGE(1), 0x01,
USAGE(1), 0x30, // X
USAGE(1), 0x31, // Y
USAGE(1), 0x38, // scroll
LOGICAL_MINIMUM(1), 0x81,
LOGICAL_MAXIMUM(1), 0x7f,
INPUT(1), 0x06, // Relative data
END_COLLECTION(0),
END_COLLECTION(0),
};
reportLength = sizeof(report_descriptor);
return report_descriptor;
} else if (_mouse_type == ABS_MOUSE) {
static const uint8_t report_descriptor[] = {
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x02, // Mouse
COLLECTION(1), 0x01, // Application
USAGE(1), 0x01, // Pointer
COLLECTION(1), 0x00, // Physical
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x30, // X
USAGE(1), 0x31, // Y
LOGICAL_MINIMUM(1), 0x00, // 0
LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767
REPORT_SIZE(1), 0x10,
REPORT_COUNT(1), 0x02,
INPUT(1), 0x02, // Data, Variable, Absolute
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x38, // scroll
LOGICAL_MINIMUM(1), 0x81, // -127
LOGICAL_MAXIMUM(1), 0x7f, // 127
REPORT_SIZE(1), 0x08,
REPORT_COUNT(1), 0x01,
INPUT(1), 0x06, // Data, Variable, Relative
USAGE_PAGE(1), 0x09, // Buttons
USAGE_MINIMUM(1), 0x01,
USAGE_MAXIMUM(1), 0x03,
LOGICAL_MINIMUM(1), 0x00, // 0
LOGICAL_MAXIMUM(1), 0x01, // 1
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x01,
INPUT(1), 0x02, // Data, Variable, Absolute
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x05,
INPUT(1), 0x01, // Constant
END_COLLECTION(0),
END_COLLECTION(0)
};
reportLength = sizeof(report_descriptor);
return report_descriptor;
}
return NULL;
}
#define DEFAULT_CONFIGURATION (1)
#define TOTAL_DESCRIPTOR_LENGTH ((1 * CONFIGURATION_DESCRIPTOR_LENGTH) \
+ (1 * INTERFACE_DESCRIPTOR_LENGTH) \
+ (1 * HID_DESCRIPTOR_LENGTH) \
+ (2 * ENDPOINT_DESCRIPTOR_LENGTH))
const uint8_t *USBMouse::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t configuration_descriptor_temp[] = {
CONFIGURATION_DESCRIPTOR_LENGTH, // bLength
CONFIGURATION_DESCRIPTOR, // bDescriptorType
LSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (LSB)
MSB(TOTAL_DESCRIPTOR_LENGTH), // wTotalLength (MSB)
0x01, // bNumInterfaces
DEFAULT_CONFIGURATION, // bConfigurationValue
0x00, // iConfiguration
C_RESERVED | C_SELF_POWERED, // bmAttributes
C_POWER(0), // bMaxPower
INTERFACE_DESCRIPTOR_LENGTH, // bLength
INTERFACE_DESCRIPTOR, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
HID_CLASS, // bInterfaceClass
HID_SUBCLASS_BOOT, // bInterfaceSubClass
HID_PROTOCOL_MOUSE, // bInterfaceProtocol
0x00, // iInterface
HID_DESCRIPTOR_LENGTH, // bLength
HID_DESCRIPTOR, // bDescriptorType
LSB(HID_VERSION_1_11), // bcdHID (LSB)
MSB(HID_VERSION_1_11), // bcdHID (MSB)
0x00, // bCountryCode
0x01, // bNumDescriptors
REPORT_DESCRIPTOR, // bDescriptorType
(uint8_t)(LSB(report_desc_length())), // wDescriptorLength (LSB)
(uint8_t)(MSB(report_desc_length())), // wDescriptorLength (MSB)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_in, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
ENDPOINT_DESCRIPTOR_LENGTH, // bLength
ENDPOINT_DESCRIPTOR, // bDescriptorType
_int_out, // bEndpointAddress
E_INTERRUPT, // bmAttributes
LSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (LSB)
MSB(MAX_HID_REPORT_SIZE), // wMaxPacketSize (MSB)
1, // bInterval (milliseconds)
};
MBED_ASSERT(sizeof(configuration_descriptor_temp) == sizeof(_configuration_descriptor));
memcpy(_configuration_descriptor, configuration_descriptor_temp, sizeof(_configuration_descriptor));
return _configuration_descriptor;
}

View File

@ -0,0 +1,235 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBMOUSE_H
#define USBMOUSE_H
#include "USBHID.h"
#include "PlatformMutex.h"
#define REPORT_ID_MOUSE 2
/* Common usage */
enum MOUSE_BUTTON {
MOUSE_LEFT = 1,
MOUSE_RIGHT = 2,
MOUSE_MIDDLE = 4,
};
/* X and Y limits */
/* These values do not directly map to screen pixels */
/* Zero may be interpreted as meaning 'no movement' */
#define X_MIN_ABS (1) /*!< Minimum value on x-axis */
#define Y_MIN_ABS (1) /*!< Minimum value on y-axis */
#define X_MAX_ABS (0x7fff) /*!< Maximum value on x-axis */
#define Y_MAX_ABS (0x7fff) /*!< Maximum value on y-axis */
#define X_MIN_REL (-127) /*!< The maximum value that we can move to the left on the x-axis */
#define Y_MIN_REL (-127) /*!< The maximum value that we can move up on the y-axis */
#define X_MAX_REL (127) /*!< The maximum value that we can move to the right on the x-axis */
#define Y_MAX_REL (127) /*!< The maximum value that we can move down on the y-axis */
enum MOUSE_TYPE {
ABS_MOUSE,
REL_MOUSE,
};
/**
*
* USBMouse example
* @code
* #include "mbed.h"
* #include "USBMouse.h"
*
* USBMouse mouse;
*
* int main(void)
* {
* while (1)
* {
* mouse.move(20, 0);
* wait(0.5);
* }
* }
*
* @endcode
*
*
* @code
* #include "mbed.h"
* #include "USBMouse.h"
* #include <math.h>
*
* USBMouse mouse(true, ABS_MOUSE);
*
* int main(void)
* {
* uint16_t x_center = (X_MAX_ABS - X_MIN_ABS)/2;
* uint16_t y_center = (Y_MAX_ABS - Y_MIN_ABS)/2;
* uint16_t x_screen = 0;
* uint16_t y_screen = 0;
*
* uint32_t x_origin = x_center;
* uint32_t y_origin = y_center;
* uint32_t radius = 5000;
* uint32_t angle = 0;
*
* while (1)
* {
* x_screen = x_origin + cos((double)angle*3.14/180.0)*radius;
* y_screen = y_origin + sin((double)angle*3.14/180.0)*radius;
*
* mouse.move(x_screen, y_screen);
* angle += 3;
* wait(0.01);
* }
* }
*
* @endcode
*
* @note Synchronization level: Thread safe
*/
class USBMouse: public USBHID {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE)
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMouse(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE)
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMouse(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x1234, uint16_t product_id = 0x0001, uint16_t product_release = 0x0001);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBMouse();
/**
* Write a state of the mouse
*
* @param x x-axis position
* @param y y-axis position
* @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE)
* @param z wheel state (>0 to scroll down, <0 to scroll up)
* @returns true if there is no error, false otherwise
*/
bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z);
/**
* Move the cursor to (x, y)
*
* @param x x-axis position
* @param y y-axis position
* @returns true if there is no error, false otherwise
*/
bool move(int16_t x, int16_t y);
/**
* Press one or several buttons
*
* @param button button state (ex: press(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool press(uint8_t button);
/**
* Release one or several buttons
*
* @param button button state (ex: release(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool release(uint8_t button);
/**
* Double click (MOUSE_LEFT)
*
* @returns true if there is no error, false otherwise
*/
bool double_click();
/**
* Click
*
* @param button state of the buttons ( ex: clic(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool click(uint8_t button);
/**
* Scrolling
*
* @param z value of the wheel (>0 to go down, <0 to go up)
* @returns true if there is no error, false otherwise
*/
bool scroll(int8_t z);
/*
* To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
*
* @returns pointer to the report descriptor
*/
virtual const uint8_t *report_desc();
protected:
/*
* Get configuration descriptor
*
* @returns pointer to the configuration descriptor
*/
virtual const uint8_t *configuration_desc(uint8_t index);
private:
MOUSE_TYPE _mouse_type;
uint8_t _button;
uint8_t _configuration_descriptor[41];
PlatformMutex _mutex;
bool mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z);
};
#endif

View File

@ -0,0 +1,830 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBMouseKeyboard.h"
#include "usb_phy_api.h"
#include "mbed_wait_api.h"
typedef struct {
unsigned char usage;
unsigned char modifier;
} KEYMAP;
#ifdef US_KEYBOARD
/* US keyboard (as HID standard) */
#define KEYMAP_SIZE (152)
const KEYMAP keymap[KEYMAP_SIZE] = {
{0, 0}, /* NUL */
{0, 0}, /* SOH */
{0, 0}, /* STX */
{0, 0}, /* ETX */
{0, 0}, /* EOT */
{0, 0}, /* ENQ */
{0, 0}, /* ACK */
{0, 0}, /* BEL */
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
{0, 0}, /* VT */
{0, 0}, /* FF */
{0, 0}, /* CR */
{0, 0}, /* SO */
{0, 0}, /* SI */
{0, 0}, /* DEL */
{0, 0}, /* DC1 */
{0, 0}, /* DC2 */
{0, 0}, /* DC3 */
{0, 0}, /* DC4 */
{0, 0}, /* NAK */
{0, 0}, /* SYN */
{0, 0}, /* ETB */
{0, 0}, /* CAN */
{0, 0}, /* EM */
{0, 0}, /* SUB */
{0, 0}, /* ESC */
{0, 0}, /* FS */
{0, 0}, /* GS */
{0, 0}, /* RS */
{0, 0}, /* US */
{0x2c, 0}, /* */
{0x1e, KEY_SHIFT}, /* ! */
{0x34, KEY_SHIFT}, /* " */
{0x20, KEY_SHIFT}, /* # */
{0x21, KEY_SHIFT}, /* $ */
{0x22, KEY_SHIFT}, /* % */
{0x24, KEY_SHIFT}, /* & */
{0x34, 0}, /* ' */
{0x26, KEY_SHIFT}, /* ( */
{0x27, KEY_SHIFT}, /* ) */
{0x25, KEY_SHIFT}, /* * */
{0x2e, KEY_SHIFT}, /* + */
{0x36, 0}, /* , */
{0x2d, 0}, /* - */
{0x37, 0}, /* . */
{0x38, 0}, /* / */
{0x27, 0}, /* 0 */
{0x1e, 0}, /* 1 */
{0x1f, 0}, /* 2 */
{0x20, 0}, /* 3 */
{0x21, 0}, /* 4 */
{0x22, 0}, /* 5 */
{0x23, 0}, /* 6 */
{0x24, 0}, /* 7 */
{0x25, 0}, /* 8 */
{0x26, 0}, /* 9 */
{0x33, KEY_SHIFT}, /* : */
{0x33, 0}, /* ; */
{0x36, KEY_SHIFT}, /* < */
{0x2e, 0}, /* = */
{0x37, KEY_SHIFT}, /* > */
{0x38, KEY_SHIFT}, /* ? */
{0x1f, KEY_SHIFT}, /* @ */
{0x04, KEY_SHIFT}, /* A */
{0x05, KEY_SHIFT}, /* B */
{0x06, KEY_SHIFT}, /* C */
{0x07, KEY_SHIFT}, /* D */
{0x08, KEY_SHIFT}, /* E */
{0x09, KEY_SHIFT}, /* F */
{0x0a, KEY_SHIFT}, /* G */
{0x0b, KEY_SHIFT}, /* H */
{0x0c, KEY_SHIFT}, /* I */
{0x0d, KEY_SHIFT}, /* J */
{0x0e, KEY_SHIFT}, /* K */
{0x0f, KEY_SHIFT}, /* L */
{0x10, KEY_SHIFT}, /* M */
{0x11, KEY_SHIFT}, /* N */
{0x12, KEY_SHIFT}, /* O */
{0x13, KEY_SHIFT}, /* P */
{0x14, KEY_SHIFT}, /* Q */
{0x15, KEY_SHIFT}, /* R */
{0x16, KEY_SHIFT}, /* S */
{0x17, KEY_SHIFT}, /* T */
{0x18, KEY_SHIFT}, /* U */
{0x19, KEY_SHIFT}, /* V */
{0x1a, KEY_SHIFT}, /* W */
{0x1b, KEY_SHIFT}, /* X */
{0x1c, KEY_SHIFT}, /* Y */
{0x1d, KEY_SHIFT}, /* Z */
{0x2f, 0}, /* [ */
{0x31, 0}, /* \ */
{0x30, 0}, /* ] */
{0x23, KEY_SHIFT}, /* ^ */
{0x2d, KEY_SHIFT}, /* _ */
{0x35, 0}, /* ` */
{0x04, 0}, /* a */
{0x05, 0}, /* b */
{0x06, 0}, /* c */
{0x07, 0}, /* d */
{0x08, 0}, /* e */
{0x09, 0}, /* f */
{0x0a, 0}, /* g */
{0x0b, 0}, /* h */
{0x0c, 0}, /* i */
{0x0d, 0}, /* j */
{0x0e, 0}, /* k */
{0x0f, 0}, /* l */
{0x10, 0}, /* m */
{0x11, 0}, /* n */
{0x12, 0}, /* o */
{0x13, 0}, /* p */
{0x14, 0}, /* q */
{0x15, 0}, /* r */
{0x16, 0}, /* s */
{0x17, 0}, /* t */
{0x18, 0}, /* u */
{0x19, 0}, /* v */
{0x1a, 0}, /* w */
{0x1b, 0}, /* x */
{0x1c, 0}, /* y */
{0x1d, 0}, /* z */
{0x2f, KEY_SHIFT}, /* { */
{0x31, KEY_SHIFT}, /* | */
{0x30, KEY_SHIFT}, /* } */
{0x35, KEY_SHIFT}, /* ~ */
{0, 0}, /* DEL */
{0x3a, 0}, /* F1 */
{0x3b, 0}, /* F2 */
{0x3c, 0}, /* F3 */
{0x3d, 0}, /* F4 */
{0x3e, 0}, /* F5 */
{0x3f, 0}, /* F6 */
{0x40, 0}, /* F7 */
{0x41, 0}, /* F8 */
{0x42, 0}, /* F9 */
{0x43, 0}, /* F10 */
{0x44, 0}, /* F11 */
{0x45, 0}, /* F12 */
{0x46, 0}, /* PRINT_SCREEN */
{0x47, 0}, /* SCROLL_LOCK */
{0x39, 0}, /* CAPS_LOCK */
{0x53, 0}, /* NUM_LOCK */
{0x49, 0}, /* INSERT */
{0x4a, 0}, /* HOME */
{0x4b, 0}, /* PAGE_UP */
{0x4e, 0}, /* PAGE_DOWN */
{0x4f, 0}, /* RIGHT_ARROW */
{0x50, 0}, /* LEFT_ARROW */
{0x51, 0}, /* DOWN_ARROW */
{0x52, 0}, /* UP_ARROW */
};
#else
/* UK keyboard */
#define KEYMAP_SIZE (152)
const KEYMAP keymap[KEYMAP_SIZE] = {
{0, 0}, /* NUL */
{0, 0}, /* SOH */
{0, 0}, /* STX */
{0, 0}, /* ETX */
{0, 0}, /* EOT */
{0, 0}, /* ENQ */
{0, 0}, /* ACK */
{0, 0}, /* BEL */
{0x2a, 0}, /* BS */ /* Keyboard Delete (Backspace) */
{0x2b, 0}, /* TAB */ /* Keyboard Tab */
{0x28, 0}, /* LF */ /* Keyboard Return (Enter) */
{0, 0}, /* VT */
{0, 0}, /* FF */
{0, 0}, /* CR */
{0, 0}, /* SO */
{0, 0}, /* SI */
{0, 0}, /* DEL */
{0, 0}, /* DC1 */
{0, 0}, /* DC2 */
{0, 0}, /* DC3 */
{0, 0}, /* DC4 */
{0, 0}, /* NAK */
{0, 0}, /* SYN */
{0, 0}, /* ETB */
{0, 0}, /* CAN */
{0, 0}, /* EM */
{0, 0}, /* SUB */
{0, 0}, /* ESC */
{0, 0}, /* FS */
{0, 0}, /* GS */
{0, 0}, /* RS */
{0, 0}, /* US */
{0x2c, 0}, /* */
{0x1e, KEY_SHIFT}, /* ! */
{0x1f, KEY_SHIFT}, /* " */
{0x32, 0}, /* # */
{0x21, KEY_SHIFT}, /* $ */
{0x22, KEY_SHIFT}, /* % */
{0x24, KEY_SHIFT}, /* & */
{0x34, 0}, /* ' */
{0x26, KEY_SHIFT}, /* ( */
{0x27, KEY_SHIFT}, /* ) */
{0x25, KEY_SHIFT}, /* * */
{0x2e, KEY_SHIFT}, /* + */
{0x36, 0}, /* , */
{0x2d, 0}, /* - */
{0x37, 0}, /* . */
{0x38, 0}, /* / */
{0x27, 0}, /* 0 */
{0x1e, 0}, /* 1 */
{0x1f, 0}, /* 2 */
{0x20, 0}, /* 3 */
{0x21, 0}, /* 4 */
{0x22, 0}, /* 5 */
{0x23, 0}, /* 6 */
{0x24, 0}, /* 7 */
{0x25, 0}, /* 8 */
{0x26, 0}, /* 9 */
{0x33, KEY_SHIFT}, /* : */
{0x33, 0}, /* ; */
{0x36, KEY_SHIFT}, /* < */
{0x2e, 0}, /* = */
{0x37, KEY_SHIFT}, /* > */
{0x38, KEY_SHIFT}, /* ? */
{0x34, KEY_SHIFT}, /* @ */
{0x04, KEY_SHIFT}, /* A */
{0x05, KEY_SHIFT}, /* B */
{0x06, KEY_SHIFT}, /* C */
{0x07, KEY_SHIFT}, /* D */
{0x08, KEY_SHIFT}, /* E */
{0x09, KEY_SHIFT}, /* F */
{0x0a, KEY_SHIFT}, /* G */
{0x0b, KEY_SHIFT}, /* H */
{0x0c, KEY_SHIFT}, /* I */
{0x0d, KEY_SHIFT}, /* J */
{0x0e, KEY_SHIFT}, /* K */
{0x0f, KEY_SHIFT}, /* L */
{0x10, KEY_SHIFT}, /* M */
{0x11, KEY_SHIFT}, /* N */
{0x12, KEY_SHIFT}, /* O */
{0x13, KEY_SHIFT}, /* P */
{0x14, KEY_SHIFT}, /* Q */
{0x15, KEY_SHIFT}, /* R */
{0x16, KEY_SHIFT}, /* S */
{0x17, KEY_SHIFT}, /* T */
{0x18, KEY_SHIFT}, /* U */
{0x19, KEY_SHIFT}, /* V */
{0x1a, KEY_SHIFT}, /* W */
{0x1b, KEY_SHIFT}, /* X */
{0x1c, KEY_SHIFT}, /* Y */
{0x1d, KEY_SHIFT}, /* Z */
{0x2f, 0}, /* [ */
{0x64, 0}, /* \ */
{0x30, 0}, /* ] */
{0x23, KEY_SHIFT}, /* ^ */
{0x2d, KEY_SHIFT}, /* _ */
{0x35, 0}, /* ` */
{0x04, 0}, /* a */
{0x05, 0}, /* b */
{0x06, 0}, /* c */
{0x07, 0}, /* d */
{0x08, 0}, /* e */
{0x09, 0}, /* f */
{0x0a, 0}, /* g */
{0x0b, 0}, /* h */
{0x0c, 0}, /* i */
{0x0d, 0}, /* j */
{0x0e, 0}, /* k */
{0x0f, 0}, /* l */
{0x10, 0}, /* m */
{0x11, 0}, /* n */
{0x12, 0}, /* o */
{0x13, 0}, /* p */
{0x14, 0}, /* q */
{0x15, 0}, /* r */
{0x16, 0}, /* s */
{0x17, 0}, /* t */
{0x18, 0}, /* u */
{0x19, 0}, /* v */
{0x1a, 0}, /* w */
{0x1b, 0}, /* x */
{0x1c, 0}, /* y */
{0x1d, 0}, /* z */
{0x2f, KEY_SHIFT}, /* { */
{0x64, KEY_SHIFT}, /* | */
{0x30, KEY_SHIFT}, /* } */
{0x32, KEY_SHIFT}, /* ~ */
{0, 0}, /* DEL */
{0x3a, 0}, /* F1 */
{0x3b, 0}, /* F2 */
{0x3c, 0}, /* F3 */
{0x3d, 0}, /* F4 */
{0x3e, 0}, /* F5 */
{0x3f, 0}, /* F6 */
{0x40, 0}, /* F7 */
{0x41, 0}, /* F8 */
{0x42, 0}, /* F9 */
{0x43, 0}, /* F10 */
{0x44, 0}, /* F11 */
{0x45, 0}, /* F12 */
{0x46, 0}, /* PRINT_SCREEN */
{0x47, 0}, /* SCROLL_LOCK */
{0x39, 0}, /* CAPS_LOCK */
{0x53, 0}, /* NUM_LOCK */
{0x49, 0}, /* INSERT */
{0x4a, 0}, /* HOME */
{0x4b, 0}, /* PAGE_UP */
{0x4e, 0}, /* PAGE_DOWN */
{0x4f, 0}, /* RIGHT_ARROW */
{0x50, 0}, /* LEFT_ARROW */
{0x51, 0}, /* DOWN_ARROW */
{0x52, 0}, /* UP_ARROW */
};
#endif
USBMouseKeyboard::USBMouseKeyboard(bool connect_blocking, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(get_usb_phy(), 0, 0, vendor_id, product_id, product_release)
{
_lock_status = 0;
_button = 0;
_mouse_type = mouse_type;
if (connect_blocking) {
USBDevice::connect();
wait_ready();
} else {
init();
}
};
USBMouseKeyboard::USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBHID(phy, 0, 0, vendor_id, product_id, product_release)
{
_lock_status = 0;
_button = 0;
_mouse_type = mouse_type;
};
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
USBMouseKeyboard::~USBMouseKeyboard()
{
deinit();
}
const uint8_t *USBMouseKeyboard::report_desc()
{
if (_mouse_type == REL_MOUSE) {
static const uint8_t reportDescriptor[] = {
// Keyboard
USAGE_PAGE(1), 0x01,
USAGE(1), 0x06,
COLLECTION(1), 0x01,
REPORT_ID(1), REPORT_ID_KEYBOARD,
USAGE_PAGE(1), 0x07,
USAGE_MINIMUM(1), 0xE0,
USAGE_MAXIMUM(1), 0xE7,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x08,
INPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x08,
INPUT(1), 0x01,
REPORT_COUNT(1), 0x05,
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x08,
USAGE_MINIMUM(1), 0x01,
USAGE_MAXIMUM(1), 0x05,
OUTPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x03,
OUTPUT(1), 0x01,
REPORT_COUNT(1), 0x06,
REPORT_SIZE(1), 0x08,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(2), 0xff, 0x00,
USAGE_PAGE(1), 0x07,
USAGE_MINIMUM(1), 0x00,
USAGE_MAXIMUM(2), 0xff, 0x00,
INPUT(1), 0x00,
END_COLLECTION(0),
// Mouse
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x02, // Mouse
COLLECTION(1), 0x01, // Application
USAGE(1), 0x01, // Pointer
COLLECTION(1), 0x00, // Physical
REPORT_ID(1), REPORT_ID_MOUSE,
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x09, // Buttons
USAGE_MINIMUM(1), 0x1,
USAGE_MAXIMUM(1), 0x3,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
INPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x05,
INPUT(1), 0x01,
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x08,
USAGE_PAGE(1), 0x01,
USAGE(1), 0x30, // X
USAGE(1), 0x31, // Y
USAGE(1), 0x38, // scroll
LOGICAL_MINIMUM(1), 0x81,
LOGICAL_MAXIMUM(1), 0x7f,
INPUT(1), 0x06,
END_COLLECTION(0),
END_COLLECTION(0),
// Media Control
USAGE_PAGE(1), 0x0C,
USAGE(1), 0x01,
COLLECTION(1), 0x01,
REPORT_ID(1), REPORT_ID_VOLUME,
USAGE_PAGE(1), 0x0C,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x07,
USAGE(1), 0xB5, // Next Track
USAGE(1), 0xB6, // Previous Track
USAGE(1), 0xB7, // Stop
USAGE(1), 0xCD, // Play / Pause
USAGE(1), 0xE2, // Mute
USAGE(1), 0xE9, // Volume Up
USAGE(1), 0xEA, // Volume Down
INPUT(1), 0x02, // Input (Data, Variable, Absolute)
REPORT_COUNT(1), 0x01,
INPUT(1), 0x01,
END_COLLECTION(0),
};
reportLength = sizeof(reportDescriptor);
return reportDescriptor;
} else if (_mouse_type == ABS_MOUSE) {
static const uint8_t reportDescriptor[] = {
// Keyboard
USAGE_PAGE(1), 0x01,
USAGE(1), 0x06,
COLLECTION(1), 0x01,
REPORT_ID(1), REPORT_ID_KEYBOARD,
USAGE_PAGE(1), 0x07,
USAGE_MINIMUM(1), 0xE0,
USAGE_MAXIMUM(1), 0xE7,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x08,
INPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x08,
INPUT(1), 0x01,
REPORT_COUNT(1), 0x05,
REPORT_SIZE(1), 0x01,
USAGE_PAGE(1), 0x08,
USAGE_MINIMUM(1), 0x01,
USAGE_MAXIMUM(1), 0x05,
OUTPUT(1), 0x02,
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x03,
OUTPUT(1), 0x01,
REPORT_COUNT(1), 0x06,
REPORT_SIZE(1), 0x08,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(2), 0xff, 0x00,
USAGE_PAGE(1), 0x07,
USAGE_MINIMUM(1), 0x00,
USAGE_MAXIMUM(2), 0xff, 0x00,
INPUT(1), 0x00,
END_COLLECTION(0),
// Mouse
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x02, // Mouse
COLLECTION(1), 0x01, // Application
USAGE(1), 0x01, // Pointer
COLLECTION(1), 0x00, // Physical
REPORT_ID(1), REPORT_ID_MOUSE,
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x30, // X
USAGE(1), 0x31, // Y
LOGICAL_MINIMUM(1), 0x00, // 0
LOGICAL_MAXIMUM(2), 0xff, 0x7f, // 32767
REPORT_SIZE(1), 0x10,
REPORT_COUNT(1), 0x02,
INPUT(1), 0x02, // Data, Variable, Absolute
USAGE_PAGE(1), 0x01, // Generic Desktop
USAGE(1), 0x38, // scroll
LOGICAL_MINIMUM(1), 0x81, // -127
LOGICAL_MAXIMUM(1), 0x7f, // 127
REPORT_SIZE(1), 0x08,
REPORT_COUNT(1), 0x01,
INPUT(1), 0x06, // Data, Variable, Relative
USAGE_PAGE(1), 0x09, // Buttons
USAGE_MINIMUM(1), 0x01,
USAGE_MAXIMUM(1), 0x03,
LOGICAL_MINIMUM(1), 0x00, // 0
LOGICAL_MAXIMUM(1), 0x01, // 1
REPORT_COUNT(1), 0x03,
REPORT_SIZE(1), 0x01,
INPUT(1), 0x02, // Data, Variable, Absolute
REPORT_COUNT(1), 0x01,
REPORT_SIZE(1), 0x05,
INPUT(1), 0x01, // Constant
END_COLLECTION(0),
END_COLLECTION(0),
// Media Control
USAGE_PAGE(1), 0x0C,
USAGE(1), 0x01,
COLLECTION(1), 0x01,
REPORT_ID(1), REPORT_ID_VOLUME,
USAGE_PAGE(1), 0x0C,
LOGICAL_MINIMUM(1), 0x00,
LOGICAL_MAXIMUM(1), 0x01,
REPORT_SIZE(1), 0x01,
REPORT_COUNT(1), 0x07,
USAGE(1), 0xB5, // Next Track
USAGE(1), 0xB6, // Previous Track
USAGE(1), 0xB7, // Stop
USAGE(1), 0xCD, // Play / Pause
USAGE(1), 0xE2, // Mute
USAGE(1), 0xE9, // Volume Up
USAGE(1), 0xEA, // Volume Down
INPUT(1), 0x02, // Input (Data, Variable, Absolute)
REPORT_COUNT(1), 0x01,
INPUT(1), 0x01,
END_COLLECTION(0),
};
reportLength = sizeof(reportDescriptor);
return reportDescriptor;
}
return NULL;
}
void USBMouseKeyboard::report_rx()
{
assert_locked();
HID_REPORT report;
read_nb(&report);
// we take [1] because [0] is the report ID
_lock_status = report.data[1] & 0x07;
}
uint8_t USBMouseKeyboard::lock_status()
{
return _lock_status;
}
bool USBMouseKeyboard::update(int16_t x, int16_t y, uint8_t button, int8_t z)
{
bool ret;
switch (_mouse_type) {
case REL_MOUSE:
_mutex.lock();
while (x > 127) {
if (!_mouse_send(127, 0, button, z)) {
_mutex.unlock();
return false;
}
x = x - 127;
}
while (x < -128) {
if (!_mouse_send(-128, 0, button, z)) {
_mutex.unlock();
return false;
}
x = x + 128;
}
while (y > 127) {
if (!_mouse_send(0, 127, button, z)) {
_mutex.unlock();
return false;
}
y = y - 127;
}
while (y < -128) {
if (!_mouse_send(0, -128, button, z)) {
_mutex.unlock();
return false;
}
y = y + 128;
}
ret = _mouse_send(x, y, button, z);
_mutex.unlock();
return ret;
case ABS_MOUSE:
_mutex.lock();
HID_REPORT report;
report.data[0] = REPORT_ID_MOUSE;
report.data[1] = x & 0xff;
report.data[2] = (x >> 8) & 0xff;
report.data[3] = y & 0xff;
report.data[4] = (y >> 8) & 0xff;
report.data[5] = -z;
report.data[6] = button & 0x07;
report.length = 7;
ret = send(&report);
_mutex.unlock();
return ret;
default:
return false;
}
}
bool USBMouseKeyboard::_mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z)
{
_mutex.lock();
HID_REPORT report;
report.data[0] = REPORT_ID_MOUSE;
report.data[1] = buttons & 0x07;
report.data[2] = x;
report.data[3] = y;
report.data[4] = -z; // >0 to scroll down, <0 to scroll up
report.length = 5;
bool ret = send(&report);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::move(int16_t x, int16_t y)
{
_mutex.lock();
bool ret = update(x, y, _button, 0);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::scroll(int8_t z)
{
_mutex.lock();
bool ret = update(0, 0, _button, z);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::doubleClick()
{
_mutex.lock();
if (!click(MOUSE_LEFT)) {
_mutex.unlock();
return false;
}
wait(0.1);
bool ret = click(MOUSE_LEFT);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::click(uint8_t button)
{
_mutex.lock();
if (!update(0, 0, button, 0)) {
_mutex.unlock();
return false;
}
wait(0.01);
bool ret = update(0, 0, 0, 0);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::press(uint8_t button)
{
_mutex.lock();
_button = button & 0x07;
bool ret = update(0, 0, button, 0);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::release(uint8_t button)
{
_mutex.lock();
_button = (_button & (~button)) & 0x07;
bool ret = update(0, 0, _button, 0);
_mutex.unlock();
return ret;
}
int USBMouseKeyboard::_putc(int c)
{
_mutex.lock();
bool ret = key_code(c, keymap[c].modifier);
_mutex.unlock();
return ret;
}
bool USBMouseKeyboard::key_code(uint8_t key, uint8_t modifier)
{
// Send a simulated keyboard keypress. Returns true if successful.
_mutex.lock();
HID_REPORT report;
report.data[0] = REPORT_ID_KEYBOARD;
report.data[1] = modifier;
report.data[2] = 0;
report.data[3] = keymap[key].usage;
report.data[4] = 0;
report.data[5] = 0;
report.data[6] = 0;
report.data[7] = 0;
report.data[8] = 0;
report.length = 9;
if (!send(&report)) {
_mutex.unlock();
return false;
}
report.data[1] = 0;
report.data[3] = 0;
if (!send(&report)) {
_mutex.unlock();
return false;
}
_mutex.unlock();
return true;
}
bool USBMouseKeyboard::media_control(MEDIA_KEY key)
{
_mutex.lock();
HID_REPORT report;
report.data[0] = REPORT_ID_VOLUME;
report.data[1] = (1 << key) & 0x7f;
report.length = 2;
send(&report);
report.data[0] = REPORT_ID_VOLUME;
report.data[1] = 0;
report.length = 2;
bool ret = send(&report);
_mutex.unlock();
return ret;
}
int USBMouseKeyboard::_getc()
{
return -1;
}

View File

@ -0,0 +1,246 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBMOUSEKEYBOARD_H
#define USBMOUSEKEYBOARD_H
#define REPORT_ID_KEYBOARD 1
#define REPORT_ID_MOUSE 2
#define REPORT_ID_VOLUME 3
#include "USBMouse.h"
#include "USBKeyboard.h"
#include "Stream.h"
#include "USBHID.h"
#include "PlatformMutex.h"
/**
* USBMouseKeyboard example
* @code
*
* #include "mbed.h"
* #include "USBMouseKeyboard.h"
*
* USBMouseKeyboard key_mouse;
*
* int main(void)
* {
* while(1)
* {
* key_mouse.move(20, 0);
* key_mouse.printf("Hello From MBED\r\n");
* wait(1);
* }
* }
* @endcode
*
*
* @code
*
* #include "mbed.h"
* #include "USBMouseKeyboard.h"
*
* USBMouseKeyboard key_mouse(ABS_MOUSE);
*
* int main(void)
* {
* while(1)
* {
* key_mouse.move(X_MAX_ABS/2, Y_MAX_ABS/2);
* key_mouse.printf("Hello from MBED\r\n");
* wait(1);
* }
* }
* @endcode
*
* @note Synchronization level: Thread safe
*/
class USBMouseKeyboard: public USBHID, public mbed::Stream {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE)
* @param vendor_id Your vendor_id (default: 0x1234)
* @param product_id Your product_id (default: 0x0001)
* @param product_release Your preoduct_release (default: 0x0001)
*
*/
USBMouseKeyboard(bool connect_blocking = true, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param mouse_type Mouse type: ABS_MOUSE (absolute mouse) or REL_MOUSE (relative mouse) (default: REL_MOUSE)
* @param vendor_id Your vendor_id (default: 0x1234)
* @param product_id Your product_id (default: 0x0001)
* @param product_release Your preoduct_release (default: 0x0001)
*
*/
USBMouseKeyboard(USBPhy *phy, MOUSE_TYPE mouse_type = REL_MOUSE, uint16_t vendor_id = 0x0021, uint16_t product_id = 0x0011, uint16_t product_release = 0x0001);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBMouseKeyboard();
/**
* Write a state of the mouse
*
* @param x x-axis position
* @param y y-axis position
* @param buttons buttons state (first bit represents MOUSE_LEFT, second bit MOUSE_RIGHT and third bit MOUSE_MIDDLE)
* @param z wheel state (>0 to scroll down, <0 to scroll up)
* @returns true if there is no error, false otherwise
*/
bool update(int16_t x, int16_t y, uint8_t buttons, int8_t z);
/**
* Move the cursor to (x, y)
*
* @param x x-axis position
* @param y y-axis position
* @returns true if there is no error, false otherwise
*/
bool move(int16_t x, int16_t y);
/**
* Press one or several buttons
*
* @param button button state (ex: press(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool press(uint8_t button);
/**
* Release one or several buttons
*
* @param button button state (ex: release(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool release(uint8_t button);
/**
* Double click (MOUSE_LEFT)
*
* @returns true if there is no error, false otherwise
*/
bool doubleClick();
/**
* Click
*
* @param button state of the buttons ( ex: clic(MOUSE_LEFT))
* @returns true if there is no error, false otherwise
*/
bool click(uint8_t button);
/**
* Scrolling
*
* @param z value of the wheel (>0 to go down, <0 to go up)
* @returns true if there is no error, false otherwise
*/
bool scroll(int8_t z);
/**
* To send a character defined by a modifier(CTRL, SHIFT, ALT) and the key
*
* @code
* //To send CTRL + s (save)
* keyboard.keyCode('s', KEY_CTRL);
* @endcode
*
* @param modifier bit 0: KEY_CTRL, bit 1: KEY_SHIFT, bit 2: KEY_ALT (default: 0)
* @param key character to send
* @returns true if there is no error, false otherwise
*/
bool key_code(uint8_t key, uint8_t modifier = 0);
/**
* Send a character
*
* @param c character to be sent
* @returns true if there is no error, false otherwise
*/
virtual int _putc(int c);
/**
* Control media keys
*
* @param key media key pressed (KEY_NEXT_TRACK, KEY_PREVIOUS_TRACK, KEY_STOP, KEY_PLAY_PAUSE, KEY_MUTE, KEY_VOLUME_UP, KEY_VOLUME_DOWN)
* @returns true if there is no error, false otherwise
*/
bool media_control(MEDIA_KEY key);
/**
* Read status of lock keys. Useful to switch-on/off leds according to key pressed. Only the first three bits of the result is important:
* - First bit: NUM_LOCK
* - Second bit: CAPS_LOCK
* - Third bit: SCROLL_LOCK
*
* @returns status of lock keys
*/
uint8_t lock_status();
/*
* To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
*
* @returns pointer to the report descriptor
*/
virtual const uint8_t *report_desc();
/*
* Called when a data is received on the OUT endpoint. Useful to switch on LED of LOCK keys
*
* @returns if handle by subclass, return true
*/
virtual void report_rx();
private:
MOUSE_TYPE _mouse_type;
uint8_t _button;
uint8_t _lock_status;
PlatformMutex _mutex;
bool _mouse_send(int8_t x, int8_t y, uint8_t buttons, int8_t z);
//dummy otherwise it doesn't compile (we must define all methods of an abstract class)
virtual int _getc();
};
#endif

View File

@ -0,0 +1,475 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MIDIMESSAGE_H
#define MIDIMESSAGE_H
#include <stdint.h>
#define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage
// MIDI Message Format
//
// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ]
//
// MIDI Data Messages (Channel Specific)
//
// Message msg n m
// ---------------------------------------------
// Note Off 0x8 Key Velocity
// Note On 0x9 Key Velocity
// Polyphonic Aftertouch 0xA Key Pressure
// Control Change 0xB Controller Value
// Program Change 0xC Program -
// Channel Aftertouch 0xD Pressure -
// Pitch Wheel 0xE LSB MSB
#define CABLE_NUM (0<<4)
/** A MIDI message container */
class MIDIMessage {
public:
MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {}
MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0)
{
for (int i = 0; i < 4; i++) {
data[i] = buf[i];
}
}
/**
* Copy constructor
*/
MIDIMessage(const MIDIMessage &other)
{
*this = other;
}
/**
* Assignment operator
*/
MIDIMessage &operator=(const MIDIMessage &other)
{
length = other.length;
for (int i = 0; i < length; i++) {
data[i] = other.data[i];
}
return *this;
}
~MIDIMessage()
{
delete[] data;
}
/**
* Set this MIDIMessage to a raw MIDI message
*
* @param buf is a true MIDI message (not USBMidi message)
* @param buf_len size of message
*/
void from_raw(uint8_t *buf, int buf_len)
{
length = buf_len + 1;
if (length > MAX_MIDI_MESSAGE_SIZE) {
// Message is too big
length = 0;
return;
}
// first byte keeped for retro-compatibility
data[0] = CABLE_NUM | 0x08;
for (int i = 0; i < buf_len; i++) {
data[i + 1] = buf[i];
}
}
// create messages
/** Create a NoteOff message
* @param key Key ID
* @param velocity Key velocity (0-127, default = 127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x08;
msg.data[1] = 0x80 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = velocity & 0x7F;
msg.length = 4;
return msg;
}
/** Create a NoteOn message
* @param key Key ID
* @param velocity Key velocity (0-127, default = 127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x09;
msg.data[1] = 0x90 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = velocity & 0x7F;
msg.length = 4;
return msg;
}
/** Create a PolyPhonic Aftertouch message
* @param key Key ID
* @param pressure Aftertouch pressure (0-127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0A;
msg.data[1] = 0xA0 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = pressure & 0x7F;
msg.length = 4;
return msg;
}
/** Create a Control Change message
* @param control Controller ID
* @param value Controller value (0-127)
* @param channel Controller channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ControlChange(int control, int value, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0B;
msg.data[1] = 0xB0 | (channel & 0x0F);
msg.data[2] = control & 0x7F;
msg.data[3] = value & 0x7F;
msg.length = 4;
return msg;
}
/** Create a Program Change message
* @param program Program ID
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ProgramChange(int program, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0C;
msg.data[1] = 0xC0 | (channel & 0x0F);
msg.data[2] = program & 0x7F;
msg.data[3] = 0x00;
msg.length = 4;
return msg;
}
/** Create a Channel Aftertouch message
* @param pressure Pressure
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ChannelAftertouch(int pressure, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0D;
msg.data[1] = 0xD0 | (channel & 0x0F);
msg.data[2] = pressure & 0x7F;
msg.data[3] = 0x00;
msg.length = 4;
return msg;
}
/** Create a Pitch Wheel message
* @param pitch Pitch (-8192 - 8191, default = 0)
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage PitchWheel(int pitch = 0, int channel = 0)
{
MIDIMessage msg;
int p = pitch + 8192; // 0 - 16383, 8192 is center
msg.data[0] = CABLE_NUM | 0x0E;
msg.data[1] = 0xE0 | (channel & 0x0F);
msg.data[2] = p & 0x7F;
msg.data[3] = (p >> 7) & 0x7F;
msg.length = 4;
return msg;
}
/** Create an All Notes Off message
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage AllNotesOff(int channel = 0)
{
return ControlChange(123, 0, channel);
}
/** Create a SysEx message
* @param data SysEx data (including 0xF0 .. 0xF7)
* @param len SysEx data length
* @returns A MIDIMessage
*/
static MIDIMessage SysEx(uint8_t *data, int len)
{
MIDIMessage msg;
msg.from_raw(data, len);
return msg;
}
// decode messages
/** MIDI Message Types */
enum MIDIMessageType {
ErrorType,
NoteOffType,
NoteOnType,
PolyphonicAftertouchType,
ControlChangeType,
ProgramChangeType,
ChannelAftertouchType,
PitchWheelType,
ResetAllControllersType,
AllNotesOffType,
SysExType
};
/** Read the message type
*
* @returns MIDIMessageType
*/
MIDIMessageType type()
{
MIDIMessageType message_type;
uint8_t min_size;
switch ((data[1] >> 4) & 0xF) {
case 0x8:
// message, channel
// key
// velocity
min_size = 3;
message_type = NoteOffType;
break;
case 0x9:
// message, channel
// key
// velocity
min_size = 3;
message_type = NoteOnType;
break;
case 0xA:
// message, channel
// key
// pressure
min_size = 3;
message_type = PolyphonicAftertouchType;
break;
case 0xB:
// message, channel
// controller
min_size = 2;
if ((data[2] & 0x7F) < 120) { // standard controllers
message_type = ControlChangeType;
} else if ((data[2] & 0x7F) == 121) {
message_type = ResetAllControllersType;
} else if ((data[2] & 0x7F) == 123) {
message_type = AllNotesOffType;
} else {
message_type = ErrorType; // unsupported atm
}
break;
case 0xC:
// message, channel
// program
min_size = 2;
message_type = ProgramChangeType;
break;
case 0xD:
// message, channel
// pressure
min_size = 2;
message_type = ChannelAftertouchType;
break;
case 0xE:
// message, channel
// pitch lsb
// pitch msb
min_size = 3;
message_type = PitchWheelType;
break;
case 0xF:
min_size = 2;
message_type = SysExType;
break;
default:
message_type = ErrorType;
break;
}
if (length < min_size) {
// too small to be a valid message
message_type = ErrorType;
}
return message_type;
}
/**
* Read the channel number
*
* @return channel number or -1 on error
*/
int channel()
{
return (data[1] & 0x0F);
}
/**
* Read the key ID
*
* @return key ID or -1 on error
*/
int key()
{
MIDIMessageType msg_type = type();
if ((msg_type != NoteOffType) &&
(msg_type != NoteOnType) &&
(msg_type != PolyphonicAftertouchType)) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the velocity
*
* @return velocity or -1 on error
*/
int velocity()
{
MIDIMessageType msg_type = type();
if ((msg_type != NoteOffType) &&
(msg_type != NoteOnType)) {
return -1;
}
return data[3] & 0x7F;
}
/**
* Read the controller value
*
* @return controller value or -1 on error
*/
int value()
{
MIDIMessageType msg_type = type();
if ((msg_type != ControlChangeType) &&
(msg_type != ResetAllControllersType) &&
(msg_type != AllNotesOffType)) {
return -1;
}
return data[3] & 0x7F;
}
/**
* Read the aftertouch pressure
*
* @return aftertouch pressure or -1 on error
*/
int pressure()
{
MIDIMessageType msg_type = type();
if ((msg_type != PolyphonicAftertouchType) &&
(msg_type != ChannelAftertouchType)) {
return -1;
}
if (type() == PolyphonicAftertouchType) {
return data[3] & 0x7F;
} else {
return data[2] & 0x7F;
}
}
/**
* Read the controller number
*
* @return controller number or -1 on error
*/
int controller()
{
MIDIMessageType msg_type = type();
if ((msg_type != ControlChangeType) &&
(msg_type != ResetAllControllersType) &&
(msg_type != AllNotesOffType)) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the program number
*
* @return program number or -1 on error
*/
int program()
{
MIDIMessageType msg_type = type();
if (msg_type != ProgramChangeType) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the pitch value
*
* @return pitch value or -1 on error
*/
int pitch()
{
MIDIMessageType msg_type = type();
if (msg_type != PitchWheelType) {
return -1;
}
int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F);
return p - 8192; // 0 - 16383, 8192 is center
}
uint8_t *data;
uint16_t length;
};
#endif

View File

@ -0,0 +1,398 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBMIDI.h"
#include "EndpointResolver.h"
#include "usb_phy_api.h"
#define FLAG_WRITE_DONE (1 << 0)
#define FLAG_DISCONNECT (1 << 1)
#define FLAG_CONNECT (1 << 2)
USBMIDI::USBMIDI(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
{
_init();
if (connect_blocking) {
USBDevice::connect();
wait_ready();
} else {
init();
}
}
USBMIDI::USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init();
// User or child responsible for calling connect or init
}
USBMIDI::~USBMIDI()
{
deinit();
}
void USBMIDI::_init()
{
_bulk_buf_pos = 0;
_bulk_buf_size = 0;
_data_ready = false;
_cur_data = 0;
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(64);
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MaxSize);
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MaxSize);
MBED_ASSERT(resolver.valid());
}
bool USBMIDI::ready()
{
return _flags.get() & FLAG_CONNECT ? true : false;
}
void USBMIDI::wait_ready()
{
_flags.wait_any(FLAG_CONNECT, osWaitForever, false);
}
// write plain MIDIMessage that will be converted to USBMidi event packet
bool USBMIDI::write(MIDIMessage m)
{
_write_mutex.lock();
bool ret = true;
// first byte keeped for retro-compatibility
for (int p = 1; p < m.length; p += 3) {
uint8_t buf[4];
// Midi message to USBMidi event packet
buf[0] = m.data[1] >> 4;
// SysEx
if (buf[0] == 0xF) {
if ((m.length - p) > 3) {
// SysEx start or continue
buf[0] = 0x4;
} else {
switch (m.length - p) {
case 1:
// SysEx end with one byte
buf[0] = 0x5;
break;
case 2:
// SysEx end with two bytes
buf[0] = 0x6;
break;
case 3:
// SysEx end with three bytes
buf[0] = 0x7;
break;
}
}
}
buf[1] = m.data[p];
if (p + 1 < m.length) {
buf[2] = m.data[p + 1];
} else {
buf[2] = 0;
}
if (p + 2 < m.length) {
buf[3] = m.data[p + 2];
} else {
buf[3] = 0;
}
_flags.clear(FLAG_WRITE_DONE);
USBDevice::write_start(_bulk_in, buf, 4);
uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false);
if (flags & FLAG_DISCONNECT) {
ret = false;
break;
}
USBDevice::write_finish(_bulk_in);
}
_write_mutex.unlock();
return ret;
}
bool USBMIDI::readable()
{
lock();
bool ret = _data_ready;
unlock();
return ret;
}
bool USBMIDI::read(MIDIMessage *m)
{
lock();
// Invalidate message
m->length = 0;
if (!_data_ready) {
unlock();
return false;
}
m->from_raw(_data, _cur_data);
_cur_data = 0;
_next_message();
if (!_data_ready) {
read_start(_bulk_out, _bulk_buf, MaxSize);
}
unlock();
return true;
}
void USBMIDI::attach(mbed::Callback<void()> callback)
{
lock();
_callback = callback;
unlock();
}
void USBMIDI::callback_state_change(DeviceState new_state)
{
assert_locked();
if (new_state == Configured) {
_flags.set(FLAG_CONNECT);
_flags.clear(FLAG_DISCONNECT);
} else {
_flags.set(FLAG_DISCONNECT);
_flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE);
}
}
void USBMIDI::callback_request(const setup_packet_t *setup)
{
assert_locked();
RequestResult result = PassThrough;
uint8_t *data = NULL;
uint32_t size = 0;
complete_request(result, data, size);
}
void USBMIDI::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
assert_locked();
complete_request_xfer_done(false);
}
void USBMIDI::callback_set_configuration(uint8_t configuration)
{
assert_locked();
if (configuration == DEFAULT_CONFIGURATION) {
complete_set_configuration(false);
}
endpoint_remove_all();
endpoint_add(_bulk_in, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_in_callback);
endpoint_add(_bulk_out, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_out_callback);
read_start(_bulk_out, _bulk_buf, MaxSize);
complete_set_configuration(true);
}
void USBMIDI::callback_set_interface(uint16_t interface, uint8_t alternate)
{
assert_locked();
complete_set_interface(true);
}
const uint8_t *USBMIDI::string_iinterface_desc()
{
static const uint8_t string_iinterface_descriptor[] = {
0x0c, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio
};
return string_iinterface_descriptor;
}
const uint8_t *USBMIDI::string_iproduct_desc()
{
static const uint8_t string_iproduct_descriptor[] = {
0x16, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio
};
return string_iproduct_descriptor;
}
const uint8_t *USBMIDI::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t config_descriptor_temp[] = {
// configuration descriptor
0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50,
// The Audio Interface Collection
0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor
0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor
// MIDI IN JACKS
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
// MIDI OUT JACKS
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00,
// OUT endpoint - Standard MS Bulk Data Endpoint Descriptor
0x09, // bLength
0x05, // bDescriptorType
_bulk_out, // bEndpointAddress
0x02, // bmAttributes
0x40, // wMaxPacketSize (LSB)
0x00, // wMaxPacketSize (MSB)
0x00, // bInterval (milliseconds)
0x00, // bRefresh
0x00, // bSynchAddress
0x05, 0x25, 0x01, 0x01, 0x01,
// IN endpoint - Standard MS Bulk Data Endpoint Descriptor
0x09, // bLength
0x05, // bDescriptorType
_bulk_in, // bEndpointAddress
0x02, // bmAttributes
0x40, // wMaxPacketSize (LSB)
0x00, // wMaxPacketSize (MSB)
0x00, // bInterval (milliseconds)
0x00, // bRefresh
0x00, // bSynchAddress
0x05, 0x25, 0x01, 0x01, 0x03,
};
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp));
return _config_descriptor;
}
void USBMIDI::_in_callback()
{
assert_locked();
_flags.set(FLAG_WRITE_DONE);
}
void USBMIDI::_out_callback()
{
assert_locked();
_bulk_buf_size = read_finish(_bulk_out);
_bulk_buf_pos = 0;
if (_callback && _next_message()) {
_callback();
return;
}
read_start(_bulk_out, _bulk_buf, MaxSize);
}
bool USBMIDI::_next_message()
{
assert_locked();
bool data_ready = false;
while (_bulk_buf_pos < _bulk_buf_size) {
uint8_t data_read;
bool data_end = true;
switch (_bulk_buf[_bulk_buf_pos]) {
case 0x2:
// Two-bytes System Common Message - undefined in USBMidi 1.0
data_read = 2;
break;
case 0x4:
// SysEx start or continue
data_end = false;
data_read = 3;
break;
case 0x5:
// Single-byte System Common Message or SysEx end with one byte
data_read = 1;
break;
case 0x6:
// SysEx end with two bytes
data_read = 2;
break;
case 0xC:
// Program change
data_read = 2;
break;
case 0xD:
// Channel pressure
data_read = 2;
break;
case 0xF:
// Single byte
data_read = 1;
break;
default:
// Others three-bytes messages
data_read = 3;
break;
}
for (uint8_t j = 1; j < data_read + 1; j++) {
if (_cur_data < sizeof(_data)) {
_data[_cur_data] = _bulk_buf[_bulk_buf_pos + j];
}
_cur_data++;
}
_bulk_buf_pos += 4;
if (data_end) {
// Message is ready to be read
data_ready = true;
break;
}
}
_data_ready = data_ready;
return data_ready;
}

View File

@ -0,0 +1,185 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBMIDI_H
#define USBMIDI_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "MIDIMessage.h"
#include "EventFlags.h"
#include "Mutex.h"
#include "Callback.h"
#define DEFAULT_CONFIGURATION (1)
/**
* USBMIDI example
*
* @code
* #include "mbed.h"
* #include "USBMIDI.h"
*
* USBMIDI midi;
*
* int main() {
* while (1) {
* for(int i=48; i<83; i++) { // send some messages!
* midi.write(MIDIMessage::NoteOn(i));
* wait(0.25);
* midi.write(MIDIMessage::NoteOff(i));
* wait(0.5);
* }
* }
* }
* @endcode
*/
class USBMIDI: public USBDevice {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMIDI(bool connect_blocking = true, uint16_t vendor_id = 0x0700, uint16_t product_id = 0x0101, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBMIDI();
/**
* Check if this class is ready
*
* @return true if configured, false otherwise
*/
bool ready();
/**
* Block until this device is configured
*/
void wait_ready();
/**
* Send a MIDIMessage
*
* @param m The MIDIMessage to send
* @return true if the message was sent, false otherwise
*/
bool write(MIDIMessage m);
/**
* Check if a message can be read
*
* @return true if a packet can be read false otherwise
* @note USBMIDI::attach must be called to enable the receiver
*/
bool readable();
/**
* Read a message
*
* @param m The MIDIMessage to fill
* @return true if a message was read, false otherwise
*/
bool read(MIDIMessage *m);
/**
* Attach a callback for when a MIDIEvent is received
*
* @param callback code to call when a packet is received
*/
void attach(mbed::Callback<void()> callback);
protected:
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 const uint8_t *string_iproduct_desc();
virtual const uint8_t *string_iinterface_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
private:
static const uint32_t MaxSize = 64;
uint8_t _bulk_buf[MaxSize];
uint32_t _bulk_buf_pos;
uint32_t _bulk_buf_size;
bool _data_ready;
uint8_t _data[MAX_MIDI_MESSAGE_SIZE + 1];
uint32_t _cur_data;
rtos::EventFlags _flags;
rtos::Mutex _write_mutex;
usb_ep_t _bulk_in;
usb_ep_t _bulk_out;
uint8_t _config_descriptor[0x65];
mbed::Callback<void()> _callback;
void _init();
void _in_callback();
void _out_callback();
bool _next_message();
};
#endif

View File

@ -0,0 +1,925 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 <stdlib.h>
#include "USBMSD.h"
#include "EndpointResolver.h"
#include "usb_phy_api.h"
#define DISK_OK 0x00
#define NO_INIT 0x01
#define NO_DISK 0x02
#define WRITE_PROTECT 0x04
#define CBW_Signature 0x43425355
#define CSW_Signature 0x53425355
// SCSI Commands
#define TEST_UNIT_READY 0x00
#define REQUEST_SENSE 0x03
#define FORMAT_UNIT 0x04
#define INQUIRY 0x12
#define MODE_SELECT6 0x15
#define MODE_SENSE6 0x1A
#define START_STOP_UNIT 0x1B
#define MEDIA_REMOVAL 0x1E
#define READ_FORMAT_CAPACITIES 0x23
#define READ_CAPACITY 0x25
#define READ10 0x28
#define WRITE10 0x2A
#define VERIFY10 0x2F
#define READ12 0xA8
#define WRITE12 0xAA
#define MODE_SELECT10 0x55
#define MODE_SENSE10 0x5A
// MSC class specific requests
#define MSC_REQUEST_RESET 0xFF
#define MSC_REQUEST_GET_MAX_LUN 0xFE
#define DEFAULT_CONFIGURATION (1)
// max packet size
#define MAX_PACKET 64
// CSW Status
enum Status {
CSW_PASSED,
CSW_FAILED,
CSW_ERROR,
};
USBMSD::USBMSD(BlockDevice *bd, bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release),
_initialized(false), _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), _configure_task(&_queue), _bd(bd)
{
_init();
if (connect_blocking) {
connect();
} else {
init();
}
}
USBMSD::USBMSD(USBPhy *phy, BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release),
_initialized(false), _in_task(&_queue), _out_task(&_queue), _reset_task(&_queue), _control_task(&_queue), _configure_task(&_queue), _bd(bd)
{
_init();
}
void USBMSD::_init()
{
_bd->init();
_in_task = mbed::callback(this, &USBMSD::_in);
_out_task = mbed::callback(this, &USBMSD::_out);
_reset_task = mbed::callback(this, &USBMSD::_reset);
_control_task = mbed::callback(this, &USBMSD::_control);
_configure_task = mbed::callback(this, &USBMSD::_configure);
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(64);
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MAX_PACKET);
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MAX_PACKET);
MBED_ASSERT(resolver.valid());
_stage = READ_CBW;
memset((void *)&_cbw, 0, sizeof(CBW));
memset((void *)&_csw, 0, sizeof(CSW));
_page = NULL;
}
USBMSD::~USBMSD()
{
disconnect();
_bd->deinit();
deinit();
}
bool USBMSD::connect()
{
_mutex_init.lock();
_mutex.lock();
// already initialized
if (_initialized) {
_mutex.unlock();
_mutex_init.unlock();
return false;
}
//disk initialization
if (disk_status() & NO_INIT) {
if (disk_initialize()) {
_mutex.unlock();
_mutex_init.unlock();
return false;
}
}
// get number of blocks
_block_count = disk_sectors();
// get memory size
_memory_size = disk_size();
if (_block_count > 0) {
_block_size = _memory_size / _block_count;
if (_block_size != 0) {
free(_page);
_page = (uint8_t *)malloc(_block_size * sizeof(uint8_t));
if (_page == NULL) {
_mutex.unlock();
_mutex_init.unlock();
return false;
}
}
} else {
_mutex.unlock();
_mutex_init.unlock();
return false;
}
//connect the device
USBDevice::connect();
_initialized = true;
_mutex.unlock();
_mutex_init.unlock();
return true;
}
void USBMSD::disconnect()
{
_mutex_init.lock();
_mutex.lock();
USBDevice::disconnect();
_initialized = false;
_in_task.cancel();
_out_task.cancel();
_reset_task.cancel();
_control_task.cancel();
_configure_task.cancel();
_mutex.unlock();
// object mutex must be unlocked for waiting
_in_task.wait();
_out_task.wait();
_reset_task.wait();
_control_task.wait();
_configure_task.wait();
_mutex.lock();
//De-allocate MSD page size:
free(_page);
_page = NULL;
_mutex.unlock();
_mutex_init.unlock();
}
void USBMSD::process()
{
_queue.dispatch();
}
void USBMSD::attach(mbed::Callback<void()> cb)
{
lock();
_queue.attach(cb);
unlock();
}
int USBMSD::disk_read(uint8_t *data, uint64_t block, uint8_t count)
{
bd_addr_t addr = block * _bd->get_erase_size();
bd_size_t size = count * _bd->get_erase_size();
return _bd->read(data, addr, size);
}
int USBMSD::disk_write(const uint8_t *data, uint64_t block, uint8_t count)
{
bd_addr_t addr = block * _bd->get_erase_size();
bd_size_t size = count * _bd->get_erase_size();
int ret = _bd->erase(addr, size);
if (ret != 0) {
return ret;
}
return _bd->program(data, addr, size);
}
int USBMSD::disk_initialize()
{
return 0;
}
uint64_t USBMSD::disk_sectors()
{
return _bd->size() / _bd->get_erase_size();
}
uint64_t USBMSD::disk_size()
{
return _bd->size();
}
int USBMSD::disk_status()
{
return 0;
}
void USBMSD::_isr_out()
{
_out_task.call();
}
void USBMSD::_isr_in()
{
_in_task.call();
}
void USBMSD::callback_state_change(DeviceState new_state)
{
// called in ISR context
if (new_state != Configured) {
_reset_task.cancel();
_reset_task.call();
}
}
void USBMSD::callback_request(const setup_packet_t *setup)
{
// called in ISR context
if (setup->bmRequestType.Type == CLASS_TYPE) {
_control_task.call(setup);
} else {
complete_request(PassThrough, NULL, 0);
}
}
void USBMSD::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
// called in ISR context
bool success = setup->bRequest == MSC_REQUEST_GET_MAX_LUN;
complete_request_xfer_done(success);
}
void USBMSD::callback_set_configuration(uint8_t configuration)
{
// called in ISR context
if (configuration != DEFAULT_CONFIGURATION) {
complete_set_configuration(false);
return;
}
_configure_task.call();
}
void USBMSD::callback_set_interface(uint16_t interface, uint8_t alternate)
{
// called in ISR context
bool success = (interface == 0) && (alternate == 0);
complete_set_interface(success);
}
const uint8_t *USBMSD::string_iinterface_desc()
{
static const uint8_t string_iinterface_descriptor[] = {
0x08, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'M', 0, 'S', 0, 'D', 0 //bString iInterface - MSD
};
return string_iinterface_descriptor;
}
const uint8_t *USBMSD::string_iproduct_desc()
{
static const uint8_t string_iproduct_descriptor[] = {
0x12, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'M', 0, 'S', 0, 'D', 0 //bString iProduct - Mbed Audio
};
return string_iproduct_descriptor;
}
const uint8_t *USBMSD::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t config_descriptor_temp[] = {
// Configuration 1
9, // bLength
2, // bDescriptorType
LSB(9 + 9 + 7 + 7), // wTotalLength
MSB(9 + 9 + 7 + 7),
0x01, // bNumInterfaces
0x01, // bConfigurationValue: 0x01 is used to select this configuration
0x00, // iConfiguration: no string to describe this configuration
0xC0, // bmAttributes
100, // bMaxPower, device power consumption is 100 mA
// Interface 0, Alternate Setting 0, MSC Class
9, // bLength
4, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x08, // bInterfaceClass
0x06, // bInterfaceSubClass
0x50, // bInterfaceProtocol
0x04, // iInterface
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
7, // bLength
5, // bDescriptorType
_bulk_in, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk)
LSB(MAX_PACKET), // wMaxPacketSize (LSB)
MSB(MAX_PACKET), // wMaxPacketSize (MSB)
0, // bInterval
// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
7, // bLength
5, // bDescriptorType
_bulk_out, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk)
LSB(MAX_PACKET), // wMaxPacketSize (LSB)
MSB(MAX_PACKET), // wMaxPacketSize (MSB)
0 // bInterval
};
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_configuration_descriptor));
memcpy(_configuration_descriptor, config_descriptor_temp, sizeof(_configuration_descriptor));
return _configuration_descriptor;
}
void USBMSD::_out()
{
_mutex.lock();
_bulk_out_size = read_finish(_bulk_out);
_out_ready = true;
_process();
_mutex.unlock();
}
void USBMSD::_in()
{
_mutex.lock();
write_finish(_bulk_in);
_in_ready = true;
_process();
_mutex.unlock();
}
void USBMSD::_reset()
{
_mutex.lock();
msd_reset();
_mutex.unlock();
}
void USBMSD::_control(const setup_packet_t *setup)
{
_mutex.lock();
static const uint8_t maxLUN[1] = {0};
RequestResult result = PassThrough;
uint8_t *data = NULL;
uint32_t size = 0;
if (setup->bmRequestType.Type == CLASS_TYPE) {
switch (setup->bRequest) {
case MSC_REQUEST_RESET:
result = Success;
msd_reset();
break;
case MSC_REQUEST_GET_MAX_LUN:
result = Send;
data = (uint8_t *)maxLUN;
size = 1;
break;
default:
break;
}
}
complete_request(result, data, size);
_mutex.unlock();
}
void USBMSD::_configure()
{
_mutex.lock();
// Configure endpoints > 0
endpoint_add(_bulk_in, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_in);
endpoint_add(_bulk_out, MAX_PACKET, USB_EP_TYPE_BULK, &USBMSD::_isr_out);
MBED_ASSERT(sizeof(_bulk_out_buf) == MAX_PACKET);
MBED_ASSERT(sizeof(_bulk_in_buf) == MAX_PACKET);
_out_ready = false;
_in_ready = true;
//activate readings
read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf));
complete_set_configuration(true);
_mutex.unlock();
}
void USBMSD::_process()
{
// Mutex must be locked by caller
switch (_stage) {
// the device has to decode the CBW received
case READ_CBW:
if (!_out_ready) {
break;
}
CBWDecode(_bulk_out_buf, _bulk_out_size);
_read_next();
break;
case PROCESS_CBW:
switch (_cbw.CB[0]) {
// the device has to receive data from the host
case WRITE10:
case WRITE12:
if (!_out_ready) {
break;
}
memoryWrite(_bulk_out_buf, _bulk_out_size);
_read_next();
break;
case VERIFY10:
if (!_out_ready) {
break;
}
memoryVerify(_bulk_out_buf, _bulk_out_size);
_read_next();
break;
// the device has to send data to the host
case READ10:
case READ12:
if (!_in_ready) {
break;
}
memoryRead();
break;
}
break;
//the device has to send a CSW
case SEND_CSW:
if (!_in_ready) {
break;
}
sendCSW();
break;
// an error has occurred: stall endpoint and send CSW
default:
endpoint_stall(_bulk_out);
endpoint_stall(_bulk_in);
_csw.Status = CSW_ERROR;
sendCSW();
break;
}
}
void USBMSD::_write_next(uint8_t *data, uint32_t size)
{
lock();
MBED_ASSERT(size <= MAX_PACKET);
MBED_ASSERT(_in_ready);
uint32_t send_size = MAX_PACKET > size ? size : MAX_PACKET;
memcpy(_bulk_in_buf, data, send_size);
write_start(_bulk_in, _bulk_in_buf, send_size);
_in_ready = false;
unlock();
}
void USBMSD::_read_next()
{
lock();
MBED_ASSERT(_out_ready);
read_start(_bulk_out, _bulk_out_buf, sizeof(_bulk_out_buf));
_out_ready = false;
unlock();
}
void USBMSD::memoryWrite(uint8_t *buf, uint16_t size)
{
if ((_addr + size) > _memory_size) {
size = _memory_size - _addr;
_stage = ERROR;
endpoint_stall(_bulk_out);
}
// we fill an array in RAM of 1 block before writing it in memory
for (int i = 0; i < size; i++) {
_page[_addr % _block_size + i] = buf[i];
}
// if the array is filled, write it in memory
if (!((_addr + size) % _block_size)) {
if (!(disk_status() & WRITE_PROTECT)) {
disk_write(_page, _addr / _block_size, 1);
}
}
_addr += size;
_length -= size;
_csw.DataResidue -= size;
if ((!_length) || (_stage != PROCESS_CBW)) {
_csw.Status = (_stage == ERROR) ? CSW_FAILED : CSW_PASSED;
sendCSW();
}
}
void USBMSD::memoryVerify(uint8_t *buf, uint16_t size)
{
uint32_t n;
if ((_addr + size) > _memory_size) {
size = _memory_size - _addr;
_stage = ERROR;
endpoint_stall(_bulk_out);
}
// beginning of a new block -> load a whole block in RAM
if (!(_addr % _block_size)) {
disk_read(_page, _addr / _block_size, 1);
}
// info are in RAM -> no need to re-read memory
for (n = 0; n < size; n++) {
if (_page[_addr % _block_size + n] != buf[n]) {
_mem_ok = false;
break;
}
}
_addr += size;
_length -= size;
_csw.DataResidue -= size;
if (!_length || (_stage != PROCESS_CBW)) {
_csw.Status = (_mem_ok && (_stage == PROCESS_CBW)) ? CSW_PASSED : CSW_FAILED;
sendCSW();
}
}
bool USBMSD::inquiryRequest(void)
{
uint8_t inquiry[] = { 0x00, 0x80, 0x00, 0x01,
36 - 4, 0x80, 0x00, 0x00,
'M', 'B', 'E', 'D', '.', 'O', 'R', 'G',
'M', 'B', 'E', 'D', ' ', 'U', 'S', 'B', ' ', 'D', 'I', 'S', 'K', ' ', ' ', ' ',
'1', '.', '0', ' ',
};
if (!write(inquiry, sizeof(inquiry))) {
return false;
}
return true;
}
bool USBMSD::readFormatCapacity()
{
uint8_t capacity[] = { 0x00, 0x00, 0x00, 0x08,
(uint8_t)((_block_count >> 24) & 0xff),
(uint8_t)((_block_count >> 16) & 0xff),
(uint8_t)((_block_count >> 8) & 0xff),
(uint8_t)((_block_count >> 0) & 0xff),
0x02,
(uint8_t)((_block_size >> 16) & 0xff),
(uint8_t)((_block_size >> 8) & 0xff),
(uint8_t)((_block_size >> 0) & 0xff),
};
if (!write(capacity, sizeof(capacity))) {
return false;
}
return true;
}
bool USBMSD::readCapacity(void)
{
uint8_t capacity[] = {
(uint8_t)(((_block_count - 1) >> 24) & 0xff),
(uint8_t)(((_block_count - 1) >> 16) & 0xff),
(uint8_t)(((_block_count - 1) >> 8) & 0xff),
(uint8_t)(((_block_count - 1) >> 0) & 0xff),
(uint8_t)((_block_size >> 24) & 0xff),
(uint8_t)((_block_size >> 16) & 0xff),
(uint8_t)((_block_size >> 8) & 0xff),
(uint8_t)((_block_size >> 0) & 0xff),
};
if (!write(capacity, sizeof(capacity))) {
return false;
}
return true;
}
bool USBMSD::write(uint8_t *buf, uint16_t size)
{
if (size >= _cbw.DataLength) {
size = _cbw.DataLength;
}
_stage = SEND_CSW;
_write_next(buf, size);
_csw.DataResidue -= size;
_csw.Status = CSW_PASSED;
return true;
}
bool USBMSD::modeSense6(void)
{
uint8_t sense6[] = { 0x03, 0x00, 0x00, 0x00 };
if (!write(sense6, sizeof(sense6))) {
return false;
}
return true;
}
void USBMSD::sendCSW()
{
_csw.Signature = CSW_Signature;
_write_next((uint8_t *)&_csw, sizeof(CSW));
_stage = READ_CBW;
}
bool USBMSD::requestSense(void)
{
uint8_t request_sense[] = {
0x70,
0x00,
0x05, // Sense Key: illegal request
0x00,
0x00,
0x00,
0x00,
0x0A,
0x00,
0x00,
0x00,
0x00,
0x30,
0x01,
0x00,
0x00,
0x00,
0x00,
};
if (!write(request_sense, sizeof(request_sense))) {
return false;
}
return true;
}
void USBMSD::fail()
{
_csw.Status = CSW_FAILED;
sendCSW();
}
void USBMSD::CBWDecode(uint8_t *buf, uint16_t size)
{
if (size == sizeof(_cbw)) {
memcpy((uint8_t *)&_cbw, buf, size);
if (_cbw.Signature == CBW_Signature) {
_csw.Tag = _cbw.Tag;
_csw.DataResidue = _cbw.DataLength;
if ((_cbw.CBLength < 1) || (_cbw.CBLength > 16)) {
fail();
} else {
switch (_cbw.CB[0]) {
case TEST_UNIT_READY:
testUnitReady();
break;
case REQUEST_SENSE:
requestSense();
break;
case INQUIRY:
inquiryRequest();
break;
case MODE_SENSE6:
modeSense6();
break;
case READ_FORMAT_CAPACITIES:
readFormatCapacity();
break;
case READ_CAPACITY:
readCapacity();
break;
case READ10:
case READ12:
if (infoTransfer()) {
if ((_cbw.Flags & 0x80)) {
_stage = PROCESS_CBW;
memoryRead();
} else {
endpoint_stall(_bulk_out);
_csw.Status = CSW_ERROR;
sendCSW();
}
}
break;
case WRITE10:
case WRITE12:
if (infoTransfer()) {
if (!(_cbw.Flags & 0x80)) {
_stage = PROCESS_CBW;
} else {
endpoint_stall(_bulk_in);
_csw.Status = CSW_ERROR;
sendCSW();
}
}
break;
case VERIFY10:
if (!(_cbw.CB[1] & 0x02)) {
_csw.Status = CSW_PASSED;
sendCSW();
break;
}
if (infoTransfer()) {
if (!(_cbw.Flags & 0x80)) {
_stage = PROCESS_CBW;
_mem_ok = true;
} else {
endpoint_stall(_bulk_in);
_csw.Status = CSW_ERROR;
sendCSW();
}
}
break;
case MEDIA_REMOVAL:
_csw.Status = CSW_PASSED;
sendCSW();
break;
default:
fail();
break;
}
}
}
}
}
void USBMSD::testUnitReady(void)
{
if (_cbw.DataLength != 0) {
if ((_cbw.Flags & 0x80) != 0) {
endpoint_stall(_bulk_in);
} else {
endpoint_stall(_bulk_out);
}
}
_csw.Status = CSW_PASSED;
sendCSW();
}
void USBMSD::memoryRead(void)
{
uint32_t n;
n = (_length > MAX_PACKET) ? MAX_PACKET : _length;
if ((_addr + n) > _memory_size) {
n = _memory_size - _addr;
_stage = ERROR;
}
// we read an entire block
if (!(_addr % _block_size)) {
disk_read(_page, _addr / _block_size, 1);
}
// write data which are in RAM
_write_next(&_page[_addr % _block_size], MAX_PACKET);
_addr += n;
_length -= n;
_csw.DataResidue -= n;
if (!_length || (_stage != PROCESS_CBW)) {
_csw.Status = (_stage == PROCESS_CBW) ? CSW_PASSED : CSW_FAILED;
_stage = (_stage == PROCESS_CBW) ? SEND_CSW : _stage;
}
}
bool USBMSD::infoTransfer(void)
{
uint32_t n;
// Logical Block Address of First Block
n = (_cbw.CB[2] << 24) | (_cbw.CB[3] << 16) | (_cbw.CB[4] << 8) | (_cbw.CB[5] << 0);
_addr = n * _block_size;
// Number of Blocks to transfer
switch (_cbw.CB[0]) {
case READ10:
case WRITE10:
case VERIFY10:
n = (_cbw.CB[7] << 8) | (_cbw.CB[8] << 0);
break;
case READ12:
case WRITE12:
n = (_cbw.CB[6] << 24) | (_cbw.CB[7] << 16) | (_cbw.CB[8] << 8) | (_cbw.CB[9] << 0);
break;
}
_length = n * _block_size;
if (!_cbw.DataLength) { // host requests no data
_csw.Status = CSW_FAILED;
sendCSW();
return false;
}
if (_cbw.DataLength != _length) {
if ((_cbw.Flags & 0x80) != 0) {
endpoint_stall(_bulk_in);
} else {
endpoint_stall(_bulk_out);
}
_csw.Status = CSW_FAILED;
sendCSW();
return false;
}
return true;
}
void USBMSD::msd_reset()
{
_stage = READ_CBW;
}

297
usb/device/USBMSD/USBMSD.h Normal file
View File

@ -0,0 +1,297 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBMSD_H
#define USBMSD_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "platform/Callback.h"
#include "events/PolledQueue.h"
#include "events/Task.h"
#include "BlockDevice.h"
#include "Mutex.h"
#include "USBDevice.h"
/**
* USBMSD class: generic class in order to use all kinds of blocks storage chip
*
* Introduction
*
* USBMSD implements the MSD protocol. It permits to access a block device (flash, sdcard,...)
* from a computer over USB.
*
* @code
* #include "mbed.h"
* #include "SDBlockDevice.h"
* #include "USBMSD.h"
*
* SDBlockDevice sd(PTE3, PTE1, PTE2, PTE4);
* USBMSD usb(&sd);
*
* int main() {
*
* while(true) {
* usb.process();
* }
*
* return 0;
* }
* @endcode
*/
class USBMSD: public USBDevice {
public:
/**
* Constructor
*
* This creates a new USBMSD object with the given block device. Connect must be called
* for the block device to connect.
*
* @param bd BlockDevice to mount as a USB drive
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your preoduct_release
*/
USBMSD(BlockDevice *bd, bool connect_blocking = true, uint16_t vendor_id = 0x0703, uint16_t product_id = 0x0104, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param bd BlockDevice to mount as a USB drive
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your preoduct_release
*/
USBMSD(USBPhy *phy, BlockDevice *bd, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call disconnect
* before this destructor runs.
*/
virtual ~USBMSD();
/**
* Connect the USB MSD device.
*
* @returns true if successful
*/
bool connect();
/**
* Disconnect the USB MSD device.
*/
void disconnect();
/**
* Perform USB processing
*/
void process();
/**
* Called when USBMSD needs to perform processing
*
* @param cb Callback called when USBMSD needs process() to be called
*/
void attach(mbed::Callback<void()> cb);
protected:
/*
* read one or more blocks on a storage chip
*
* @param data pointer where will be stored read data
* @param block starting block number
* @param count number of blocks to read
* @returns 0 if successful
*/
virtual int disk_read(uint8_t *data, uint64_t block, uint8_t count);
/*
* write one or more blocks on a storage chip
*
* @param data data to write
* @param block starting block number
* @param count number of blocks to write
* @returns 0 if successful
*/
virtual int disk_write(const uint8_t *data, uint64_t block, uint8_t count);
/*
* Disk initilization
*/
virtual int disk_initialize();
/*
* Return the number of blocks
*
* @returns number of blocks
*/
virtual uint64_t disk_sectors();
/*
* Return memory size
*
* @returns memory size
*/
virtual uint64_t disk_size();
/*
* To check the status of the storage chip
*
* @returns status: 0: OK, 1: disk not initialized, 2: no medium in the drive, 4: write protected
*/
virtual int disk_status();
private:
// MSC Bulk-only Stage
enum Stage {
READ_CBW, // wait a CBW
ERROR, // error
PROCESS_CBW, // process a CBW request
SEND_CSW, // send a CSW
};
// Bulk-only CBW
typedef MBED_PACKED(struct)
{
uint32_t Signature;
uint32_t Tag;
uint32_t DataLength;
uint8_t Flags;
uint8_t LUN;
uint8_t CBLength;
uint8_t CB[16];
} CBW;
// Bulk-only CSW
typedef MBED_PACKED(struct)
{
uint32_t Signature;
uint32_t Tag;
uint32_t DataResidue;
uint8_t Status;
} CSW;
// If this class has been initialized
bool _initialized;
//state of the bulk-only state machine
Stage _stage;
// current CBW
CBW _cbw;
// CSW which will be sent
CSW _csw;
// addr where will be read or written data
uint32_t _addr;
// length of a reading or writing
uint32_t _length;
// memory OK (after a memoryVerify)
bool _mem_ok;
// cache in RAM before writing in memory. Useful also to read a block.
uint8_t *_page;
int _block_size;
uint64_t _memory_size;
uint64_t _block_count;
// endpoints
usb_ep_t _bulk_in;
usb_ep_t _bulk_out;
uint8_t _bulk_in_buf[64];
uint8_t _bulk_out_buf[64];
bool _out_ready;
bool _in_ready;
uint32_t _bulk_out_size;
// Interrupt to thread deferral
events::PolledQueue _queue;
events::Task<void()> _in_task;
events::Task<void()> _out_task;
events::Task<void()> _reset_task;
events::Task<void(const setup_packet_t *)> _control_task;
events::Task<void()> _configure_task;
BlockDevice *_bd;
rtos::Mutex _mutex_init;
rtos::Mutex _mutex;
// space for config descriptor
uint8_t _configuration_descriptor[32];
virtual const uint8_t *string_iproduct_desc();
virtual const uint8_t *string_iinterface_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
virtual void callback_set_configuration(uint8_t configuration);
virtual void callback_set_interface(uint16_t interface, uint8_t alternate);
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);
void _isr_out();
void _isr_in();
void _out();
void _in();
void _reset();
void _control(const setup_packet_t *request);
void _configure();
void _init();
void _process();
void _write_next(uint8_t *data, uint32_t size);
void _read_next();
void CBWDecode(uint8_t *buf, uint16_t size);
void sendCSW(void);
bool inquiryRequest(void);
bool write(uint8_t *buf, uint16_t size);
bool readFormatCapacity();
bool readCapacity(void);
bool infoTransfer(void);
void memoryRead(void);
bool modeSense6(void);
void testUnitReady(void);
bool requestSense(void);
void memoryVerify(uint8_t *buf, uint16_t size);
void memoryWrite(uint8_t *buf, uint16_t size);
void msd_reset();
void fail();
};
#endif

318
usb/device/USBPhy/USBPhy.h Normal file
View File

@ -0,0 +1,318 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBPHY_H
#define USBPHY_H
#include "USBPhyTypes.h"
#include "USBPhyEvents.h"
/** Abstract interface to physical USB hardware
*
* # Defined behavior
* * You can use any endpoint configurations that fit in the parameters
* of the table returned by USBPhy::endpoint_table.
* * You can use all endpoints in any valid endpoint configuration concurrently.
* * The device supports use of at least one control, bulk, interrupt and
* isochronous in each direction at the same time - at least 8 endpoints.
* * USBPhy supports all standard endpoint sizes (wMaxPacketSize).
* * USBPhy can handle an interrupt latency of at least 100ms if the host PC
* is not performing a reset or setting the device's address.
* * USBPhy only sends USBPhyEvents when it is in the initialized state.
* * When unpowered, USBPhy only sends the USBPhyEvents::power event.
* * On USB reset, all endpoints are removed except for endpoint 0.
* * A call to USBPhy::ep0_write results in the call of USBPhyEvents::in when
* the PC reads the data unless a power loss, reset, or a call to
* USBPhy::disconnect occurs first.
* * A call to USBPhy::endpoint_write results in the call of USBPhyEvents::in
* when the pc reads the data unless a power loss, reset, or a call to
* USBPhy::endpoint_abort occurs first.
* * A call to USBPhy::endpoint_read results in the call of USBPhyEvents::out
* when the pc sends data unless a power loss, reset, or a call to
* USBPhy::endpoint_abort occurs first.
* * Endpoint 0 naks all transactions aside from setup packets until
* higher-level code calls one of USBPhy::ep0_read, USBPhy::ep0_write or
* USBPhy::ep0_stall.
* * Endpoint 0 stall automatically clears on reception of a setup packet.
*
* # Undefined behavior
* * Calling USBPhy::endpoint_add or USBPhy::endpoint_remove outside of the
* control requests SetInterface or SetConfiguration.
* * Calling USBPhy::endpoint_remove on an endpoint that has an ongoing read
* or write operation. To avoid undefined behavior, you must abort ongoing
* operations with USBPhy::endpoint_abort.
* * Devices behavior is undefined if latency is greater than 2ms when address
* is being set - see USB spec 9.2.6.3.
* * Devices behavior is undefined if latency is greater than 10ms when a
* reset occurs - see USB spec 7.1.7.5.
* * Calling any of the USBPhy::endpoint_* functions on endpoint 0.
*
* # Notes
* * Make sure USBPhy sends USBPhyEvents in the correct order when multiple
* packets are present. USBPhy must send IN endpoint events before OUT
* endpoint events if both are pending.
* * A host PC may resend setup packets to a USB device if there is noise on
* the USB line. The USBPhy should be able to handle this scenario and
* respond to the setup packet with an ACK.
* * Bidirectional protocols making use of alternating IN and OUT phases
* should not rely on the last ACK an IN transfer to indicate that the
* OUT phase should start. Instead, the OUT phase should be started at
* the same time the last IN transfer is started. This is because the ACK
* to the last in transfer may be dropped if there is noise on the USB
* line. If dropped, it will only be resent on the next IN phase. You can
* find more information on this in section 8.5.3.3 of the USB
* specification.
*
* @ingroup usb_device_core
*/
class USBPhy {
public:
USBPhy() {};
virtual ~USBPhy() {};
/**
* Initialize this USBPhy instance
*
* This function must be called before calling
* any other functions of this class, unless specifically
* noted.
*
* @param events Callback class to handle USB events
*/
virtual void init(USBPhyEvents *events) = 0;
/**
* Power down this USBPhy instance
*
* Disable interrupts and stop sending events.
*/
virtual void deinit() = 0;
/**
* Check if USB power is present
*
* Devices which don't support checking the USB power state
* must always return true.
*
* @return true if USB power is present, false otherwise
*/
virtual bool powered() = 0;
/**
* Make the USB phy visible to the USB host
*
* Enable either the D+ or D- pullup so the host can detect
* the presence of this device.
*/
virtual void connect() = 0;
/**
* Detach the USB phy
*
* Disable the D+ and D- pullup and stop responding to
* USB traffic.
*/
virtual void disconnect() = 0;
/**
* Set this device to the configured state
*
* Enable added endpoints if they are not enabled
* already.
*/
virtual void configure() = 0;
/**
* Leave the configured state
*
* This is a notification to the USBPhy indicating that the device
* is leaving the configured state. The USBPhy can disable all
* endpoints other than endpoint 0.
*
*/
virtual void unconfigure() = 0;
/**
* Enable the start of frame interrupt
*
* Call USBPhyEvents::sof on every frame.
*/
virtual void sof_enable() = 0;
/**
* Disable the start of frame interrupt
*
* Stop calling USBPhyEvents::sof.
*/
virtual void sof_disable() = 0;
/**
* Set the USBPhy's address
*
* @param address This device's USB address
*/
virtual void set_address(uint8_t address) = 0;
/**
* Wake upstream devices
*/
virtual void remote_wakeup() = 0;
/**
* Get the endpoint table
*
* This function returns a table which describes the endpoints
* can be used, the functionality of those endpoints and the
* resource cost.
*/
virtual const usb_ep_table_t *endpoint_table() = 0;
/**
* Set wMaxPacketSize of endpoint 0
*
* @param max_packet The wMaxPacketSize value for endpoint 0
* @return The actual size of endpoint 0
*/
virtual uint32_t ep0_set_max_packet(uint32_t max_packet) = 0;
/**
* Read the contents of the SETUP packet
*
* @param buffer Buffer to fill with data
* @param size Size of buffer passed in
*/
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size) = 0;
/**
* Start receiving a packet of up to wMaxPacketSize on endpoint 0
*
* @param data Buffer to fill with the data read
* @param size Size of buffer
*/
virtual void ep0_read(uint8_t *data, uint32_t size) = 0;
/**
* Read the contents of a received packet
*
* @return Size of data read
*/
virtual uint32_t ep0_read_result() = 0;
/**
* Write a packet on endpoint 0
*
* @param buffer Buffer fill with data to send
* @param size Size of data to send
*/
virtual void ep0_write(uint8_t *buffer, uint32_t size) = 0;
/**
* Protocol stall on endpoint 0
*
* Stall all IN and OUT packets on endpoint 0 until a setup packet
* is received.
* @note The stall is cleared automatically when a setup packet is received
*/
virtual void ep0_stall() = 0;
/**
* Configure and enable an endpoint
*
* @param endpoint Endpoint to configure and enable
* @param max_packet The maximum packet size that can be sent or received
* @param type The type of endpoint this should be configured as -
* USB_EP_TYPE_BULK, USB_EP_TYPE_INT or USB_EP_TYPE_ISO
* @note This function cannot be used to configure endpoint 0. That must be done
* with ep0_set_max_packet
*/
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type) = 0;
/**
* Disable an endpoint
*
* @param endpoint Endpoint to disable
*/
virtual void endpoint_remove(usb_ep_t endpoint) = 0;
/**
* Perform a functional stall on the given endpoint
*
* Set the HALT feature for this endpoint so that all further
* communication is aborted.
*
* @param endpoint Endpoint to stall
*/
virtual void endpoint_stall(usb_ep_t endpoint) = 0;
/**
* Unstall the endpoint
*
* Clear the HALT feature on this endpoint so communication can
* resume.
*
* @param endpoint Endpoint to stall
*/
virtual void endpoint_unstall(usb_ep_t endpoint) = 0;
/**
* Start a read on the given endpoint
*
* @param endpoint Endpoint to start the read on
* @param data Buffer to fill with data
* @param size Size of the read buffer. This must be at least
* the max packet size for this endpoint.
* @return true if the read was successfully started, false otherwise
*/
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size) = 0;
/**
* Finish a read on the given endpoint
*
* @param endpoint Endpoint to check
* @return true if data was read false otherwise
*/
virtual uint32_t endpoint_read_result(usb_ep_t endpoint) = 0;
/**
* Start a write on the given endpoint
*
* @param endpoint Endpoint to write to
* @param data Buffer to write
* @param size Size of data to write
* @return true if the data was prepared for transmit, false otherwise
*/
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size) = 0;
/**
* Abort the current transfer if it has not yet been sent
*
* @param endpoint Endpoint to abort the transfer on. It is implementation defined
* if this function has an effect on receive endpoints.
*/
virtual void endpoint_abort(usb_ep_t endpoint) = 0;
/**
* Callback used for performing USB processing
*
* USBPhy processing should be triggered by calling USBPhyEvents::start_process
* and done inside process. All USBPhyEvents callbacks aside from
* USBPhyEvents::start_process must be called in the context of process
*/
virtual void process() = 0;
};
#endif

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBPHY_EVENTS_H
#define USBPHY_EVENTS_H
#include "USBPhyTypes.h"
/** Event handler for USBPhy
*
* This class is the event handler for the USBPhy class. Any events generated
* by USBPhy are passed to this class via the virtual functions.
*
* @ingroup usb_device_core
*
*/
class USBPhyEvents {
public:
USBPhyEvents() {};
virtual ~USBPhyEvents() {};
/**
* Callback called when a bus reset occurs
* @note called in the contex of USBPhy::process
*/
virtual void reset() = 0;
/**
* Callback called when an endpoint 0 setup packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_setup() = 0;
/**
* Callback called when an endpoint 0 out packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_out() = 0;
/**
* Callback called when an endpoint 0 in packet is received
* @note called in the contex of USBPhy::process
*/
virtual void ep0_in() = 0;
/**
* Callback called USB power is applied or removed
*
* @param powered true if USB power is present, false otherwise
* @note called in the contex of USBPhy::process
*/
virtual void power(bool powered) = 0;
/**
* Callback called when entering or leaving suspend mode
*
* @param suspended true if entering suspend mode false otherwise
* @note called in the contex of USBPhy::process
*/
virtual void suspend(bool suspended) = 0;
/**
* Callback called on start of frame
*
* @param frame_number The current frame number
* @note This callback is enabled/disabled by
* calling USBPhy::sof_enable / USBPhy::sof_disable
* @note called in the contex of USBPhy::process
*/
virtual void sof(int frame_number) = 0;
/**
* Callback called on the reception of an OUT packet
*
* @param endpoint Endpoint which received the OUT packet
* @note called in the contex of USBPhy::process
*/
virtual void out(usb_ep_t endpoint) = 0;
/**
* Callback called on the transmission of an IN packet
*
* @param endpoint Endpoint which sent the IN packet
* @note called in the contex of USBPhy::process
*/
virtual void in(usb_ep_t endpoint) = 0;
/**
* Callback called to indicate the USB processing needs to be done
*/
virtual void start_process() = 0;
};
#endif

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBPHY_TYPES_H
#define USBPHY_TYPES_H
#include <stdint.h>
typedef uint8_t usb_ep_t;
typedef enum {
USB_EP_TYPE_CTRL = 0,
USB_EP_TYPE_ISO = 1,
USB_EP_TYPE_BULK = 2,
USB_EP_TYPE_INT = 3
} usb_ep_type_t;
enum {
USB_EP_ATTR_ALLOW_CTRL = 1 << USB_EP_TYPE_CTRL,
USB_EP_ATTR_ALLOW_BULK = 1 << USB_EP_TYPE_BULK,
USB_EP_ATTR_ALLOW_INT = 1 << USB_EP_TYPE_INT,
USB_EP_ATTR_ALLOW_ISO = 1 << USB_EP_TYPE_ISO,
USB_EP_ATTR_ALLOW_ALL = USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_ALLOW_BULK |
USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_ALLOW_ISO,
USB_EP_ATTR_DIR_IN = 0 << 4,
USB_EP_ATTR_DIR_OUT = 1 << 4,
USB_EP_ATTR_DIR_IN_OR_OUT = 2 << 4,
USB_EP_ATTR_DIR_IN_AND_OUT = 3 << 4,
USB_EP_ATTR_DIR_MASK = 3 << 4
};
typedef uint8_t usb_ep_attr_t;
struct usb_ep_entry_t {
usb_ep_attr_t attributes;
uint8_t byte_cost;
uint16_t base_cost;
};
struct usb_ep_table_t {
uint32_t resources;
usb_ep_entry_t table[16];
};
#endif

View File

@ -0,0 +1,645 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBCDC.h"
#include "EndpointResolver.h"
#include "AsyncOp.h"
#include "usb_phy_api.h"
static const uint8_t cdc_line_coding_default[7] = {0x80, 0x25, 0x00, 0x00, 0x00, 0x00, 0x08};
#define DEFAULT_CONFIGURATION (1)
#define CDC_SET_LINE_CODING 0x20
#define CDC_GET_LINE_CODING 0x21
#define CDC_SET_CONTROL_LINE_STATE 0x22
// Control Line State bits
#define CLS_DTR (1 << 0)
#define CLS_RTS (1 << 1)
#define CDC_MAX_PACKET_SIZE 64
class USBCDC::AsyncWrite: public AsyncOp {
public:
AsyncWrite(USBCDC *serial, uint8_t *buf, uint32_t size):
serial(serial), tx_buf(buf), tx_size(size), result(false)
{
}
virtual ~AsyncWrite()
{
}
virtual bool process()
{
if (!serial->_terminal_connected) {
result = false;
return true;
}
uint32_t actual_size = 0;
serial->send_nb(tx_buf, tx_size, &actual_size, true);
tx_size -= actual_size;
tx_buf += actual_size;
if (tx_size == 0) {
result = true;
return true;
}
// Start transfer if it hasn't been
serial->_send_isr_start();
return false;
}
USBCDC *serial;
uint8_t *tx_buf;
uint32_t tx_size;
bool result;
};
class USBCDC::AsyncRead: public AsyncOp {
public:
AsyncRead(USBCDC *serial, uint8_t *buf, uint32_t size, uint32_t *size_read, bool read_all)
: serial(serial), rx_buf(buf), rx_size(size), rx_actual(size_read), all(read_all), result(false)
{
}
virtual ~AsyncRead()
{
}
virtual bool process()
{
if (!serial->_terminal_connected) {
result = false;
return true;
}
uint32_t actual_size = 0;
serial->receive_nb(rx_buf, rx_size, &actual_size);
rx_buf += actual_size;
*rx_actual += actual_size;
rx_size -= actual_size;
if ((!all && *rx_actual > 0) || (rx_size == 0)) {
// Wake thread if request is done
result = true;
return true;
}
serial->_receive_isr_start();
return false;
}
USBCDC *serial;
uint8_t *rx_buf;
uint32_t rx_size;
uint32_t *rx_actual;
bool all;
bool result;
};
class USBCDC::AsyncWait: public AsyncOp {
public:
AsyncWait(USBCDC *serial)
: serial(serial)
{
}
virtual ~AsyncWait()
{
}
virtual bool process()
{
if (serial->_terminal_connected) {
return true;
}
return false;
}
USBCDC *serial;
};
USBCDC::USBCDC(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
{
_init();
if (connect_blocking) {
connect();
wait_ready();
} else {
init();
}
}
USBCDC::USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init();
}
USBCDC::~USBCDC()
{
deinit();
}
void USBCDC::_init()
{
memcpy(_cdc_line_coding, cdc_line_coding_default, sizeof(_cdc_line_coding));
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(CDC_MAX_PACKET_SIZE);
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE);
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, CDC_MAX_PACKET_SIZE);
_int_in = resolver.endpoint_in(USB_EP_TYPE_INT, CDC_MAX_PACKET_SIZE);
MBED_ASSERT(resolver.valid());
_terminal_connected = false;
_tx_in_progress = false;
_tx_buf = _tx_buffer;
_tx_size = 0;
_rx_in_progress = false;
_rx_buf = _rx_buffer;
_rx_size = 0;
}
void USBCDC::callback_reset()
{
assert_locked();
/* Called in ISR context */
_change_terminal_connected(false);
};
void USBCDC::callback_state_change(DeviceState new_state)
{
assert_locked();
/* Called in ISR context */
if (new_state != Configured) {
_change_terminal_connected(false);
}
}
void USBCDC::callback_request(const setup_packet_t *setup)
{
assert_locked();
/* Called in ISR context */
RequestResult result = PassThrough;
uint8_t *data = NULL;
uint32_t size = 0;
/* Only process class-specific requests */
if (setup->bmRequestType.Type == CLASS_TYPE) {
switch (setup->bRequest) {
case CDC_GET_LINE_CODING:
result = Send;
data = _cdc_line_coding;
size = 7;
break;
case CDC_SET_LINE_CODING:
result = Receive;
data = _cdc_new_line_coding;
size = 7;
break;
case CDC_SET_CONTROL_LINE_STATE:
if (setup->wValue & CLS_DTR) {
_change_terminal_connected(true);
} else {
_change_terminal_connected(false);
}
result = Success;
break;
default:
result = Failure;
break;
}
}
complete_request(result, data, size);
}
void USBCDC::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
assert_locked();
/* Called in ISR context */
if (aborted) {
complete_request_xfer_done(false);
return;
}
bool success = false;
/* Process class-specific requests */
if (setup->bmRequestType.Type == CLASS_TYPE) {
if ((setup->bRequest == CDC_SET_LINE_CODING) && (setup->wLength == 7)) {
if (memcmp(_cdc_line_coding, _cdc_new_line_coding, 7)) {
memcpy(_cdc_line_coding, _cdc_new_line_coding, 7);
const uint8_t *buf = _cdc_line_coding;
int baud = buf[0] + (buf[1] << 8)
+ (buf[2] << 16) + (buf[3] << 24);
int stop = buf[4];
int bits = buf[6];
int parity = buf[5];
line_coding_changed(baud, bits, parity, stop);
}
success = true;
}
if (setup->bRequest == CDC_GET_LINE_CODING) {
success = true;
}
}
complete_request_xfer_done(success);
}
void USBCDC::callback_set_configuration(uint8_t configuration)
{
assert_locked();
/* Called in ISR context */
bool ret = false;
if (configuration == DEFAULT_CONFIGURATION) {
// Configure endpoints > 0
endpoint_add(_int_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_INT);
endpoint_add(_bulk_in, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_send_isr);
endpoint_add(_bulk_out, CDC_MAX_PACKET_SIZE, USB_EP_TYPE_BULK, &USBCDC::_receive_isr);
read_start(_bulk_out, _rx_buf, sizeof(_rx_buffer));
_rx_in_progress = true;
ret = true;
}
complete_set_configuration(ret);
}
void USBCDC::callback_set_interface(uint16_t interface, uint8_t alternate)
{
assert_locked();
complete_set_interface(true);
}
void USBCDC::_change_terminal_connected(bool connected)
{
assert_locked();
_terminal_connected = connected;
if (!_terminal_connected) {
// Abort TX
if (_tx_in_progress) {
endpoint_abort(_bulk_in);
_tx_in_progress = false;
}
_tx_buf = _tx_buffer;
_tx_size = 0;
_tx_list.process();
MBED_ASSERT(_tx_list.empty());
// Abort RX
if (_rx_in_progress) {
endpoint_abort(_bulk_in);
_rx_in_progress = false;
}
_rx_buf = _rx_buffer;
_rx_size = 0;
_rx_list.process();
MBED_ASSERT(_rx_list.empty());
}
_connected_list.process();
}
bool USBCDC::ready()
{
lock();
bool ready = _terminal_connected;
unlock();
return ready;
}
void USBCDC::wait_ready()
{
lock();
AsyncWait wait_op(this);
_connected_list.add(&wait_op);
unlock();
wait_op.wait(NULL);
}
bool USBCDC::send(uint8_t *buffer, uint32_t size)
{
lock();
AsyncWrite write_op(this, buffer, size);
_tx_list.add(&write_op);
unlock();
write_op.wait(NULL);
return write_op.result;
}
void USBCDC::send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now)
{
lock();
*actual = 0;
if (_terminal_connected && !_tx_in_progress) {
uint32_t free = sizeof(_tx_buffer) - _tx_size;
uint32_t write_size = free > size ? size : free;
if (size > 0) {
memcpy(_tx_buf, buffer, write_size);
}
_tx_size += write_size;
*actual = write_size;
if (now) {
_send_isr_start();
}
}
unlock();
}
void USBCDC::_send_isr_start()
{
assert_locked();
if (!_tx_in_progress && _tx_size) {
if (USBDevice::write_start(_bulk_in, _tx_buffer, _tx_size)) {
_tx_in_progress = true;
}
}
}
/*
* Called by when CDC data is sent
* Warning: Called in ISR
*/
void USBCDC::_send_isr()
{
assert_locked();
write_finish(_bulk_in);
_tx_buf = _tx_buffer;
_tx_size = 0;
_tx_in_progress = false;
_tx_list.process();
if (!_tx_in_progress) {
data_tx();
}
}
bool USBCDC::receive(uint8_t *buffer, uint32_t size, uint32_t *size_read)
{
lock();
bool read_all = size_read == NULL;
uint32_t size_read_dummy;
uint32_t *size_read_ptr = read_all ? &size_read_dummy : size_read;
*size_read_ptr = 0;
AsyncRead read_op(this, buffer, size, size_read_ptr, read_all);
_rx_list.add(&read_op);
unlock();
read_op.wait(NULL);
return read_op.result;
}
void USBCDC::receive_nb(uint8_t *buffer, uint32_t size, uint32_t *size_read)
{
*size_read = 0;
if (_terminal_connected && !_rx_in_progress) {
// Copy data over
uint32_t copy_size = _rx_size > size ? size : _rx_size;
memcpy(buffer, _rx_buf, copy_size);
*size_read = copy_size;
_rx_buf += copy_size;
_rx_size -= copy_size;
if (_rx_size == 0) {
_receive_isr_start();
}
}
}
void USBCDC::_receive_isr_start()
{
if ((_rx_size == 0) && !_rx_in_progress) {
// Refill the buffer
read_start(_bulk_out, _rx_buffer, sizeof(_rx_buffer));
_rx_in_progress = true;
}
}
/*
* Called by when CDC data is received
* Warning: Called in ISR
*/
void USBCDC::_receive_isr()
{
assert_locked();
MBED_ASSERT(_rx_size == 0);
_rx_buf = _rx_buffer;
_rx_size = read_finish(_bulk_out);
_rx_in_progress = false;
_rx_list.process();
if (!_rx_in_progress) {
data_rx();
}
}
const uint8_t *USBCDC::device_desc()
{
uint8_t ep0_size = endpoint_max_packet_size(0x00);
uint8_t device_descriptor_temp[] = {
18, // bLength
1, // bDescriptorType
0x10, 0x01, // bcdUSB
2, // 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 *USBCDC::string_iinterface_desc()
{
static const uint8_t stringIinterfaceDescriptor[] = {
0x08,
STRING_DESCRIPTOR,
'C', 0, 'D', 0, 'C', 0,
};
return stringIinterfaceDescriptor;
}
const uint8_t *USBCDC::string_iproduct_desc()
{
static const uint8_t stringIproductDescriptor[] = {
0x16,
STRING_DESCRIPTOR,
'C', 0, 'D', 0, 'C', 0, ' ', 0, 'D', 0, 'E', 0, 'V', 0, 'I', 0, 'C', 0, 'E', 0
};
return stringIproductDescriptor;
}
#define CONFIG1_DESC_SIZE (9+8+9+5+5+4+5+7+9+7+7)
const uint8_t *USBCDC::configuration_desc(uint8_t index)
{
uint8_t config_descriptor_temp[] = {
// configuration descriptor
9, // bLength
2, // bDescriptorType
LSB(CONFIG1_DESC_SIZE), // wTotalLength
MSB(CONFIG1_DESC_SIZE),
2, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0x80, // bmAttributes
50, // bMaxPower
// IAD to associate the two CDC interfaces
0x08, // bLength
0x0b, // bDescriptorType
0x00, // bFirstInterface
0x02, // bInterfaceCount
0x02, // bFunctionClass
0x02, // bFunctionSubClass
0, // bFunctionProtocol
0, // iFunction
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
9, // bLength
4, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
1, // bNumEndpoints
0x02, // bInterfaceClass
0x02, // bInterfaceSubClass
0x01, // bInterfaceProtocol
0, // iInterface
// CDC Header Functional Descriptor, CDC Spec 5.2.3.1, Table 26
5, // bFunctionLength
0x24, // bDescriptorType
0x00, // bDescriptorSubtype
0x10, 0x01, // bcdCDC
// Call Management Functional Descriptor, CDC Spec 5.2.3.2, Table 27
5, // bFunctionLength
0x24, // bDescriptorType
0x01, // bDescriptorSubtype
0x03, // bmCapabilities
1, // bDataInterface
// Abstract Control Management Functional Descriptor, CDC Spec 5.2.3.3, Table 28
4, // bFunctionLength
0x24, // bDescriptorType
0x02, // bDescriptorSubtype
0x06, // bmCapabilities
// Union Functional Descriptor, CDC Spec 5.2.3.8, Table 33
5, // bFunctionLength
0x24, // bDescriptorType
0x06, // bDescriptorSubtype
0, // bMasterInterface
1, // bSlaveInterface0
// 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=intr)
LSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB)
16, // bInterval
// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
9, // bLength
4, // bDescriptorType
1, // bInterfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
0x0A, // bInterfaceClass
0x00, // bInterfaceSubClass
0x00, // 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(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(CDC_MAX_PACKET_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(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (LSB)
MSB(CDC_MAX_PACKET_SIZE), // wMaxPacketSize (MSB)
0 // bInterval
};
if (index == 0) {
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
memcpy(_config_descriptor, config_descriptor_temp, sizeof(_config_descriptor));
return _config_descriptor;
} else {
return NULL;
}
}

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBCDC_H
#define USBCDC_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "OperationList.h"
class AsyncOp;
class USBCDC: public USBDevice {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBCDC(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBCDC(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBCDC();
/**
* Check if this class is ready
*
* @return true if a terminal is connected, false otherwise
*/
bool ready();
/**
* Block until the terminal is connected
*/
void wait_ready();
/*
* Send a buffer
*
* This function blocks until the full contents have been sent.
*
* @param buffer buffer to be sent
* @param size length of the buffer
* @returns true if successful false if interrupted due to a state change
*/
bool send(uint8_t *buffer, uint32_t size);
/**
* Send what there is room for
*
* @param buffer data to send
* @param size maximum number of bytes to send
* @param actual a pointer to where to store the number of bytes sent
* @param now true to start data transmission, false to wait
*/
void send_nb(uint8_t *buffer, uint32_t size, uint32_t *actual, bool now = true);
/*
* Read a buffer from a certain endpoint. Warning: blocking
*
* Blocks until at least one byte of data has been read (actual != NULL) or
* until the full size has been read (actual == NULL).
*
* @param buffer buffer where will be stored bytes
* @param size the maximum number of bytes to read
* @param actual A pointer to where to store the number of bytes actually read
* or NULL to read the full size
* @returns true if successful false if interrupted due to a state change
*/
bool receive(uint8_t *buffer, uint32_t size, uint32_t *actual = NULL);
/**
* Read from the receive buffer
*
* @param buffer buffer to fill with data
* @param size maximum number of bytes read
* @param actual a pointer to where to store the number of bytes actually received
*/
void receive_nb(uint8_t *buffer, uint32_t size, uint32_t *actual);
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
*
* @param index descriptor index
* @returns pointer to the configuration descriptor
*/
virtual const uint8_t *configuration_desc(uint8_t index);
/*
* Called by USBCallback_requestCompleted when CDC line coding is changed
* Warning: Called in ISR
*
* @param baud The baud rate
* @param bits The number of bits in a word (5-8)
* @param parity The parity
* @param stop The number of stop bits (1 or 2)
*/
virtual void line_coding_changed(int baud, int bits, int parity, int stop) {};
/*
* Called when there is data that can be read
*/
virtual void data_rx() {}
/*
* Called when there is space in the TX buffer
*/
virtual void data_tx() {}
protected:
class AsyncWrite;
class AsyncRead;
class AsyncWait;
virtual void callback_reset();
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);
void _init();
void _change_terminal_connected(bool connected);
void _send_isr_start();
void _send_isr();
void _receive_isr_start();
void _receive_isr();
usb_ep_t _bulk_in;
usb_ep_t _bulk_out;
usb_ep_t _int_in;
uint8_t _cdc_line_coding[7];
uint8_t _cdc_new_line_coding[7];
uint8_t _config_descriptor[75];
OperationList<AsyncWait> _connected_list;
bool _terminal_connected;
OperationList<AsyncWrite> _tx_list;
bool _tx_in_progress;
uint8_t _tx_buffer[64];
uint8_t *_tx_buf;
uint32_t _tx_size;
OperationList<AsyncRead> _rx_list;
bool _rx_in_progress;
uint8_t _rx_buffer[64];
uint8_t *_rx_buf;
uint32_t _rx_size;
};
#endif

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBSerial.h"
#include "usb_phy_api.h"
USBSerial::USBSerial(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBCDC(get_usb_phy(), vendor_id, product_id, product_release)
{
_settings_changed_callback = 0;
if (connect_blocking) {
connect();
wait_ready();
} else {
init();
}
}
USBSerial::USBSerial(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release):
USBCDC(phy, vendor_id, product_id, product_release)
{
_settings_changed_callback = 0;
}
USBSerial::~USBSerial()
{
deinit();
}
int USBSerial::_putc(int c)
{
if (send((uint8_t *)&c, 1)) {
return c;
} else {
return -1;
}
}
int USBSerial::_getc()
{
uint8_t c = 0;
if (receive(&c, sizeof(c))) {
return c;
} else {
return -1;
}
}
void USBSerial::data_rx()
{
assert_locked();
//call a potential handler
if (rx) {
rx.call();
}
}
uint8_t USBSerial::available()
{
USBCDC::lock();
uint8_t size = 0;
if (!_rx_in_progress) {
size = _rx_size > 0xFF ? 0xFF : _rx_size;
}
USBCDC::unlock();
return size;
}
bool USBSerial::connected()
{
return _terminal_connected;
}

View File

@ -0,0 +1,219 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBSERIAL_H
#define USBSERIAL_H
#include "USBCDC.h"
#include "Stream.h"
#include "Callback.h"
/**
* USBSerial example
*
* @code
* #include "mbed.h"
* #include "USBSerial.h"
*
* //Virtual serial port over USB
* USBSerial serial;
*
* int main(void) {
*
* while(1)
* {
* serial.printf("I am a virtual serial port\n");
* wait(1);
* }
* }
* @endcode
*/
class USBSerial: public USBCDC, public mbed::Stream {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id (default: 0x1f00)
* @param product_id Your product_id (default: 0x2012)
* @param product_release Your product_release (default: 0x0001)
*
*/
USBSerial(bool connect_blocking = true, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id (default: 0x1f00)
* @param product_id Your product_id (default: 0x2012)
* @param product_release Your product_release (default: 0x0001)
*
*/
USBSerial(USBPhy *phy, uint16_t vendor_id = 0x1f00, uint16_t product_id = 0x2012, uint16_t product_release = 0x0001);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBSerial();
/**
* Send a character. You can use puts, printf.
*
* @param c character to be sent
* @returns true if there is no error, false otherwise
*/
virtual int _putc(int c);
/**
* Read a character: blocking
*
* @returns character read
*/
virtual int _getc();
/**
* Check the number of bytes available.
*
* @returns the number of bytes available
*/
uint8_t available();
/**
* Check if the terminal is connected.
*
* @returns connection status
*/
bool connected();
/** Determine if there is a character available to read
*
* @returns
* 1 if there is a character available to read,
* 0 otherwise
*/
int readable()
{
return available() ? 1 : 0;
}
/** Determine if there is space available to write a character
*
* @returns
* 1 if there is space to write a character,
* 0 otherwise
*/
int writeable()
{
return 1; // always return 1, for write operation is blocking
}
/**
* Attach a member function to call when a packet is received.
*
* @param tptr pointer to the object to call the member function on
* @param mptr pointer to the member function to be called
*/
template<typename T>
void attach(T *tptr, void (T::*mptr)(void))
{
USBCDC::lock();
if ((mptr != NULL) && (tptr != NULL)) {
rx = mbed::Callback<void()>(mptr, tptr);
}
USBCDC::unlock();
}
/**
* Attach a callback called when a packet is received
*
* @param fptr function pointer
*/
void attach(void (*fptr)(void))
{
USBCDC::lock();
if (fptr != NULL) {
rx = mbed::Callback<void()>(fptr);
}
USBCDC::unlock();
}
/**
* Attach a Callback called when a packet is received
*
* @param cb Callback to attach
*/
void attach(mbed::Callback<void()> &cb)
{
USBCDC::lock();
rx = cb;
USBCDC::unlock();
}
/**
* Attach a callback to call when serial's settings are changed.
*
* @param fptr function pointer
*/
void attach(void (*fptr)(int baud, int bits, int parity, int stop))
{
USBCDC::lock();
_settings_changed_callback = fptr;
USBCDC::unlock();
}
protected:
virtual void data_rx();
virtual void line_coding_changed(int baud, int bits, int parity, int stop)
{
assert_locked();
if (_settings_changed_callback) {
_settings_changed_callback(baud, bits, parity, stop);
}
}
private:
mbed::Callback<void()> rx;
void (*_settings_changed_callback)(int baud, int bits, int parity, int stop);
};
#endif

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 <stddef.h>
#include "usb_phy_api.h"
#include "mbed_error.h"
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
USBPhy *get_usb_phy()
{
error("This board does not have a hardware USB driver");
return NULL;
}
#endif

View File

@ -0,0 +1,37 @@
/** \addtogroup hal */
/** @{*/
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_USB_PHY_API_H
#define MBED_USB_PHY_API_H
#include "USBPhy.h"
/** Return a the USBPhy instance for this hardware
*
* For details on adding support for a USBPhy see the specification in USBPhy.h.
*
* @return A pointer to a USBPhy instance
* @note Calling this function on platforms without a USBPhy will result in an
* error
*/
USBPhy *get_usb_phy();
#endif
/** @}*/

View File

@ -0,0 +1,61 @@
/* mbed Microcontroller Library
* 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.
*/
#define NUMBER_OF_LOGICAL_ENDPOINTS (4)
#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2)
/* Define physical endpoint numbers */
/* Endpoint No. */
/* ---------------- */
#define EP0OUT (0x00)
#define EP0IN (0x80)
#define EP1OUT (0x01)
#define EP1IN (0x81)
#define EP2OUT (0x02)
#define EP2IN (0x82)
#define EP3OUT (0x03)
#define EP3IN (0x83)
/* Maximum Packet sizes */
#define MAX_PACKET_SIZE_EP0 (64)
#define MAX_PACKET_SIZE_EP1 (64)
#define MAX_PACKET_SIZE_EP2 (64)
#define MAX_PACKET_SIZE_EP3 (1023)
/* Generic endpoints - intended to be portable accross devices */
/* and be suitable for simple USB devices. */
/* Bulk endpoints */
#define EPBULK_OUT (EP2OUT)
#define EPBULK_IN (EP2IN)
#define EPBULK_OUT_callback EP2_OUT_callback
#define EPBULK_IN_callback EP2_IN_callback
/* Interrupt endpoints */
#define EPINT_OUT (EP1OUT)
#define EPINT_IN (EP1IN)
#define EPINT_OUT_callback EP1_OUT_callback
#define EPINT_IN_callback EP1_IN_callback
/* Isochronous endpoints */
#define EPISO_OUT (EP3OUT)
#define EPISO_IN (EP3IN)
#define EPISO_OUT_callback EP3_OUT_callback
#define EPISO_IN_callback EP3_IN_callback
#define MAX_PACKET_SIZE_EPBULK (MAX_PACKET_SIZE_EP2)
#define MAX_PACKET_SIZE_EPINT (MAX_PACKET_SIZE_EP1)
#define MAX_PACKET_SIZE_EPISO (MAX_PACKET_SIZE_EP3)

View File

@ -0,0 +1,72 @@
/* mbed Microcontroller Library
* 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.
*/
#ifndef USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t* endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
private:
USBPhyEvents *events;
uint8_t *read_buffers[16];
uint16_t read_sizes[16];
bool endpoint_read_core(usb_ep_t endpoint, uint32_t max_packet);
bool endpoint_read_result_core(usb_ep_t endpoint, uint8_t *data, uint32_t size, uint32_t *bytesRead);
static void _usbisr(void);
};
#endif

View File

@ -0,0 +1,713 @@
/* mbed Microcontroller Library
* 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.
*/
#if defined(DEVICE_USBDEVICE) && DEVICE_USBDEVICE && \
(defined(TARGET_KL25Z) | defined(TARGET_KL43Z) | \
defined(TARGET_KL46Z) | defined(TARGET_K20D50M) | \
defined(TARGET_K64F) | defined(TARGET_K22F) | \
defined(TARGET_TEENSY3_1))
#if defined(TARGET_KSDK2_MCUS)
#include "fsl_common.h"
#endif
#include "USBPhyHw.h"
#include "USBEndpoints_Kinetis.h"
#include "mbed_critical.h"
static USBPhyHw *instance;
static volatile int epComplete = 0;
// Convert physical endpoint number to register bit
#define EP(endpoint) (1<<(endpoint))
// Conversion macros
#define PHY_TO_LOG(endpoint) ((endpoint)>>1)
#define DESC_TO_LOG(endpoint) ((endpoint) & 0xF)
#define DESC_TO_PHY(endpoint) ((((endpoint)&0x0F)<<1) | (((endpoint) & 0x80) ? 1:0))
#define PHY_TO_DESC(endpoint) (((endpoint)>>1)|(((endpoint)&1)?0x80:0))
// Get endpoint direction
#define DESC_EP_IN(endpoint) ((endpoint) & 0x80U ? true : false)
#define DESC_EP_OUT(endpoint) ((endpoint) & 0x80U ? false : true)
#define BD_OWN_MASK (1<<7)
#define BD_DATA01_MASK (1<<6)
#define BD_KEEP_MASK (1<<5)
#define BD_NINC_MASK (1<<4)
#define BD_DTS_MASK (1<<3)
#define BD_STALL_MASK (1<<2)
#define TX 1
#define RX 0
#define ODD 0
#define EVEN 1
// this macro waits a physical endpoint number
#define EP_BDT_IDX(ep, dir, odd) (((ep * 4) + (2 * dir) + (1 * odd)))
#define SETUP_TOKEN 0x0D
#define IN_TOKEN 0x09
#define OUT_TOKEN 0x01
#define TOK_PID(idx) ((bdt[idx].info >> 2) & 0x0F)
// for each endpt: 8 bytes
typedef struct BDT {
uint8_t info; // BD[0:7]
uint8_t dummy; // RSVD: BD[8:15]
uint16_t byte_count; // BD[16:32]
uint32_t address; // Addr
} BDT;
typedef enum {
CTRL_XFER_READY,
CTRL_XFER_IN,
CTRL_XFER_NONE,
CTRL_XFER_OUT
} ctrl_xfer_t;
// there are:
// * 4 bidirectionnal endpt -> 8 physical endpt
// * as there are ODD and EVEN buffer -> 8*2 bdt
MBED_ALIGN(512) BDT bdt[NUMBER_OF_PHYSICAL_ENDPOINTS * 2]; // 512 bytes aligned!
uint8_t * endpoint_buffer[NUMBER_OF_PHYSICAL_ENDPOINTS * 2];
uint8_t ep0_buffer[2][MAX_PACKET_SIZE_EP0];
uint8_t ep1_buffer[2][MAX_PACKET_SIZE_EP1];
uint8_t ep2_buffer[2][MAX_PACKET_SIZE_EP2];
uint8_t ep3_buffer[2][MAX_PACKET_SIZE_EP3];
static bool setup_suspend = false;
static uint8_t set_addr = 0;
static uint8_t addr = 0;
static ctrl_xfer_t ctrl_xfer = CTRL_XFER_READY;
static uint32_t Data1 = 0x55555555;
static uint32_t frameNumber() {
return((USB0->FRMNUML | (USB0->FRMNUMH << 8)) & 0x07FF);
}
USBPhy *get_usb_phy()
{
static USBPhyHw usbphy;
return &usbphy;
}
USBPhyHw::USBPhyHw()
{
}
USBPhyHw::~USBPhyHw()
{
}
void USBPhyHw::init(USBPhyEvents *events)
{
this->events = events;
// Disable IRQ
NVIC_DisableIRQ(USB0_IRQn);
#if (defined(FSL_FEATURE_SOC_MPU_COUNT) && (FSL_FEATURE_SOC_MPU_COUNT > 0U))
MPU->CESR=0;
#endif
#if (defined(FSL_FEATURE_SOC_SYSMPU_COUNT) && (FSL_FEATURE_SOC_SYSMPU_COUNT > 0U))
SYSMPU->CESR=0;
#endif
#if defined(TARGET_KL43Z) || defined(TARGET_K22F) || defined(TARGET_K64F)
// enable USBFS clock
CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U);
#else
// choose usb src as PLL
SIM->SOPT2 &= ~SIM_SOPT2_PLLFLLSEL_MASK;
SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | (1 << SIM_SOPT2_PLLFLLSEL_SHIFT));
// enable OTG clock
SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
#endif
// Attach IRQ
instance = this;
// USB Module Configuration
// Set BDT Base Register
USB0->BDTPAGE1 = (uint8_t)((uint32_t)bdt>>8);
USB0->BDTPAGE2 = (uint8_t)((uint32_t)bdt>>16);
USB0->BDTPAGE3 = (uint8_t)((uint32_t)bdt>>24);
// Clear interrupt flag
USB0->ISTAT = 0xff;
// USB Interrupt Enablers
USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK |
USB_INTEN_ERROREN_MASK |
USB_INTEN_USBRSTEN_MASK;
// Disable weak pull downs
USB0->USBCTRL &= ~(USB_USBCTRL_PDE_MASK | USB_USBCTRL_SUSP_MASK);
USB0->USBTRC0 |= 0x40;
/* Allocate control endpoint buffers */
endpoint_buffer[EP_BDT_IDX(0, TX, ODD)] = ep0_buffer[TX];
endpoint_buffer[EP_BDT_IDX(0, RX, ODD)] = ep0_buffer[RX];
endpoint_buffer[EP_BDT_IDX(1, TX, ODD)] = ep1_buffer[TX];
endpoint_buffer[EP_BDT_IDX(1, RX, ODD)] = ep1_buffer[RX];
endpoint_buffer[EP_BDT_IDX(2, TX, ODD)] = ep2_buffer[TX];
endpoint_buffer[EP_BDT_IDX(2, RX, ODD)] = ep2_buffer[RX];
endpoint_buffer[EP_BDT_IDX(3, TX, ODD)] = ep3_buffer[TX];
endpoint_buffer[EP_BDT_IDX(3, RX, ODD)] = ep3_buffer[RX];
NVIC_SetVector(USB0_IRQn, (uint32_t)&_usbisr);
NVIC_EnableIRQ(USB0_IRQn);
}
void USBPhyHw::deinit()
{
disconnect();
NVIC_DisableIRQ(USB0_IRQn);
USB0->INTEN = 0;
}
bool USBPhyHw::powered()
{
return true;
}
void USBPhyHw::connect()
{
// enable USB
USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
// Pull up enable
USB0->CONTROL |= USB_CONTROL_DPPULLUPNONOTG_MASK;
}
void USBPhyHw::disconnect()
{
// disable all endpoints to prevent them from nacking when disconnected
for(int i = 0; i < 16; i++) {
USB0->ENDPOINT[i].ENDPT = 0x00;
}
// disable USB
USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
// Pull up disable
USB0->CONTROL &= ~USB_CONTROL_DPPULLUPNONOTG_MASK;
while (USB0->ISTAT) {
USB0->ISTAT = 0xFF;
}
}
void USBPhyHw::configure()
{
// not needed
}
void USBPhyHw::unconfigure()
{
// not needed
}
void USBPhyHw::sof_enable()
{
USB0->INTEN |= USB_INTEN_SOFTOKEN_MASK;
}
void USBPhyHw::sof_disable()
{
USB0->INTEN &= ~USB_INTEN_SOFTOKEN_MASK;
}
void USBPhyHw::set_address(uint8_t address)
{
// we don't set the address now otherwise the usb controller does not ack
// we set a flag instead
// see usbisr when an IN token is received
set_addr = 1;
addr = address;
}
void USBPhyHw::remote_wakeup()
{
// TODO
}
#define ALLOW_BULK_OR_INT_ENDPOINTS (USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_ALLOW_INT)
#define ALLOW_NO_ENDPOINTS 0
const usb_ep_table_t* USBPhyHw::endpoint_table()
{
static const usb_ep_table_t endpoint_table = {
1, // No cost per endpoint - everything allocated up front
{
{USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_BULK_OR_INT_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_BULK_OR_INT_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ALLOW_NO_ENDPOINTS | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0}
}
};
return &endpoint_table;
}
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
{
return MAX_PACKET_SIZE_EP0;
}
// read setup packet
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
{
uint32_t sz;
endpoint_read_result_core(EP0OUT, buffer, size, &sz);
}
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
{
if (ctrl_xfer == CTRL_XFER_READY) {
// Transfer is done so ignore call
return;
}
if (ctrl_xfer == CTRL_XFER_IN) {
ctrl_xfer = CTRL_XFER_READY;
// Control transfer with a data IN stage.
// The next packet received will be the status packet - an OUT packet using DATA1
//
// PROBLEM:
// If a Setup packet is received after status packet of
// a Control In transfer has been received in the RX buffer
// but before the processor has had a chance the prepare
// this buffer for the Setup packet, the Setup packet
// will be dropped.
//
// WORKAROUND:
// Set data toggle to DATA0 so if the status stage of a
// Control In transfer arrives it will be ACKed by hardware
// but will be discarded without filling the RX buffer.
// This allows a subsequent SETUP packet to be stored
// without any processor intervention.
Data1 &= ~1UL; // set DATA0
endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
} else {
endpoint_read(EP0OUT, data, size);
}
// Clear suspend after the setup stage
if (setup_suspend) {
USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
setup_suspend = false;
}
}
uint32_t USBPhyHw::ep0_read_result()
{
return endpoint_read_result(EP0OUT);
}
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
{
if (ctrl_xfer == CTRL_XFER_READY) {
// Transfer is done so ignore call
return;
}
if ((ctrl_xfer == CTRL_XFER_NONE) || (ctrl_xfer == CTRL_XFER_OUT)) {
// Prepare for next setup packet
endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
ctrl_xfer = CTRL_XFER_READY;
}
endpoint_write(EP0IN, buffer, size);
// Clear suspend after the setup stage
if (setup_suspend) {
USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
setup_suspend = false;
}
}
void USBPhyHw::ep0_stall()
{
if (ctrl_xfer == CTRL_XFER_READY) {
// Transfer is done so ignore call
return;
}
ctrl_xfer = CTRL_XFER_READY;
// Clear suspend after the setup stage
if (setup_suspend) {
USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
setup_suspend = false;
}
core_util_critical_section_enter();
endpoint_stall(EP0OUT);
// Prepare for next setup packet
// Note - time between stalling and setting up the endpoint
// must be kept to a minimum to prevent a dropped SETUP
// packet.
endpoint_read_core(EP0OUT, MAX_PACKET_SIZE_EP0);
core_util_critical_section_exit();
}
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
{
uint32_t handshake_flag = 0;
uint8_t * buf;
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
return false;
}
uint32_t log_endpoint = DESC_TO_LOG(endpoint);
if (type != USB_EP_TYPE_ISO) {
handshake_flag = USB_ENDPT_EPHSHK_MASK;
}
if (DESC_EP_IN(endpoint)) {
buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, TX, ODD)][0];
} else {
buf = &endpoint_buffer[EP_BDT_IDX(log_endpoint, RX, ODD)][0];
}
// IN endpt -> device to host (TX)
if (DESC_EP_IN(endpoint)) {
bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].address = (uint32_t) buf;
bdt[EP_BDT_IDX(log_endpoint, TX, ODD )].info = 0;
bdt[EP_BDT_IDX(log_endpoint, TX, EVEN)].address = 0;
USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
USB_ENDPT_EPTXEN_MASK; // en TX (IN) tran
}
// OUT endpt -> host to device (RX)
else {
bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].byte_count = max_packet;
bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].address = (uint32_t) buf;
bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info = BD_DTS_MASK;
bdt[EP_BDT_IDX(log_endpoint, RX, EVEN)].info = 0;
if (log_endpoint == 0) {
// Prepare for setup packet
bdt[EP_BDT_IDX(log_endpoint, RX, ODD )].info |= BD_OWN_MASK;
}
USB0->ENDPOINT[log_endpoint].ENDPT |= handshake_flag | // ep handshaking (not if iso endpoint)
USB_ENDPT_EPRXEN_MASK; // en RX (OUT) tran.
}
// First transfer will be a DATA0 packet
Data1 &= ~(1 << DESC_TO_PHY(endpoint));
return true;
}
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
{
USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT = 0;
}
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
{
USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT |= USB_ENDPT_EPSTALL_MASK;
}
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
{
// Next transfer will be a DATA0 packet
Data1 &= ~(1 << DESC_TO_PHY(endpoint));
USB0->ENDPOINT[DESC_TO_LOG(endpoint)].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
}
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
uint8_t log = DESC_TO_LOG(endpoint);
read_buffers[log] = data;
read_sizes[log] = size;
return endpoint_read_core(endpoint, size);
}
bool USBPhyHw::endpoint_read_core(usb_ep_t endpoint, uint32_t max_packet)
{
uint8_t log_endpoint = DESC_TO_LOG(endpoint);
uint32_t idx = EP_BDT_IDX(log_endpoint, RX, 0);
bdt[idx].byte_count = max_packet;
if ((Data1 >> DESC_TO_PHY(endpoint)) & 1) {
bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
}
else {
bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
}
Data1 ^= (1 << DESC_TO_PHY(endpoint));
return true;
}
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
{
uint8_t log = DESC_TO_LOG(endpoint);
uint32_t bytes_read = 0;
endpoint_read_result_core(endpoint, read_buffers[log], read_sizes[log], &bytes_read);
read_buffers[log] = NULL;
read_sizes[log] = 0;
return bytes_read;
}
bool USBPhyHw::endpoint_read_result_core(usb_ep_t endpoint, uint8_t *data, uint32_t size, uint32_t *bytes_read)
{
uint32_t n, sz, idx, setup = 0;
uint8_t not_iso;
uint8_t * ep_buf;
uint32_t log_endpoint = DESC_TO_LOG(endpoint);
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
return false;
}
// if read on a IN endpoint -> error
if (DESC_EP_IN(endpoint)) {
return false;
}
idx = EP_BDT_IDX(log_endpoint, RX, 0);
sz = bdt[idx].byte_count;
not_iso = USB0->ENDPOINT[log_endpoint].ENDPT & USB_ENDPT_EPHSHK_MASK;
//for isochronous endpoint, we don't wait an interrupt
if ((log_endpoint != 0) && not_iso && !(epComplete & EP(DESC_TO_PHY(endpoint)))) {
return false;
}
if ((log_endpoint == 0) && (TOK_PID(idx) == SETUP_TOKEN)) {
setup = 1;
}
ep_buf = endpoint_buffer[idx];
for (n = 0; n < sz; n++) {
data[n] = ep_buf[n];
}
if (setup) {
// Record the setup type
if ((data[6] == 0) && (data[7] == 0)) {
ctrl_xfer = CTRL_XFER_NONE;
} else {
uint8_t in_xfer = (data[0] >> 7) & 1;
ctrl_xfer = in_xfer ? CTRL_XFER_IN : CTRL_XFER_OUT;
}
}
*bytes_read = sz;
epComplete &= ~EP(DESC_TO_PHY(endpoint));
return true;
}
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
uint32_t idx, n;
uint8_t * ep_buf;
if (DESC_TO_PHY(endpoint) > NUMBER_OF_PHYSICAL_ENDPOINTS - 1) {
return false;
}
// if write on a OUT endpoint -> error
if (DESC_EP_OUT(endpoint)) {
return false;
}
idx = EP_BDT_IDX(DESC_TO_LOG(endpoint), TX, 0);
bdt[idx].byte_count = size;
ep_buf = endpoint_buffer[idx];
for (n = 0; n < size; n++) {
ep_buf[n] = data[n];
}
if ((Data1 >> DESC_TO_PHY(endpoint)) & 1) {
bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK | BD_DATA01_MASK;
} else {
bdt[idx].info = BD_OWN_MASK | BD_DTS_MASK;
}
Data1 ^= (1 << DESC_TO_PHY(endpoint));
return true;
}
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
{
uint8_t dir = DESC_EP_IN(endpoint) ? TX : RX;
uint32_t idx = EP_BDT_IDX(DESC_TO_LOG(endpoint), dir, 0);
bdt[idx].info &= ~BD_OWN_MASK;
}
void USBPhyHw::process()
{
uint8_t i;
uint8_t istat = USB0->ISTAT & USB0->INTEN;
// reset interrupt
if (istat & USB_ISTAT_USBRST_MASK) {
// disable all endpt
for(i = 0; i < 16; i++) {
USB0->ENDPOINT[i].ENDPT = 0x00;
}
// enable control endpoint
setup_suspend = false;
endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
Data1 = 0x55555555;
USB0->CTL |= USB_CTL_ODDRST_MASK;
USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
USB0->ISTAT = 0xFF; // clear all interrupt status flags
USB0->ERRSTAT = 0xFF; // clear all error flags
USB0->ERREN = 0xFF; // enable error interrupt sources
USB0->ADDR = 0x00; // set default address
memset(read_buffers, 0, sizeof(read_buffers));
memset(read_sizes, 0, sizeof(read_sizes));
// reset bus for USBDevice layer
events->reset();
NVIC_ClearPendingIRQ(USB0_IRQn);
NVIC_EnableIRQ(USB0_IRQn);
return;
}
// resume interrupt
if (istat & USB_ISTAT_RESUME_MASK) {
USB0->ISTAT = USB_ISTAT_RESUME_MASK;
events->suspend(false);
}
// SOF interrupt
if (istat & USB_ISTAT_SOFTOK_MASK) {
USB0->ISTAT = USB_ISTAT_SOFTOK_MASK;
// SOF event, read frame number
events->sof(frameNumber());
}
// stall interrupt
if (istat & 1<<7) {
if (USB0->ENDPOINT[0].ENDPT & USB_ENDPT_EPSTALL_MASK)
USB0->ENDPOINT[0].ENDPT &= ~USB_ENDPT_EPSTALL_MASK;
USB0->ISTAT = USB_ISTAT_STALL_MASK;
}
// token interrupt
if (istat & USB_ISTAT_TOKDNE_MASK) {
uint32_t num = (USB0->STAT >> 4) & 0x0F;
uint32_t dir = (USB0->STAT >> 3) & 0x01;
uint32_t ev_odd = (USB0->STAT >> 2) & 0x01;
int phy_ep = (num << 1) | dir;
bool tx_en = (USB0->ENDPOINT[PHY_TO_LOG(phy_ep)].ENDPT & USB_ENDPT_EPTXEN_MASK) ? true : false;
bool rx_en = (USB0->ENDPOINT[PHY_TO_LOG(phy_ep)].ENDPT & USB_ENDPT_EPRXEN_MASK) ? true : false;
// setup packet
if (tx_en && (num == 0) && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == SETUP_TOKEN)) {
setup_suspend = true;
Data1 |= 0x02 | 0x01; // set DATA1 for TX and RX
bdt[EP_BDT_IDX(0, TX, EVEN)].info &= ~BD_OWN_MASK;
bdt[EP_BDT_IDX(0, TX, ODD)].info &= ~BD_OWN_MASK;
// EP0 SETUP event (SETUP data received)
events->ep0_setup();
} else {
// OUT packet
if (rx_en && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == OUT_TOKEN)) {
if (num == 0)
events->ep0_out();
else {
epComplete |= EP(phy_ep);
events->out(PHY_TO_DESC(phy_ep));
}
}
// IN packet
if (tx_en && (TOK_PID((EP_BDT_IDX(num, dir, ev_odd))) == IN_TOKEN)) {
if (num == 0) {
events->ep0_in();
if (set_addr == 1) {
USB0->ADDR = addr & 0x7F;
set_addr = 0;
}
}
else {
epComplete |= EP(phy_ep);
events->in(PHY_TO_DESC(phy_ep));
}
}
}
USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
}
// sleep interrupt
if (istat & 1<<4) {
USB0->ISTAT = USB_ISTAT_SLEEP_MASK;
events->suspend(true);
}
// error interrupt
if (istat & USB_ISTAT_ERROR_MASK) {
USB0->ERRSTAT = 0xFF;
USB0->ISTAT = USB_ISTAT_ERROR_MASK;
}
// Check if the suspend condition should be removed here
// 1. Don't attempt to clear USB_CTL_TXSUSPENDTOKENBUSY_MASK if it isn't set. This
// is to avoid potential race conditions.
// 2. If a setup packet is being processed then remove suspend on the next control transfer rather than here
// 3. Process all pending packets before removing suspend
bool suspended = (USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) != 0;
if (suspended && !setup_suspend && ((USB0->ISTAT & USB_ISTAT_TOKDNE_MASK) == 0)) {
USB0->CTL &= ~USB_CTL_TXSUSPENDTOKENBUSY_MASK;
}
NVIC_ClearPendingIRQ(USB0_IRQn);
NVIC_EnableIRQ(USB0_IRQn);
}
void USBPhyHw::_usbisr(void) {
NVIC_DisableIRQ(USB0_IRQn);
instance->events->start_process();
}
#endif

View File

@ -0,0 +1,97 @@
/* mbed Microcontroller Library
* 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.
*/
#define NUMBER_OF_LOGICAL_ENDPOINTS (16)
#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2)
/* Define physical endpoint numbers */
/* Endpoint No. Type(s) MaxPacket DoubleBuffer */
/* ---------------- ------------ ---------- --- */
#define EP0OUT (0x00) /* Control 64 No */
#define EP0IN (0x80) /* Control 64 No */
#define EP1OUT (0x01) /* Interrupt 64 No */
#define EP1IN (0x81) /* Interrupt 64 No */
#define EP2OUT (0x02) /* Bulk 64 Yes */
#define EP2IN (0x82) /* Bulk 64 Yes */
#define EP3OUT (0x03) /* Isochronous 1023 Yes */
#define EP3IN (0x83) /* Isochronous 1023 Yes */
#define EP4OUT (0x04) /* Interrupt 64 No */
#define EP4IN (0x84) /* Interrupt 64 No */
#define EP5OUT (0x05) /* Bulk 64 Yes */
#define EP5IN (0x85) /* Bulk 64 Yes */
#define EP6OUT (0x06) /* Isochronous 1023 Yes */
#define EP6IN (0x86) /* Isochronous 1023 Yes */
#define EP7OUT (0x07) /* Interrupt 64 No */
#define EP7IN (0x87) /* Interrupt 64 No */
#define EP8OUT (0x08) /* Bulk 64 Yes */
#define EP8IN (0x88) /* Bulk 64 Yes */
#define EP9OUT (0x09) /* Isochronous 1023 Yes */
#define EP9IN (0x89) /* Isochronous 1023 Yes */
#define EP10OUT (0x0A) /* Interrupt 64 No */
#define EP10IN (0x8A) /* Interrupt 64 No */
#define EP11OUT (0x0B) /* Bulk 64 Yes */
#define EP11IN (0x8B) /* Bulk 64 Yes */
#define EP12OUT (0x0C) /* Isochronous 1023 Yes */
#define EP12IN (0x8C) /* Isochronous 1023 Yes */
#define EP13OUT (0x0D) /* Interrupt 64 No */
#define EP13IN (0x8D) /* Interrupt 64 No */
#define EP14OUT (0x0E) /* Bulk 64 Yes */
#define EP14IN (0x8E) /* Bulk 64 Yes */
#define EP15OUT (0x0F) /* Bulk 64 Yes */
#define EP15IN (0x8F) /* Bulk 64 Yes */
/* Maximum Packet sizes */
#define MAX_PACKET_SIZE_EP0 (64)
#define MAX_PACKET_SIZE_EP1 (64)
#define MAX_PACKET_SIZE_EP2 (64)
#define MAX_PACKET_SIZE_EP3 (1023)
#define MAX_PACKET_SIZE_EP4 (64)
#define MAX_PACKET_SIZE_EP5 (64)
#define MAX_PACKET_SIZE_EP6 (1023)
#define MAX_PACKET_SIZE_EP7 (64)
#define MAX_PACKET_SIZE_EP8 (64)
#define MAX_PACKET_SIZE_EP9 (1023)
#define MAX_PACKET_SIZE_EP10 (64)
#define MAX_PACKET_SIZE_EP11 (64)
#define MAX_PACKET_SIZE_EP12 (1023)
#define MAX_PACKET_SIZE_EP13 (64)
#define MAX_PACKET_SIZE_EP14 (64)
#define MAX_PACKET_SIZE_EP15 (64)
/* Generic endpoints - intended to be portable accross devices */
/* and be suitable for simple USB devices. */
/* Bulk endpoints */
#define EPBULK_OUT (EP2OUT)
#define EPBULK_IN (EP2IN)
#define EPBULK_OUT_callback EP2_OUT_callback
#define EPBULK_IN_callback EP2_IN_callback
/* Interrupt endpoints */
#define EPINT_OUT (EP1OUT)
#define EPINT_IN (EP1IN)
#define EPINT_OUT_callback EP1_OUT_callback
#define EPINT_IN_callback EP1_IN_callback
/* Isochronous endpoints */
#define EPISO_OUT (EP3OUT)
#define EPISO_IN (EP3IN)
#define EPISO_OUT_callback EP3_OUT_callback
#define EPISO_IN_callback EP3_IN_callback
#define MAX_PACKET_SIZE_EPBULK (MAX_PACKET_SIZE_EP2)
#define MAX_PACKET_SIZE_EPINT (MAX_PACKET_SIZE_EP1)
#define MAX_PACKET_SIZE_EPISO (MAX_PACKET_SIZE_EP3)

View File

@ -0,0 +1,809 @@
/* mbed Microcontroller Library
* 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.
*/
#if defined(DEVICE_USBDEVICE) && DEVICE_USBDEVICE && \
(defined(TARGET_LPC1768) || defined(TARGET_LPC2368) || defined(TARGET_LPC2460))
#include "USBEndpoints_LPC17_LPC23.h"
#include "USBPhyHw.h"
#include "usb_phy_api.h"
// Get endpoint direction
#define IN_EP(endpoint) ((endpoint) & 0x80U ? true : false)
#define OUT_EP(endpoint) ((endpoint) & 0x80U ? false : true)
// Convert physical endpoint number to register bit
#define EP(endpoint) (1UL<<DESC_TO_PHY(endpoint))
// Check if this is an isochronous endpoint
#define ISO_EP(endpoint) ((((endpoint) & 0xF) == 3) || (((endpoint) & 0xF) == 6) || (((endpoint) & 0xF) == 9) || (((endpoint) & 0xF) == 12))
#define DESC_TO_PHY(endpoint) ((((endpoint)&0x0F)<<1) | (((endpoint) & 0x80) ? 1:0))
#define PHY_TO_DESC(endpoint) (((endpoint)>>1)|(((endpoint)&1)?0x80:0))
// Power Control for Peripherals register
#define PCUSB (1UL<<31)
// USB Clock Control register
#define DEV_CLK_EN (1UL<<1)
#define AHB_CLK_EN (1UL<<4)
// USB Clock Status register
#define DEV_CLK_ON (1UL<<1)
#define AHB_CLK_ON (1UL<<4)
// USB Device Interupt registers
#define FRAME (1UL<<0)
#define EP_FAST (1UL<<1)
#define EP_SLOW (1UL<<2)
#define DEV_STAT (1UL<<3)
#define CCEMPTY (1UL<<4)
#define CDFULL (1UL<<5)
#define RxENDPKT (1UL<<6)
#define TxENDPKT (1UL<<7)
#define EP_RLZED (1UL<<8)
#define ERR_INT (1UL<<9)
// USB Control register
#define RD_EN (1<<0)
#define WR_EN (1<<1)
#define LOG_ENDPOINT(endpoint) ((DESC_TO_PHY(endpoint)>>1)<<2)
// USB Receive Packet Length register
#define DV (1UL<<10)
#define PKT_RDY (1UL<<11)
#define PKT_LNGTH_MASK (0x3ff)
// Serial Interface Engine (SIE)
#define SIE_WRITE (0x01)
#define SIE_READ (0x02)
#define SIE_COMMAND (0x05)
#define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16))
// SIE Command codes
#define SIE_CMD_SET_ADDRESS (0xD0)
#define SIE_CMD_CONFIGURE_DEVICE (0xD8)
#define SIE_CMD_SET_MODE (0xF3)
#define SIE_CMD_READ_FRAME_NUMBER (0xF5)
#define SIE_CMD_READ_TEST_REGISTER (0xFD)
#define SIE_CMD_SET_DEVICE_STATUS (0xFE)
#define SIE_CMD_GET_DEVICE_STATUS (0xFE)
#define SIE_CMD_GET_ERROR_CODE (0xFF)
#define SIE_CMD_READ_ERROR_STATUS (0xFB)
#define SIE_CMD_SELECT_ENDPOINT(endpoint) (0x00+DESC_TO_PHY(endpoint))
#define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+DESC_TO_PHY(endpoint))
#define SIE_CMD_SET_ENDPOINT_STATUS(endpoint) (0x40+DESC_TO_PHY(endpoint))
#define SIE_CMD_CLEAR_BUFFER (0xF2)
#define SIE_CMD_VALIDATE_BUFFER (0xFA)
// SIE Device Status register
#define SIE_DS_CON (1<<0)
#define SIE_DS_CON_CH (1<<1)
#define SIE_DS_SUS (1<<2)
#define SIE_DS_SUS_CH (1<<3)
#define SIE_DS_RST (1<<4)
// SIE Device Set Address register
#define SIE_DSA_DEV_EN (1<<7)
// SIE Configue Device register
#define SIE_CONF_DEVICE (1<<0)
// Select Endpoint register
#define SIE_SE_FE (1<<0)
#define SIE_SE_ST (1<<1)
#define SIE_SE_STP (1<<2)
#define SIE_SE_PO (1<<3)
#define SIE_SE_EPN (1<<4)
#define SIE_SE_B_1_FULL (1<<5)
#define SIE_SE_B_2_FULL (1<<6)
// Set Endpoint Status command
#define SIE_SES_ST (1<<0)
#define SIE_SES_DA (1<<5)
#define SIE_SES_RF_MO (1<<6)
#define SIE_SES_CND_ST (1<<7)
static USBPhyHw *instance;
static uint32_t opStarted;
static const usb_ep_t ISO_EPS[] = {
0x03, 0x83,
0x06, 0x86,
0x09, 0x89,
0x0C, 0x8C
};
static void SIECommand(uint32_t command)
{
// The command phase of a SIE transaction
LPC_USB->USBDevIntClr = CCEMPTY;
LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command);
while (!(LPC_USB->USBDevIntSt & CCEMPTY));
}
static void SIEWriteData(uint8_t data)
{
// The data write phase of a SIE transaction
LPC_USB->USBDevIntClr = CCEMPTY;
LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data);
while (!(LPC_USB->USBDevIntSt & CCEMPTY));
}
static uint8_t SIEReadData(uint32_t command)
{
// The data read phase of a SIE transaction
LPC_USB->USBDevIntClr = CDFULL;
LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command);
while (!(LPC_USB->USBDevIntSt & CDFULL));
return (uint8_t)LPC_USB->USBCmdData;
}
static void SIEsetDeviceStatus(uint8_t status)
{
// Write SIE device status register
SIECommand(SIE_CMD_SET_DEVICE_STATUS);
SIEWriteData(status);
}
static uint8_t SIEgetDeviceStatus(void)
{
// Read SIE device status register
SIECommand(SIE_CMD_GET_DEVICE_STATUS);
return SIEReadData(SIE_CMD_GET_DEVICE_STATUS);
}
void SIEsetAddress(uint8_t address, bool enable=true)
{
// Write SIE device address register
SIECommand(SIE_CMD_SET_ADDRESS);
SIEWriteData((address & 0x7f) | (enable ? SIE_DSA_DEV_EN : 0));
}
static uint8_t SIEselectEndpoint(uint8_t endpoint)
{
// SIE select endpoint command
SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint));
return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint));
}
static uint8_t SIEclearBuffer(void)
{
// SIE clear buffer command
SIECommand(SIE_CMD_CLEAR_BUFFER);
return SIEReadData(SIE_CMD_CLEAR_BUFFER);
}
static void SIEvalidateBuffer(void)
{
// SIE validate buffer command
SIECommand(SIE_CMD_VALIDATE_BUFFER);
}
static void SIEsetEndpointStatus(uint8_t endpoint, uint8_t status)
{
// SIE set endpoint status command
SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint));
SIEWriteData(status);
}
static uint16_t SIEgetFrameNumber(void) __attribute__((unused));
static uint16_t SIEgetFrameNumber(void)
{
// Read current frame number
uint16_t lowByte;
uint16_t highByte;
SIECommand(SIE_CMD_READ_FRAME_NUMBER);
lowByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER);
highByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER);
return (highByte << 8) | lowByte;
}
static void SIEconfigureDevice(void)
{
// SIE Configure device command
SIECommand(SIE_CMD_CONFIGURE_DEVICE);
SIEWriteData(SIE_CONF_DEVICE);
}
static void SIEunconfigureDevice(void)
{
// SIE Configure device command
SIECommand(SIE_CMD_CONFIGURE_DEVICE);
SIEWriteData(0);
}
static void SIEconnect(void)
{
// Connect USB device
uint8_t status = SIEgetDeviceStatus();
SIEsetDeviceStatus(status | SIE_DS_CON);
}
static void SIEdisconnect(void)
{
// Disconnect USB device
uint8_t status = SIEgetDeviceStatus();
SIEsetDeviceStatus(status & ~SIE_DS_CON);
}
static uint8_t selectEndpointClearInterrupt(uint8_t endpoint)
{
// Implemented using using EP_INT_CLR.
LPC_USB->USBEpIntClr = EP(endpoint);
while (!(LPC_USB->USBDevIntSt & CDFULL));
return (uint8_t)LPC_USB->USBCmdData;
}
static void enableEndpointEvent(uint8_t endpoint)
{
// Route endpoint events to USBEpIntSt so they trigger an interrupt
LPC_USB->USBEpIntEn |= EP(endpoint);
}
// Do not use disableEndpointEvent. If an endpoint's event is disabled
// and a transfer occurs on that endpoint then that endpoint will enter
// a bad state. Future transfers on that endpoint will not trigger an
// interrupt even if the endpoint event is enabled again or the
// endpoint is reinitialized
/*
static void disableEndpointEvent(uint8_t endpoint) __attribute__((unused));
static void disableEndpointEvent(uint8_t endpoint)
{
// Don't set endpoint interrupt to pending in USBEpIntSt when an event occurs.
// Instead route them to USBDMARSt so they can be ignored.
LPC_USB->USBEpIntEn &= ~EP(endpoint);
}
*/
static uint32_t endpointReadcore(uint8_t endpoint, uint8_t *buffer, uint32_t size)
{
// Read from an OUT endpoint
uint32_t actual_size;
uint32_t i;
uint32_t data = 0;
uint8_t offset;
LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN;
while (!(LPC_USB->USBRxPLen & PKT_RDY));
actual_size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK;
offset = 0;
if (actual_size > 0) {
for (i = 0; i < actual_size; i++) {
if (offset == 0) {
// Fetch up to four bytes of data as a word
data = LPC_USB->USBRxData;
}
// extract a byte
if (size) {
*buffer = (data >> offset) & 0xff;
buffer++;
size--;
}
// move on to the next byte
offset = (offset + 8) % 32;
}
} else {
(void)LPC_USB->USBRxData;
}
LPC_USB->USBCtrl = 0;
return actual_size;
}
static void endpointWritecore(uint8_t endpoint, uint8_t *buffer, uint32_t size)
{
// Write to an IN endpoint
uint32_t temp, data;
uint8_t offset;
LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN;
LPC_USB->USBTxPLen = size;
offset = 0;
data = 0;
if (size > 0) {
do {
// Fetch next data byte into a word-sized temporary variable
temp = *buffer++;
// Add to current data word
temp = temp << offset;
data = data | temp;
// move on to the next byte
offset = (offset + 8) % 32;
size--;
if ((offset == 0) || (size == 0)) {
// Write the word to the endpoint
LPC_USB->USBTxData = data;
data = 0;
}
} while (size > 0);
} else {
LPC_USB->USBTxData = 0;
}
// Clear WR_EN to cover zero length packet case
LPC_USB->USBCtrl = 0;
SIEselectEndpoint(endpoint);
SIEvalidateBuffer();
}
USBPhy *get_usb_phy()
{
static USBPhyHw usbphy;
return &usbphy;
}
USBPhyHw::USBPhyHw(void)
{
}
USBPhyHw::~USBPhyHw(void)
{
}
void USBPhyHw::init(USBPhyEvents *events)
{
this->events = events;
// Disable IRQ
NVIC_DisableIRQ(USB_IRQn);
memset(read_buffers, 0, sizeof(read_buffers));
memset(read_sizes, 0, sizeof(read_sizes));
// Enable power to USB device controller
LPC_SC->PCONP |= PCUSB;
// Enable USB clocks
LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
// Configure pins P0.29 and P0.30 to be USB D+ and USB D-
LPC_PINCON->PINSEL1 &= 0xc3ffffff;
LPC_PINCON->PINSEL1 |= 0x14000000;
// Disconnect USB device
SIEdisconnect();
// Configure pin P2.9 to be Connect
LPC_PINCON->PINSEL4 &= 0xfffcffff;
LPC_PINCON->PINSEL4 |= 0x00040000;
// Connect must be low for at least 2.5uS
wait(0.3);
// Disable control endpoints
SIEsetEndpointStatus(EP0IN, SIE_SES_DA);
SIEsetEndpointStatus(EP0OUT, SIE_SES_DA);
// Set the maximum packet size for the control endpoints
endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
// Map interrupts to USBEpIntSt
enableEndpointEvent(EP0IN);
enableEndpointEvent(EP0OUT);
// Attach IRQ
instance = this;
NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
// Enable interrupts for device events and EP0
LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT | FRAME;
NVIC_EnableIRQ(USB_IRQn);
}
void USBPhyHw::deinit()
{
// Ensure device disconnected
SIEdisconnect();
// Disable USB interrupts
NVIC_DisableIRQ(USB_IRQn);
events = NULL;
opStarted = 0;
}
bool USBPhyHw::powered()
{
return true;
}
void USBPhyHw::connect(void)
{
// Enable control endpoints
SIEsetEndpointStatus(EP0IN, 0);
SIEsetEndpointStatus(EP0OUT, 0);
// Connect USB device
SIEconnect();
}
void USBPhyHw::disconnect(void)
{
// Disable control endpoints
SIEsetEndpointStatus(EP0IN, SIE_SES_DA);
SIEsetEndpointStatus(EP0OUT, SIE_SES_DA);
if (LPC_USB->USBEpIntSt & EP(EP0IN)) {
selectEndpointClearInterrupt(EP0IN);
}
if (LPC_USB->USBEpIntSt & EP(EP0OUT)) {
selectEndpointClearInterrupt(EP0OUT);
}
// Turn off USB nacking
SIEsetAddress(0, false);
// Disconnect USB device
SIEdisconnect();
// Reset all started operations
opStarted = 0;
}
void USBPhyHw::configure(void)
{
SIEconfigureDevice();
}
void USBPhyHw::unconfigure(void)
{
SIEunconfigureDevice();
}
void USBPhyHw::sof_enable()
{
//TODO
}
void USBPhyHw::sof_disable()
{
//TODO
}
void USBPhyHw::set_address(uint8_t address)
{
SIEsetAddress(address);
}
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
{
return MAX_PACKET_SIZE_EP0;
}
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
{
endpointReadcore(EP0OUT, buffer, size);
}
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
{
read_buffers[EP0OUT] = data;
read_sizes[EP0OUT] = size;
SIEselectEndpoint(EP0OUT);
SIEclearBuffer();
}
uint32_t USBPhyHw::ep0_read_result()
{
uint32_t size = endpointReadcore(EP0OUT, read_buffers[EP0OUT], read_sizes[EP0OUT]);
read_buffers[EP0OUT] = NULL;
read_sizes[EP0OUT] = 0;
return size;
}
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
{
endpointWritecore(EP0IN, buffer, size);
}
void USBPhyHw::ep0_stall(void)
{
// This will stall both control endpoints
endpoint_stall(EP0OUT);
}
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
opStarted |= EP(endpoint);
read_buffers[endpoint] = data;
read_sizes[endpoint] = size;
uint8_t status = SIEselectEndpoint(endpoint);
if (status & ((1 << 5) | (1 << 6))) {
// If any buffer has data then set the interrupt flag
LPC_USB->USBEpIntSet = EP(endpoint);
}
return true;
}
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
{
opStarted &= ~EP(endpoint);
uint32_t bytesRead = endpointReadcore(endpoint, read_buffers[endpoint], read_sizes[endpoint]);
read_buffers[endpoint] = NULL;
read_sizes[endpoint] = 0;
// Don't clear isochronous endpoints
if (!ISO_EP(endpoint)) {
SIEselectEndpoint(endpoint);
SIEclearBuffer();
}
return bytesRead;
}
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
opStarted |= EP(endpoint);
endpointWritecore(endpoint, data, size);
return true;
}
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
{
opStarted &= ~EP(endpoint);
// Clear out transfer buffers since the transfer has been aborted
if (OUT_EP(endpoint)) {
read_buffers[endpoint] = NULL;
read_sizes[endpoint] = 0;
}
}
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t maxPacket, usb_ep_type_t type)
{
// Realise an endpoint
LPC_USB->USBDevIntClr = EP_RLZED;
LPC_USB->USBReEp |= EP(endpoint);
LPC_USB->USBEpInd = DESC_TO_PHY(endpoint);
LPC_USB->USBMaxPSize = maxPacket;
while (!(LPC_USB->USBDevIntSt & EP_RLZED));
LPC_USB->USBDevIntClr = EP_RLZED;
// Map interrupts to USBEpIntSt
enableEndpointEvent(endpoint);
// Enable this endpoint
SIEsetEndpointStatus(endpoint, 0);
return true;
}
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
{
// Unrealise an endpoint
opStarted &= ~EP(endpoint);
// Disable this endpoint
SIEsetEndpointStatus(endpoint, SIE_SES_DA);
// Clear the given interrupt bit in USBEpIntSt if it is set
if (LPC_USB->USBEpIntSt & EP(endpoint)) {
selectEndpointClearInterrupt(endpoint);
}
LPC_USB->USBDevIntClr = EP_RLZED;
LPC_USB->USBReEp &= ~EP(endpoint);
while (!(LPC_USB->USBDevIntSt & EP_RLZED));
LPC_USB->USBDevIntClr = EP_RLZED;
}
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
{
// Stall an endpoint
if ((endpoint == EP0IN) || (endpoint == EP0OUT)) {
// Conditionally stall both control endpoints
SIEsetEndpointStatus(EP0OUT, SIE_SES_CND_ST);
} else {
SIEsetEndpointStatus(endpoint, SIE_SES_ST);
}
}
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
{
// Unstall an endpoint. The endpoint will also be reinitialised
SIEsetEndpointStatus(endpoint, 0);
}
void USBPhyHw::remote_wakeup(void)
{
// Remote wakeup
uint8_t status;
// Enable USB clocks
LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
status = SIEgetDeviceStatus();
SIEsetDeviceStatus(status & ~SIE_DS_SUS);
}
const usb_ep_table_t *USBPhyHw::endpoint_table()
{
static const usb_ep_table_t lpc_table = {
4096 - 32 * 4, // 32 words for endpoint buffers
// +3 based added to interrupt and isochronous to ensure enough
// space for 4 byte alignment
{
{USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}
}
};
return &lpc_table;
}
void USBPhyHw::_usbisr(void)
{
NVIC_DisableIRQ(USB_IRQn);
instance->events->start_process();
}
void USBPhyHw::process(void)
{
uint8_t devStat;
if (LPC_USB->USBDevIntSt & FRAME) {
// Start of frame event
events->sof(SIEgetFrameNumber());
// Clear interrupt status flag
LPC_USB->USBDevIntClr = FRAME;
// There is no ISO interrupt, instead a packet is transferred every SOF
for (uint32_t i = 0; i < sizeof(ISO_EPS) / sizeof(ISO_EPS[0]); i++) {
uint8_t endpoint = ISO_EPS[i];
if (opStarted & EP(endpoint)) {
opStarted &= ~EP(endpoint);
if (IN_EP(endpoint)) {
events->in(endpoint);
} else {
events->out(endpoint);
}
}
}
}
if (LPC_USB->USBDevIntSt & DEV_STAT) {
// Device Status interrupt
// Must clear the interrupt status flag before reading the device status from the SIE
LPC_USB->USBDevIntClr = DEV_STAT;
// Read device status from SIE
devStat = SIEgetDeviceStatus();
//printf("devStat: %d\r\n", devStat);
if (devStat & SIE_DS_SUS_CH) {
// Suspend status changed
if ((devStat & SIE_DS_SUS) != 0) {
events->suspend(false);
}
}
if (devStat & SIE_DS_RST) {
// Bus reset
if ((devStat & SIE_DS_SUS) == 0) {
events->suspend(true);
}
memset(read_buffers, 0, sizeof(read_buffers));
memset(read_sizes, 0, sizeof(read_sizes));
opStarted = 0;
events->reset();
}
}
if (LPC_USB->USBDevIntSt & EP_SLOW) {
// (Slow) Endpoint Interrupt
// Process IN packets before SETUP packets
// Note - order of OUT and SETUP does not matter as OUT packets
// are clobbered by SETUP packets and thus ignored.
//
// A SETUP packet can arrive at any time where as an IN packet is
// only sent after calling EP0write and an OUT packet after EP0read.
// The functions EP0write and EP0read are called only in response to
// a setup packet or IN/OUT packets sent in response to that
// setup packet. Therefore, if an IN or OUT packet is pending
// at the same time as a SETUP packet, the IN or OUT packet belongs
// to the previous control transfer and should either be processed
// before the SETUP packet (in the case of IN) or dropped (in the
// case of OUT as SETUP clobbers the OUT data).
if (LPC_USB->USBEpIntSt & EP(EP0IN)) {
selectEndpointClearInterrupt(EP0IN);
events->ep0_in();
}
// Process each endpoint interrupt
if (LPC_USB->USBEpIntSt & EP(EP0OUT)) {
if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP) {
// this is a setup packet
events->ep0_setup();
} else {
events->ep0_out();
}
}
for (uint8_t num = 2; num < 16 * 2; num++) {
uint8_t endpoint = PHY_TO_DESC(num);
if (LPC_USB->USBEpIntSt & EP(endpoint)) {
selectEndpointClearInterrupt(endpoint);
if (ISO_EP(endpoint)) {
// Processing for ISO endpoints done in FRAME handling
continue;
}
if (opStarted & EP(endpoint)) {
opStarted &= ~EP(endpoint);
if (IN_EP(endpoint)) {
events->in(endpoint);
} else {
events->out(endpoint);
}
}
}
}
LPC_USB->USBDevIntClr = EP_SLOW;
}
NVIC_ClearPendingIRQ(USB_IRQn);
NVIC_EnableIRQ(USB_IRQn);
}
#endif

View File

@ -0,0 +1,68 @@
/* mbed Microcontroller Library
* 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.
*/
#ifndef USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t *endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
private:
USBPhyEvents *events;
uint8_t *read_buffers[16];
uint16_t read_sizes[16];
static void _usbisr(void);
};
#endif

View File

@ -0,0 +1,84 @@
/* mbed Microcontroller Library
* 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.
*/
#define NUMBER_OF_LOGICAL_ENDPOINTS (16)
#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2)
/* Define physical endpoint numbers */
/* Endpoint No. Type(s) MaxSiz DoubleBuf pipe */
/* ---------------- --------- ------ --------- ---- */
#define EP0OUT (0x00) /* Control 256 No 0 */
#define EP0IN (0x80) /* Control 256 No 0 */
#define EP1OUT (0x01) /* Int 64 No 6 */
#define EP1IN (0x81) /* Int 64 No 7 */
#define EP2OUT (0x02) /* Bulk 2048 Yes 3 */
#define EP2IN (0x82) /* Bulk 2048 Yes 4 */
#define EP3OUT (0x03) /* Bulk/Iso 2048 Yes 1 */
#define EP3IN (0x83) /* Bulk/Iso 2048 Yes 2 */
/*following EP is not configured in sample program*/
#define EP6IN (0x86) /* Bulk 2048 Yes 5 */
#define EP8IN (0x88) /* Int 64 No 8 */
#define EP9IN (0x89) /* Bulk 512 Bulk 9 */
#define EP10IN (0x8A) /* Int/Bulk 2048 Bulk 10 */
#define EP11IN (0x8B) /* Bulk 2048 Yes 11 */
#define EP12IN (0x8C) /* Bulk 2048 Yes 12 */
#define EP13IN (0x8D) /* Bulk 2048 Yes 13 */
#define EP14IN (0x8E) /* Bulk 2048 Yes 14 */
#define EP15IN (0x8F) /* Bulk 2048 Yes 15 */
/* Maximum Packet sizes */
#define MAX_PACKET_SIZE_EP0 (64) /*pipe0/pipe0: control */
#define MAX_PACKET_SIZE_EP1 (64) /*pipe6/pipe7: interrupt */
#define MAX_PACKET_SIZE_EP2 (512) /*pipe3/pipe4: bulk */
#define MAX_PACKET_SIZE_EP3 (512) /*pipe1/pipe2: isochronous */
#define MAX_PACKET_SIZE_EP6 (64) /*pipe5: Note *1 */
#define MAX_PACKET_SIZE_EP8 (64) /*pipe7: Note *1 */
#define MAX_PACKET_SIZE_EP9 (512) /*pipe8: Note *1 */
#define MAX_PACKET_SIZE_EP10 (512) /*pipe9: Note *1 */
#define MAX_PACKET_SIZE_EP11 (512) /*pipe10: Note *1 */
#define MAX_PACKET_SIZE_EP12 (512) /*pipe11: Note *1 */
#define MAX_PACKET_SIZE_EP13 (512) /*pipe12: Note *1 */
#define MAX_PACKET_SIZE_EP14 (512) /*pipe13: Note *1 */
#define MAX_PACKET_SIZE_EP15 (512) /*pipe14: Note *1 */
/* Note *1: This pipe is not configure in sample program */
/* Generic endpoints - intended to be portable accross devices */
/* and be suitable for simple USB devices. */
/* Bulk endpoints */
#define EPBULK_OUT (EP2OUT)
#define EPBULK_IN (EP2IN)
#define EPBULK_OUT_callback EP2_OUT_callback
#define EPBULK_IN_callback EP2_IN_callback
/* Interrupt endpoints */
#define EPINT_OUT (EP1OUT)
#define EPINT_IN (EP1IN)
#define EPINT_OUT_callback EP1_OUT_callback
#define EPINT_IN_callback EP1_IN_callback
/* Isochronous endpoints */
#define EPISO_OUT (EP3OUT)
#define EPISO_IN (EP3IN)
#define EPISO_OUT_callback EP3_OUT_callback
#define EPISO_IN_callback EP3_IN_callback
#define MAX_PACKET_SIZE_EPBULK (MAX_PACKET_SIZE_EP2)
#define MAX_PACKET_SIZE_EPINT (MAX_PACKET_SIZE_EP1)
#define MAX_PACKET_SIZE_EPISO (MAX_PACKET_SIZE_EP3)
/*EOF*/

View File

@ -0,0 +1,106 @@
/* mbed Microcontroller Library
* 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 USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t* endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
private:
#define PIPE_NUM (16)
typedef struct {
bool enable;
uint16_t status;
uint32_t req_size;
uint32_t data_cnt;
uint8_t *p_data;
} pipe_ctrl_t;
USBPhyEvents *events;
pipe_ctrl_t pipe_ctrl[PIPE_NUM];
uint16_t setup_buffer[32];
static void _usbisr(void);
void chg_curpipe(uint16_t pipe, uint16_t isel);
uint16_t is_set_frdy(uint16_t pipe, uint16_t isel);
uint8_t * read_fifo(uint16_t pipe, uint16_t count, uint8_t *read_p);
uint16_t read_data(uint16_t pipe);
void fifo_to_buf(uint16_t pipe);
uint8_t * write_fifo(uint16_t pipe, uint16_t count, uint8_t *write_p);
uint16_t write_data(uint16_t pipe);
void buf_to_fifo(uint16_t pipe);
uint16_t * get_pipectr_reg(uint16_t pipe);
uint16_t * get_pipetre_reg(uint16_t pipe);
uint16_t * get_pipetrn_reg(uint16_t pipe);
uint16_t * get_fifoctr_reg(uint16_t pipe);
uint16_t * get_fifosel_reg(uint16_t pipe);
uint32_t * get_fifo_reg(uint16_t pipe);
uint16_t get_pid(uint16_t pipe);
void set_mbw(uint16_t pipe, uint16_t data);
void set_pid(uint16_t pipe, uint16_t new_pid);
void cpu_delay_1us(uint16_t time);
uint16_t EP2PIPE(uint16_t endpoint);
uint16_t PIPE2EP(uint16_t pipe);
uint16_t PIPE2FIFO(uint16_t pipe);
void reset_usb(uint16_t clockmode);
bool chk_vbsts(void);
void ctrl_end(uint16_t status);
void data_end(uint16_t pipe, uint16_t status);
void forced_termination(uint16_t pipe, uint16_t status);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,697 @@
/* mbed Microcontroller Library
* 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 USBPHY_RZ_A1_DEF
#define USBPHY_RZ_A1_DEF
/******************************************************************************
Macro definitions
******************************************************************************/
/* H/W function type */
#define USB_BIT0 ((uint16_t)0x0001)
#define USB_BIT1 ((uint16_t)0x0002)
#define USB_BIT2 ((uint16_t)0x0004)
#define USB_BIT3 ((uint16_t)0x0008)
#define USB_BIT4 ((uint16_t)0x0010)
#define USB_BIT5 ((uint16_t)0x0020)
#define USB_BIT6 ((uint16_t)0x0040)
#define USB_BIT7 ((uint16_t)0x0080)
#define USB_BIT8 ((uint16_t)0x0100)
#define USB_BIT9 ((uint16_t)0x0200)
#define USB_BIT10 ((uint16_t)0x0400)
#define USB_BIT11 ((uint16_t)0x0800)
#define USB_BIT12 ((uint16_t)0x1000)
#define USB_BIT13 ((uint16_t)0x2000)
#define USB_BIT14 ((uint16_t)0x4000)
#define USB_BIT15 ((uint16_t)0x8000)
#define USB_BITSET(x) ((uint16_t)((uint16_t)1 << (x)))
/* Start Pipe No */
#define USB_MIN_PIPE_NO (1u)
/* Pipe configuration table define */
#define USB_EPL (6u) /* Pipe configuration table length */
#define USB_TYPFIELD (0xC000u) /* Transfer type */
#define USB_PERIODIC (0x8000u) /* Periodic pipe */
#define USB_TYPFIELD_ISO (0xC000u) /* Isochronous */
#define USB_TYPFIELD_INT (0x8000u) /* Interrupt */
#define USB_TYPFIELD_BULK (0x4000u) /* Bulk */
#define USB_NOUSE (0x0000u) /* Not configuration */
#define USB_BFREFIELD (0x0400u) /* Buffer ready interrupt mode select */
#define USB_BFREON (0x0400u)
#define USB_BFREOFF (0x0000u)
#define USB_DBLBFIELD (0x0200u) /* Double buffer mode select */
#define USB_CFG_DBLBON (0x0200u)
#define USB_CFG_DBLBOFF (0x0000u)
#define USB_CNTMDFIELD (0x0100u) /* Continuous transfer mode select */
#define USB_CFG_CNTMDON (0x0100u)
#define USB_CFG_CNTMDOFF (0x0000u)
#define USB_SHTNAKFIELD (0x0080u) /* Transfer end NAK */
#define USB_DIRFIELD (0x0010u) /* Transfer direction select */
#define USB_DIR_H_OUT (0x0010u) /* HOST OUT */
#define USB_DIR_P_IN (0x0010u) /* PERI IN */
#define USB_DIR_H_IN (0x0000u) /* HOST IN */
#define USB_DIR_P_OUT (0x0000u) /* PERI OUT */
#define USB_BUF2FIFO (0x0010u) /* Buffer --> FIFO */
#define USB_FIFO2BUF (0x0000u) /* FIFO --> buffer */
#define USB_EPNUMFIELD (0x000Fu) /* Endpoint number select */
#define USB_MAX_EP_NO (15u) /* EP0 EP1 ... EP15 */
#define USB_BUF_SIZE(x) ((uint16_t)(((x) / 64u) - 1u) << 10u)
#define USB_BUF_NUMB(x) (x)
/* FIFO read / write result */
#define USB_FIFOERROR (0x00ffu) /* FIFO not ready */
#define USB_WRITEEND (0x0000u) /* End of write (but packet may not be outputting) */
#define USB_WRITESHRT (0x0001u) /* End of write (send short packet) */
#define USB_WRITING (0x0002u) /* Write continues */
#define USB_READEND (0x0000u) /* End of read */
#define USB_READSHRT (0x0001u) /* Insufficient (receive short packet) */
#define USB_READING (0x0002u) /* Read continues */
#define USB_READOVER (0x0003u) /* Buffer size over */
/* Transfer status Type */
#define USB_CTRL_END (0u)
#define USB_DATA_NONE (1u)
#define USB_DATA_WAIT (2u)
#define USB_DATA_OK (3u)
#define USB_DATA_SHT (4u)
#define USB_DATA_OVR (5u)
#define USB_DATA_STALL (6u)
#define USB_DATA_ERR (7u)
#define USB_DATA_STOP (8u)
#define USB_DATA_TMO (9u)
#define USB_CTRL_READING (17u)
#define USB_CTRL_WRITING (18u)
#define USB_DATA_READING (19u)
#define USB_DATA_WRITING (20u)
/* System Configuration Control Register */
#define USB_HSE (0x0080u) /* b7: Hi-speed enable */
#define USB_DCFM (0x0040u) /* b6: Function select */
#define USB_DRPD (0x0020u) /* b5: D+/D- pull down control */
#define USB_DPRPU (0x0010u) /* b4: D+ pull up control */
#define USB_DMRPU (0x0008u) /* b3: D- pull up control */ /* For low speed */
#define USB_UCKSEL (0x0004u) /* b2: USB clock select */
#define USB_EXTAL_12MHZ (0x0004u) /* EXTAL 12MHz */
#define USB_X1_48MHZ (0x0000u) /* USB_X1 48MHz */
#define USB_UPLLE (0x0002u) /* b1: USB internal PLL enable */
#define USB_USBE (0x0001u) /* b0: USB module enable */
/* CPU Bus Wait Register */
#define USB_BWAIT (0x003Fu) /* b5-0: Bus wait bit */
#define USB_BWAIT_15 (0x000Fu) /* 15 wait (access cycle 17) */
#define USB_BWAIT_14 (0x000Eu) /* 14 wait (access cycle 16) */
#define USB_BWAIT_13 (0x000Du) /* 13 wait (access cycle 15) */
#define USB_BWAIT_12 (0x000Cu) /* 12 wait (access cycle 14) */
#define USB_BWAIT_11 (0x000Bu) /* 11 wait (access cycle 13) */
#define USB_BWAIT_10 (0x000Au) /* 10 wait (access cycle 12) */
#define USB_BWAIT_9 (0x0009u) /* 9 wait (access cycle 11) */
#define USB_BWAIT_8 (0x0008u) /* 8 wait (access cycle 10) */
#define USB_BWAIT_7 (0x0007u) /* 7 wait (access cycle 9) */
#define USB_BWAIT_6 (0x0006u) /* 6 wait (access cycle 8) */
#define USB_BWAIT_5 (0x0005u) /* 5 wait (access cycle 7) */
#define USB_BWAIT_4 (0x0004u) /* 4 wait (access cycle 6) */
#define USB_BWAIT_3 (0x0003u) /* 3 wait (access cycle 5) */
#define USB_BWAIT_2 (0x0002u) /* 2 wait (access cycle 4) */
#define USB_BWAIT_1 (0x0001u) /* 1 wait (access cycle 3) */
#define USB_BWAIT_0 (0x0000u) /* 0 wait (access cycle 2) */
/* System Configuration Status Register */
#define USB_OVCMON (0xC000u) /* b15-14: Over-current monitor */
#define USB_OVCBIT (0x8000u) /* b15-14: Over-current bit */
#define USB_HTACT (0x0040u) /* b6: USB Host Sequencer Status Monitor */
#define USB_SOFEA (0x0020u) /* b5: SOF monitor */
#define USB_IDMON (0x0004u) /* b2: ID-pin monitor */
#define USB_LNST (0x0003u) /* b1-0: D+, D- line status */
#define USB_SE1 (0x0003u) /* SE1 */
#define USB_FS_KSTS (0x0002u) /* Full-Speed K State */
#define USB_FS_JSTS (0x0001u) /* Full-Speed J State */
#define USB_LS_JSTS (0x0002u) /* Low-Speed J State */
#define USB_LS_KSTS (0x0001u) /* Low-Speed K State */
#define USB_SE0 (0x0000u) /* SE0 */
/* PLL Status Register */
#define USB_PLLLOCK (0x0001u)
/* Device State Control Register */
#define USB_HNPBTOA (0x0800u) /* b11: Host negotiation protocol (BtoA) */
#define USB_EXICEN (0x0400u) /* b10: EXICEN output terminal control */
#define USB_VBUSEN (0x0200u) /* b9: VBUS output terminal control */
#define USB_WKUP (0x0100u) /* b8: Remote wakeup */
#define USB_RWUPE (0x0080u) /* b7: Remote wakeup sense */
#define USB_USBRST (0x0040u) /* b6: USB reset enable */
#define USB_RESUME (0x0020u) /* b5: Resume enable */
#define USB_UACT (0x0010u) /* b4: USB bus enable */
#define USB_RHST (0x0007u) /* b2-0: Reset handshake status */
#define USB_HSPROC (0x0004u) /* HS handshake processing */
#define USB_HSMODE (0x0003u) /* Hi-Speed mode */
#define USB_FSMODE (0x0002u) /* Full-Speed mode */
#define USB_LSMODE (0x0001u) /* Low-Speed mode */
#define USB_UNDECID (0x0000u) /* Undecided */
/* Test Mode Register */
#define USB_UTST (0x000Fu) /* b3-0: Test mode */
#define USB_H_TST_F_EN (0x000Du) /* HOST TEST FORCE ENABLE */
#define USB_H_TST_PACKET (0x000Cu) /* HOST TEST Packet */
#define USB_H_TST_SE0_NAK (0x000Bu) /* HOST TEST SE0 NAK */
#define USB_H_TST_K (0x000Au) /* HOST TEST K */
#define USB_H_TST_J (0x0009u) /* HOST TEST J */
#define USB_H_TST_NORMAL (0x0000u) /* HOST Normal Mode */
#define USB_P_TST_PACKET (0x0004u) /* PERI TEST Packet */
#define USB_P_TST_SE0_NAK (0x0003u) /* PERI TEST SE0 NAK */
#define USB_P_TST_K (0x0002u) /* PERI TEST K */
#define USB_P_TST_J (0x0001u) /* PERI TEST J */
#define USB_P_TST_NORMAL (0x0000u) /* PERI Normal Mode */
/* CFIFO/DxFIFO Port Select Register */
#define USB_RCNT (0x8000u) /* b15: Read count mode */
#define USB_REW (0x4000u) /* b14: Buffer rewind */
#define USB_DCLRM (0x2000u) /* b13: Automatic buffer clear mode */
#define USB_DREQE (0x1000u) /* b12: DREQ output enable */
#define USB_MBW (0x0C00u) /* b10: Maximum bit width for FIFO access */
#define USB_MBW_32 (0x0800u) /* FIFO access : 32bit */
#define USB_MBW_16 (0x0400u) /* FIFO access : 16bit */
#define USB_MBW_8 (0x0000u) /* FIFO access : 8bit */
#define USB_BIGEND (0x0100u) /* b8: Big endian mode */
#define USB_FIFO_BIG (0x0100u) /* Big endian */
#define USB_FIFO_LITTLE (0x0000u) /* Little endian */
#define USB_ISEL (0x0020u) /* b5: DCP FIFO port direction select */
#define USB_ISEL_WRITE (0x0020u) /* write */
#define USB_ISEL_READ (0x0000u) /* read */
#define USB_CURPIPE (0x000Fu) /* b2-0: PIPE select */
/* CFIFO/DxFIFO Port Control Register */
#define USB_BVAL (0x8000u) /* b15: Buffer valid flag */
#define USB_BCLR (0x4000u) /* b14: Buffer clear */
#define USB_FRDY (0x2000u) /* b13: FIFO ready */
#define USB_DTLN (0x0FFFu) /* b11-0: FIFO data length */
/* Interrupt Enable Register 0 */
#define USB_VBSE (0x8000u) /* b15: VBUS interrupt */
#define USB_RSME (0x4000u) /* b14: Resume interrupt */
#define USB_SOFE (0x2000u) /* b13: Frame update interrupt */
#define USB_DVSE (0x1000u) /* b12: Device state transition interrupt */
#define USB_CTRE (0x0800u) /* b11: Control transfer stage transition interrupt */
#define USB_BEMPE (0x0400u) /* b10: Buffer empty interrupt */
#define USB_NRDYE (0x0200u) /* b9: Buffer notready interrupt */
#define USB_BRDYE (0x0100u) /* b8: Buffer ready interrupt */
/* Interrupt Enable Register 1 */
#define USB_OVRCRE (0x8000u) /* b15: Over-current interrupt */
#define USB_BCHGE (0x4000u) /* b14: USB bus change interrupt */
#define USB_DTCHE (0x1000u) /* b12: Detach sense interrupt */
#define USB_ATTCHE (0x0800u) /* b11: Attach sense interrupt */
#define USB_L1RSMENDE (0x0200u) /* b9: L1 resume completion interrupt */
#define USB_LPMENDE (0x0100u) /* b8: LPM transaction completion interrupt */
#define USB_EOFERRE (0x0040u) /* b6: EOF error interrupt */
#define USB_SIGNE (0x0020u) /* b5: SETUP IGNORE interrupt */
#define USB_SACKE (0x0010u) /* b4: SETUP ACK interrupt */
#define USB_PDDETINTE (0x0001u) /* b0: PDDET detection interrupt */
/* BRDY Interrupt Enable/Status Register */
#define USB_BRDY9 (0x0200u) /* b9: PIPE9 */
#define USB_BRDY8 (0x0100u) /* b8: PIPE8 */
#define USB_BRDY7 (0x0080u) /* b7: PIPE7 */
#define USB_BRDY6 (0x0040u) /* b6: PIPE6 */
#define USB_BRDY5 (0x0020u) /* b5: PIPE5 */
#define USB_BRDY4 (0x0010u) /* b4: PIPE4 */
#define USB_BRDY3 (0x0008u) /* b3: PIPE3 */
#define USB_BRDY2 (0x0004u) /* b2: PIPE2 */
#define USB_BRDY1 (0x0002u) /* b1: PIPE1 */
#define USB_BRDY0 (0x0001u) /* b1: PIPE0 */
/* NRDY Interrupt Enable/Status Register */
#define USB_NRDY9 (0x0200u) /* b9: PIPE9 */
#define USB_NRDY8 (0x0100u) /* b8: PIPE8 */
#define USB_NRDY7 (0x0080u) /* b7: PIPE7 */
#define USB_NRDY6 (0x0040u) /* b6: PIPE6 */
#define USB_NRDY5 (0x0020u) /* b5: PIPE5 */
#define USB_NRDY4 (0x0010u) /* b4: PIPE4 */
#define USB_NRDY3 (0x0008u) /* b3: PIPE3 */
#define USB_NRDY2 (0x0004u) /* b2: PIPE2 */
#define USB_NRDY1 (0x0002u) /* b1: PIPE1 */
#define USB_NRDY0 (0x0001u) /* b1: PIPE0 */
/* BEMP Interrupt Enable/Status Register */
#define USB_BEMP9 (0x0200u) /* b9: PIPE9 */
#define USB_BEMP8 (0x0100u) /* b8: PIPE8 */
#define USB_BEMP7 (0x0080u) /* b7: PIPE7 */
#define USB_BEMP6 (0x0040u) /* b6: PIPE6 */
#define USB_BEMP5 (0x0020u) /* b5: PIPE5 */
#define USB_BEMP4 (0x0010u) /* b4: PIPE4 */
#define USB_BEMP3 (0x0008u) /* b3: PIPE3 */
#define USB_BEMP2 (0x0004u) /* b2: PIPE2 */
#define USB_BEMP1 (0x0002u) /* b1: PIPE1 */
#define USB_BEMP0 (0x0001u) /* b0: PIPE0 */
/* SOF Pin Configuration Register */
#define USB_TRNENSEL (0x0100u) /* b8: Select transaction enable period */
#define USB_BRDYM (0x0040u) /* b6: BRDY clear timing */
#define USB_INTL (0x0020u) /* b5: Interrupt sense select */
#define USB_EDGESTS (0x0010u) /* b4: */
#define USB_SOFMODE (0x000Cu) /* b3-2: SOF pin select */
#define USB_SOF_125US (0x0008u) /* SOF 125us Frame Signal */
#define USB_SOF_1MS (0x0004u) /* SOF 1ms Frame Signal */
#define USB_SOF_DISABLE (0x0000u) /* SOF Disable */
#define USB_HSEB (0x8000u) /* b15: CL only mode bit */
#define USB_REPSTART (0x0800u) /* b11: Terminator adjustment forcible starting bit */
#define USB_REPSEL (0x0300u) /* b9-8: Terminator adjustment cycle setting */
#define USB_REPSEL_128 (0x0300u) /* 128 sec */
#define USB_REPSEL_64 (0x0200u) /* 64 sec */
#define USB_REPSEL_16 (0x0100u) /* 16 sec */
#define USB_REPSEL_NONE (0x0000u) /* - */
#define USB_CLKSEL (0x0030u) /* b5-4: System clock setting */
#define USB_CLKSEL_24 (0x0030u) /* 24MHz */
#define USB_CLKSEL_20 (0x0020u) /* 20MHz */
#define USB_CLKSEL_48 (0x0010u) /* 48MHz */
#define USB_CLKSEL_30 (0x0000u) /* 30MHz */
#define USB_CDPEN (0x0008u) /* b3: Charging downstream port enable */
#define USB_PLLRESET (0x0002u) /* b1: PLL reset control */
#define USB_DIRPD (0x0001u) /* b0: Power down control */
/* Interrupt Status Register 0 */
#define USB_VBINT (0x8000u) /* b15: VBUS interrupt */
#define USB_RESM (0x4000u) /* b14: Resume interrupt */
#define USB_SOFR (0x2000u) /* b13: SOF update interrupt */
#define USB_DVST (0x1000u) /* b12: Device state transition interrupt */
#define USB_CTRT (0x0800u) /* b11: Control transfer stage transition interrupt */
#define USB_BEMP (0x0400u) /* b10: Buffer empty interrupt */
#define USB_NRDY (0x0200u) /* b9: Buffer notready interrupt */
#define USB_BRDY (0x0100u) /* b8: Buffer ready interrupt */
#define USB_VBSTS (0x0080u) /* b7: VBUS input port */
#define USB_DVSQ (0x0070u) /* b6-4: Device state */
#define USB_DS_SPD_CNFG (0x0070u) /* Suspend Configured */
#define USB_DS_SPD_ADDR (0x0060u) /* Suspend Address */
#define USB_DS_SPD_DFLT (0x0050u) /* Suspend Default */
#define USB_DS_SPD_POWR (0x0040u) /* Suspend Powered */
#define USB_DS_SUSP (0x0040u) /* Suspend */
#define USB_DS_CNFG (0x0030u) /* Configured */
#define USB_DS_ADDS (0x0020u) /* Address */
#define USB_DS_DFLT (0x0010u) /* Default */
#define USB_DS_POWR (0x0000u) /* Powered */
#define USB_DVSQS (0x0030u) /* b5-4: Device state */
#define USB_VALID (0x0008u) /* b3: Setup packet detect flag */
#define USB_CTSQ (0x0007u) /* b2-0: Control transfer stage */
#define USB_CS_SQER (0x0006u) /* Sequence error */
#define USB_CS_WRND (0x0005u) /* Ctrl write nodata status stage */
#define USB_CS_WRSS (0x0004u) /* Ctrl write status stage */
#define USB_CS_WRDS (0x0003u) /* Ctrl write data stage */
#define USB_CS_RDSS (0x0002u) /* Ctrl read status stage */
#define USB_CS_RDDS (0x0001u) /* Ctrl read data stage */
#define USB_CS_IDST (0x0000u) /* Idle or setup stage */
/* Interrupt Status Register 1 */
#define USB_OVRCR (0x8000u) /* b15: Over-current interrupt */
#define USB_BCHG (0x4000u) /* b14: USB bus change interrupt */
#define USB_DTCH (0x1000u) /* b12: Detach sense interrupt */
#define USB_ATTCH (0x0800u) /* b11: Attach sense interrupt */
#define USB_L1RSMEND (0x0200u) /* b9: L1 resume completion interrupt */
#define USB_LPMEND (0x0100u) /* b8: LPM transaction completion interrupt */
#define USB_EOFERR (0x0040u) /* b6: EOF-error interrupt */
#define USB_SIGN (0x0020u) /* b5: Setup ignore interrupt */
#define USB_SACK (0x0010u) /* b4: Setup ack interrupt */
#define USB_PDDETINT (0x0001u) /* b0: PDDET detection interrupt */
/* Frame Number Register */
#define USB_OVRN (0x8000u) /* b15: Overrun error */
#define USB_CRCE (0x4000u) /* b14: Received data error */
#define USB_FRNM (0x07FFu) /* b10-0: Frame number */
/* Device State Change Register */ /* For USB0 */
#define USB_DVCHG (0x8000u) /* b15: Device state change */
/* Micro Frame Number Register */ /* For USBHS */
#define USB_UFRNM (0x0007u) /* b2-0: Micro frame number */
/* USB Address / Low Power Status Recovery Register */
#define USB_STSRECOV (0x0F00u) /* b11-8: Status Recovery */
#define USB_USBADDR_MASK (0x007Fu) /* b6-0: USB address */
/* USB Request Type Register */
#define USB_BMREQUESTTYPE (0x00FFu) /* b7-0: USB_BMREQUESTTYPE */
#define USB_BMREQUESTTYPEDIR (0x0080u) /* b7 : Data transfer direction */
#define USB_BMREQUESTTYPETYPE (0x0060u) /* b6-5: Type */
#define USB_BMREQUESTTYPERECIP (0x001Fu) /* b4-0: Recipient */
/* USB Request Value Register */
#define USB_WVALUE (0xFFFFu) /* b15-0: wValue */
#define USB_DT_TYPE (0xFF00u)
#define USB_GET_DT_TYPE(v) (((v) & USB_DT_TYPE) >> 8)
#define USB_DT_INDEX (0x00FFu)
#define USB_CONF_NUM (0x00FFu)
#define USB_ALT_SET (0x00FFu)
/* USB Request Index Register */
#define USB_WINDEX (0xFFFFu) /* b15-0: wIndex */
#define USB_TEST_SELECT (0xFF00u) /* b15-b8: Test Mode Selectors */
#define USB_TEST_J (0x0100u) /* Test_J */
#define USB_TEST_K (0x0200u) /* Test_K */
#define USB_TEST_SE0_NAK (0x0300u) /* Test_SE0_NAK */
#define USB_TEST_PACKET (0x0400u) /* Test_Packet */
#define USB_TEST_FORCE_ENABLE (0x0500u) /* Test_Force_Enable */
#define USB_TEST_STSelectors (0x0600u) /* Standard test selectors */
#define USB_TEST_RESERVED (0x4000u) /* Reserved */
#define USB_TEST_VSTMODES (0xC000u) /* VendorSpecific test modes */
#define USB_EP_DIR (0x0080u) /* b7: Endpoint Direction */
#define USB_EP_DIR_IN (0x0080u)
#define USB_EP_DIR_OUT (0x0000u)
/* USB Request Length Register */
#define USB_WLENGTH (0xFFFFu) /* b15-0: wLength */
#define USB_TYPE (0xC000u) /* b15-14: Transfer type */
#define USB_BFRE (0x0400u) /* b10: Buffer ready interrupt mode select */
#define USB_DEVSEL (0xF000u) /* b15-14: Device address select */
#define USB_MAXP (0x007Fu) /* b6-0: Maxpacket size of default control pipe */
#define USB_MXPS (0x07FFu) /* b10-0: Maxpacket size */
#define USB_BSTS (0x8000u) /* b15: Buffer status */
#define USB_SUREQ (0x4000u) /* b14: Send USB request */
#define USB_INBUFM (0x4000u) /* b14: IN buffer monitor (Only for PIPE1 to 5) */
#define USB_CSCLR (0x2000u) /* b13: c-split status clear */
#define USB_CSSTS (0x1000u) /* b12: c-split status */
#define USB_SUREQCLR (0x0800u) /* b11: stop setup request */
#define USB_ATREPM (0x0400u) /* b10: Auto repeat mode */
#define USB_ACLRM (0x0200u) /* b9: buffer auto clear mode */
#define USB_SQCLR (0x0100u) /* b8: Sequence bit clear */
#define USB_SQSET (0x0080u) /* b7: Sequence bit set */
#define USB_SQMON (0x0040u) /* b6: Sequence bit monitor */
#define USB_PBUSY (0x0020u) /* b5: pipe busy */
#define USB_PINGE (0x0010u) /* b4: ping enable */
#define USB_CCPL (0x0004u) /* b2: Enable control transfer complete */
#define USB_PID (0x0003u) /* b1-0: Response PID */
#define USB_PID_STALL2 (0x0003u) /* STALL2 */
#define USB_PID_STALL (0x0002u) /* STALL */
#define USB_PID_BUF (0x0001u) /* BUF */
#define USB_PID_NAK (0x0000u) /* NAK */
#define USB_PIPENM (0x0007u) /* b2-0: Pipe select */
#define USB_BUFSIZE (0x7C00u) /* b14-10: Pipe buffer size */
#define USB_BUFNMB (0x007Fu) /* b6-0: Pipe buffer number */
#define USB_PIPE0BUF (256u)
#define USB_PIPEXBUF (64u)
#define USB_TRENB (0x0200u) /* b9: Transaction count enable */
#define USB_TRCLR (0x0100u) /* b8: Transaction count clear */
#define USB_TRNCNT (0xFFFFu) /* b15-0: Transaction counter */
#define USB_UPPHUB (0x7800u) /* b14-11: HUB register */
#define USB_HUBPORT (0x0700u) /* b10-8: HUB port */
#define USB_USBSPD (0x00C0u) /* b7-6: Device speed */
/********* USB0 Only ******************************************************************************/
/* PHY Crosspoint Adjustment Register */
/* PHYSLEW */
#define USB_SLEWF01 (0x0008u) /* b3: Cross point adjustment bit 01 */
#define USB_SLEWF00 (0x0004u) /* b2: Cross point adjustment bit 00 */
#define USB_SLEWR01 (0x0002u) /* b1: Cross point adjustment bit 01 */
#define USB_SLEWR00 (0x0001u) /* b0: Cross point adjustment bit 00 */
/* Deep Standby USB Transceiver Control/Terminal Monitor Register */
/* DPUSR0R */
#define USB_DVBSTS0 (0x00800000u) /* b23: USB0 VBUS input */
#define USB_DOVCB0 (0x00200000u) /* b21: USB0 OVRCURB input */
#define USB_DOVCA0 (0x00100000u) /* b20: USB0 OVRCURA input */
#define USB_DM0 (0x00200000u) /* b17: USB0 D- input */
#define USB_DP0 (0x00100000u) /* b16: USB0 D+ input */
#define USB_FIXPHY0 (0x00000010u) /* b4: USB0 transceiver output fixed bit */
#define USB_DRPD0 (0x00000008u) /* b3: D+/D- pull down resistor control bit */
#define USB_RPUE0 (0x00000002u) /* b1: DP pull up resistor control bit */
#define USB_SRPC0 (0x00000001u) /* b0: USB0 single end receiver control bit */
/* Deep Standby USB Suspend/Resume Interrupt Register */
/* DPUSR1R */
#define USB_DVBINT0 (0x00800000u) /* b23: USB0 VBUS monitor bit */
#define USB_DOVRCRB0 (0x00200000u) /* b21: USB0 OVRCURB DM monitor bit */
#define USB_DOVRCRA0 (0x00100000u) /* b20: USB0 OVRCURA DM monitor bit */
#define USB_DMINT0 (0x00020000u) /* b17: USB0 DM monitor bit */
#define USB_DPINT0 (0x00010000u) /* b16: USB0 DP monitor bit */
#define USB_DVBSE0 (0x00000080u) /* b7: USB0 VBUS interrupt enable */
#define USB_DOVRCRBE0 (0x00000020u) /* b5: USB0 OVRCURB interrupt enable */
#define USB_DOVRCRAE0 (0x00000010u) /* b4: USB0 OVRCURA interrupt enable */
#define USB_DMINTE0 (0x00000002u) /* b1: USB0 DM interrupt enable */
#define USB_DPINTE0 (0x00000001u) /* b0: USB0 DP interrupt enable */
/**************************************************************************************************/
/********* USBHS Only *****************************************************************************/
/* Low Power Control Register */
/* LPCTRL */
#define USB_HWUPM (0x0080u) /* b7: */
/* Low Power Status Register */
/* LPSTS */
#define USB_SUSPENDM (0x4000u) /* b14: UTMI SuspendM control */
/* PHY Single Access Read Data Register */
/* SPRDAT */
#define USB_PHYRDAT (0x00FFu) /* PHY read data bit */
/* Battery Charging Control Register */
/* BCCTRL */
#define USB_PDDETSTS (0x0200u) /* b9: PDDET status */
#define USB_CHGDETSTS (0x0100u) /* b8: CHGDET status */
#define USB_DCPMODE (0x0020u) /* b5: DCP mode control */
#define USB_VDMSRCE (0x0010u) /* b4: VDMSRC control */
#define USB_IDPSINKE (0x0008u) /* b3: IDPSINK control */
#define USB_VDPSRCE (0x0004u) /* b2: VDPSRC control */
#define USB_IDMSINKE (0x0002u) /* b1: IDMSINK control */
#define USB_IDPSRCE (0x0001u) /* b0: IDPSRC control */
/* Function L1 Control Register 1 */
/* PL1CTRL1 */
#define USB_L1EXTMD (0x4000u) /* b14: PHY control mode */
#define USB_DVSQEX (0x0080u) /* b7: DVSQ extension bit */
#define USB_DVSQ (0x0070u) /* b6-4: Mirror of DVSQ */
#define USB_L1NEGOMD (0x0008u) /* b3: L1 response negotiation control */
#define USB_L1RESPMD (0x0006u) /* b2-1: L1 response mode */
#define USB_L1RESPEN (0x0001u) /* b0: L1 response enable */
/* Function L1 Control Register 2 */
/* PL1CTRL2 */
#define USB_HSRDMON (0x0F00u) /* b12: RWE monitor bit */
#define USB_RWEMON (0x1000u) /* b11-8: HIRD monitor bit */
/* Host L1 Control Register 1 */
/* HL1CTRL1 */
#define USB_L1STATUS (0x0006u) /* b2-1: L1 request completion state */
#define USB_L1REQ (0x0001u) /* b0: L1 changes request bit
*/
/* Host L1 Control Register 2 */
/* HL1CTRL2 */
#define USB_BESL (0x8000u) /* b15: BESL & Alternate HIRD bit */
#define USB_L1RWE (0x1000u) /* b12: L1 RemoteWake enable for LPM tokens */
#define USB_HSRD (0x0F00u) /* b11-8: HIRD bit for LPM tokens */
#define USB_L1ADDR (0x000Fu) /* b3-0: DeviceAddress for LPM tokens */
/* PHY Timing Register 1 */
/* PHYTRIM1 */
#define USB_IMPOFFSET (0x7000u) /* b14-12: Terminator adjustment */
#define USB_HSIUP (0x0F00u) /* b11-8: Output level setup of HS */
#define USB_PCOMPENB (0x0080u) /* b7: PVDD starting detection setup */
#define USB_DFALL (0x000Cu) /* b3-2: FS/LS falling output waveform compensation function setting */
#define USB_DRISE (0x0003u) /* b1-0: FS/LS standup output waveform compensation function setting */
/* PHY Timing Register 2 */
/* PHYTRIM2 */
#define USB_DIS (0x7000u) /* b14-12: Disconnect disregard level setup */
#define USB_PDR (0x0300u) /* b9-8: HS output compensation function setting */
#define USB_HSRXENMODE (0x0080u) /* b7: HS reception permission control mode setup */
#define USB_SQU (0x000Fu) /* b3-0: Squelch disregard level setup */
/* Deep Standby USB Register */
/* DPUSR0R */
#define USB_DVBSTSHM (0x00800000u) /* b23: VBUS input */
#define USB_DOVCBHM (0x00200000u) /* b21: OVRCURB input */
#define USB_DOVCAHM (0x00100000u) /* b20: OVRCURA input */
/* DPUSR1R */
#define USB_DVBSTSH (0x00800000u) /* b23: Interruption factor return display of VBUS */
#define USB_DOVCBH (0x00200000u) /* b21: Interruption factor return display of OVRCURB */
#define USB_DOVCAH (0x00100000u) /* b20: Interruption factor return display of OVRCURA */
#define USB_DVBSTSHE (0x00000080u) /* b7 : VBUS interrupt enable */
#define USB_DOVCBHE (0x00000020u) /* b5 : OVRCURB interrupt enable */
#define USB_DOVCAHE (0x00000010u) /* b4 : OVRCURA interrupt enable */
/**************************************************************************************************/
/* DMAx Pin Configuration Register */
#define USB_DREQA (0x4000u) /* b14: Dreq active select */
#define USB_BURST (0x2000u) /* b13: Burst mode */
#define USB_DACKA (0x0400u) /* b10: Dack active select */
#define USB_DFORM (0x0380u) /* b9-7: DMA mode select */
#define USB_CPU_ADR_RD_WR (0x0000u) /* Address + RD/WR mode (CPU bus) */
#define USB_CPU_DACK_RD_WR (0x0100u) /* DACK + RD/WR (CPU bus) */
#define USB_CPU_DACK_ONLY (0x0180u) /* DACK only (CPU bus) */
#define USB_SPLIT_DACK_ONLY (0x0200u) /* DACK only (SPLIT bus) */
#define USB_DENDA (0x0040u) /* b6: Dend active select */
#define USB_PKTM (0x0020u) /* b5: Packet mode */
#define USB_DENDE (0x0010u) /* b4: Dend enable */
#define USB_OBUS (0x0004u) /* b2: OUTbus mode */
/* USB IO Register Reserved bit mask */
#define INTSTS1_MASK (0x5870u) /* INTSTS1 Reserved bit mask */
#define BRDYSTS_MASK (0xFFFFu) /* BRDYSTS Reserved bit mask */
#define NRDYSTS_MASK (0xFFFFu) /* NRDYSTS Reserved bit mask */
#define BEMPSTS_MASK (0xFFFFu) /* BEMPSTS Reserved bit mask */
/* USB Request Type Register */
#define USB_BREQUEST (0xFF00u) /* b15-8 */
/* USB Standard request */
#define USB_GET_STATUS (0x0000u)
#define USB_CLEAR_FEATURE (0x0100u)
#define USB_REQRESERVED (0x0200u)
#define USB_SET_FEATURE (0x0300u)
#define USB_REQRESERVED1 (0x0400u)
#define USB_SET_ADDRESS (0x0500u)
#define USB_GET_DESCRIPTOR (0x0600u)
#define USB_SET_DESCRIPTOR (0x0700u)
#define USB_GET_CONFIGURATION (0x0800u)
#define USB_SET_CONFIGURATION (0x0900u)
#define USB_GET_INTERFACE (0x0A00u)
#define USB_SET_INTERFACE (0x0B00u)
#define USB_SYNCH_FRAME (0x0C00u)
/* USB_BMREQUESTTYPEDIR 0x0080u(b7) */
#define USB_HOST_TO_DEV (0x0000u)
#define USB_DEV_TO_HOST (0x0080u)
/* USB_BMREQUESTTYPETYPE 0x0060u(b6-5) */
#define USB_STANDARD (0x0000u)
#define USB_CLASS (0x0020u)
#define USB_VENDOR (0x0040u)
/* USB_BMREQUESTTYPERECIP 0x001Fu(b4-0) */
#define USB_DEVICE (0x0000u)
#define USB_INTERFACE (0x0001u)
#define USB_ENDPOINT (0x0002u)
#define USB_OTHER (0x0003u)
#define USB_NULL (0x0u)
#define USB_IP0 (0) /* USB0 module */
#define USB_IP1 (1) /* USB1 module */
/* USB pipe number */
#define USB_PIPE0 (0x0u)
#define USB_PIPE1 (0x1u)
#define USB_PIPE2 (0x2u)
#define USB_PIPE3 (0x3u)
#define USB_PIPE4 (0x4u)
#define USB_PIPE5 (0x5u)
#define USB_PIPE6 (0x6u)
#define USB_PIPE7 (0x7u)
#define USB_PIPE8 (0x8u)
#define USB_PIPE9 (0x9u)
#define USB_PIPE10 (0xAu)
#define USB_PIPE11 (0xBu)
#define USB_PIPE12 (0xCu)
#define USB_PIPE13 (0xDu)
#define USB_PIPE14 (0xEu)
#define USB_PIPE15 (0xFu)
#define USB_EP0 (0x0u)
#define USB_EP1 (0x1u)
#define USB_EP2 (0x2u)
#define USB_EP3 (0x3u)
#define USB_EP4 (0x4u)
#define USB_EP5 (0x5u)
#define USB_EP6 (0x6u)
#define USB_EP7 (0x7u)
#define USB_EP8 (0x8u)
#define USB_EP9 (0x9u)
#define USB_EP10 (0xAu)
#define USB_EP11 (0xBu)
#define USB_EP12 (0xCu)
#define USB_EP13 (0xDu)
#define USB_EP14 (0xEu)
#define USB_EP15 (0xFu)
/* Descriptor type Define */
#define USB_DT_DEVICE (0x01u) /* Configuration Descriptor */
#define USB_DT_CONFIGURATION (0x02u) /* Configuration Descriptor */
#define USB_DT_STRING (0x03u) /* Configuration Descriptor */
#define USB_DT_INTERFACE (0x04u) /* Interface Descriptor */
#define USB_DT_ENDPOINT (0x05u) /* Endpoint Descriptor */
#define USB_DT_DEVICE_QUALIFIER (0x06u) /* Device Qualifier Descriptor */
#define USB_DT_OTHER_SPEED_CONF (0x07u) /* Other Speed Configuration Descriptor */
#define USB_DT_INTERFACE_POWER (0x08u) /* Interface Power Descriptor */
#define USB_DT_OTGDESCRIPTOR (0x09u) /* OTG Descriptor */
#define USB_DT_HUBDESCRIPTOR (0x29u) /* HUB descriptor */
/* Interface class Define */
#define USB_IFCLS_NOT (0x00u) /* Un corresponding Class */
#define USB_IFCLS_AUD (0x01u) /* Audio Class */
#define USB_IFCLS_CDC (0x02u) /* CDC Class */
#define USB_IFCLS_CDCC (0x02u) /* CDC-Control Class */
#define USB_IFCLS_HID (0x03u) /* HID Class */
#define USB_IFCLS_PHY (0x05u) /* Physical Class */
#define USB_IFCLS_IMG (0x06u) /* Image Class */
#define USB_IFCLS_PRN (0x07u) /* Printer Class */
#define USB_IFCLS_MAS (0x08u) /* Mass Storage Class */
#define USB_IFCLS_HUB (0x09u) /* HUB Class */
#define USB_IFCLS_CDCD (0x0Au) /* CDC-Data Class */
#define USB_IFCLS_CHIP (0x0Bu) /* Chip/Smart Card Class */
#define USB_IFCLS_CNT (0x0Cu) /* Content-Security Class */
#define USB_IFCLS_VID (0x0Du) /* Video Class */
#define USB_IFCLS_DIAG (0xDCu) /* Diagnostic Device */
#define USB_IFCLS_WIRE (0xE0u) /* Wireless Controller */
#define USB_IFCLS_APL (0xFEu) /* Application-Specific */
#define USB_IFCLS_VEN (0xFFu) /* Vendor-Specific Class */
/* Endpoint Descriptor Define */
#define USB_EP_IN (0x80u) /* In Endpoint */
#define USB_EP_OUT (0x00u) /* Out Endpoint */
#define USB_EP_ISO (0x01u) /* Isochronous Transfer */
#define USB_EP_BULK (0x02u) /* Bulk Transfer */
#define USB_EP_INT (0x03u) /* Interrupt Transfer */
/* Configuration descriptor bit define */
#define USB_CF_RESERVED (0x80u) /* Reserved(set to 1) */
#define USB_CF_SELFP (0x40u) /* Self Powered */
#define USB_CF_BUSP (0x00u) /* Bus Powered */
#define USB_CF_RWUPON (0x20u) /* Remote Wake up ON */
#define USB_CF_RWUPOFF (0x00u) /* Remote Wake up OFF */
/* Descriptor length Define */
#define USB_DD_BLENGTH (18u) /* Device Descriptor Length */
#define USB_CD_BLENGTH (9u) /* Configuration Descriptor Length */
#define USB_ID_BLENGTH (9u) /* Interface Descriptor Length */
#define USB_ED_BLENGTH (7u) /* Endpoint Descriptor Length */
#define USB_BUFSIZE_BIT (10u)
/* other */
#define USB_FUNCTION_CFIFO_USE (0x0000u)
#define USB_FUNCTION_D0FIFO_USE (0x1000u)
#define USB_FUNCTION_D1FIFO_USE (0x2000u)
#endif

View File

@ -0,0 +1,65 @@
/* mbed Microcontroller Library
* 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.
*/
#define NUMBER_OF_LOGICAL_ENDPOINTS (4)
#define NUMBER_OF_PHYSICAL_ENDPOINTS (NUMBER_OF_LOGICAL_ENDPOINTS * 2)
/* Define physical endpoint numbers */
/* Endpoint No. Type(s) MaxPacket DoubleBuffer */
/* ---------------- ------------ ---------- --- */
#define EP0OUT (0x00) /* Control 64 No */
#define EP0IN (0x80) /* Control 64 No */
#define EP1OUT (0x01) /* Int/Bulk/Iso 64/64/1023 Yes */
#define EP1IN (0x81) /* Int/Bulk/Iso 64/64/1023 Yes */
#define EP2OUT (0x02) /* Int/Bulk/Iso 64/64/1023 Yes */
#define EP2IN (0x82) /* Int/Bulk/Iso 64/64/1023 Yes */
#define EP3OUT (0x03) /* Int/Bulk/Iso 64/64/1023 Yes */
#define EP3IN (0x83) /* Int/Bulk/Iso 64/64/1023 Yes */
/* Maximum Packet sizes */
#define MAX_PACKET_SIZE_SETUP (48)
#define MAX_PACKET_SIZE_EP0 (64)
#define MAX_PACKET_SIZE_EP1 (64) /* Int/Bulk */
#define MAX_PACKET_SIZE_EP2 (64) /* Int/Bulk */
#define MAX_PACKET_SIZE_EP3 (200) /* Int/Bulk/iso (44100 stereo 16 bits) */
#define MAX_PACKET_SIZE_EP1_ISO (1023) /* Isochronous */
#define MAX_PACKET_SIZE_EP2_ISO (1023) /* Isochronous */
#define MAX_PACKET_SIZE_EP3_ISO (1023) /* Isochronous */
/* Generic endpoints - intended to be portable accross devices */
/* and be suitable for simple USB devices. */
/* Bulk endpoint */
#define EPBULK_OUT (EP2OUT)
#define EPBULK_IN (EP2IN)
#define EPBULK_OUT_callback EP2_OUT_callback
#define EPBULK_IN_callback EP2_IN_callback
/* Interrupt endpoint */
#define EPINT_OUT (EP1OUT)
#define EPINT_IN (EP1IN)
#define EPINT_OUT_callback EP1_OUT_callback
#define EPINT_IN_callback EP1_IN_callback
/* Isochronous endpoint */
#define EPISO_OUT (EP3OUT)
#define EPISO_IN (EP3IN)
#define EPISO_OUT_callback EP3_OUT_callback
#define EPISO_IN_callback EP3_IN_callback
#define MAX_PACKET_SIZE_EPBULK (MAX_PACKET_SIZE_EP2)
#define MAX_PACKET_SIZE_EPINT (MAX_PACKET_SIZE_EP1)
#define MAX_PACKET_SIZE_EPISO (MAX_PACKET_SIZE_EP3_ISO)

View File

@ -0,0 +1,98 @@
/* mbed Microcontroller Library
* 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.
*/
#ifndef USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
#if defined(TARGET_DISCO_F746NG)
#if (MBED_CONF_TARGET_USB_SPEED == 1) // Defined in json configuration file
#define TARGET_DISCO_F746NG_OTG_HS
#else
#define TARGET_DISCO_F746NG_OTG_FS
#endif
#endif
#if defined(TARGET_DISCO_F429ZI) || \
defined(TARGET_DISCO_F769NI) || \
defined(TARGET_DISCO_F746NG_OTG_HS)
#define USBHAL_IRQn OTG_HS_IRQn
#else
#define USBHAL_IRQn OTG_FS_IRQn
#endif
#include "USBEndpoints_STM32.h"
#define NB_ENDPOINT 4 // Must be a multiple of 4 bytes
#define MAXTRANSFER_SIZE 0x200
#define FIFO_USB_RAM_SIZE (MAXTRANSFER_SIZE + MAX_PACKET_SIZE_EP0 + MAX_PACKET_SIZE_EP1 + MAX_PACKET_SIZE_EP2 + MAX_PACKET_SIZE_EP3)
#if (FIFO_USB_RAM_SIZE > 0x500)
#error "FIFO dimensioning incorrect"
#endif
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t* endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
USBPhyEvents *events;
bool sof_enabled;
uint8_t epComplete[2 * NB_ENDPOINT];
PCD_HandleTypeDef hpcd;
private:
static void _usbisr(void);
};
#endif

View File

@ -0,0 +1,541 @@
/* mbed Microcontroller Library
* 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.
*/
/* This target doesn't support USB */
#if !defined(DEVICE_USBDEVICE) || !DEVICE_USBDEVICE
#define USBSTM_HAL_UNSUPPORTED
#endif
/* TARGET NOT STM does not support this HAL */
#ifndef TARGET_STM
#define USBSTM_HAL_UNSUPPORTED
#endif
#ifndef USBSTM_HAL_UNSUPPORTED
#include "USBPhyHw.h"
#include "pinmap.h"
/* endpoint conversion macros */
#define EP_TO_LOG(ep) ((ep) & 0x7F)
#define EP_TO_IDX(ep) (((ep) << 1) | ((ep) & 0x80 ? 1 : 0))
#define LOG_IN_TO_EP(ep) ((ep) | 0x80)
#define LOG_OUT_TO_EP(ep) ((ep) | 0x00)
#define IDX_TO_EP(ep) (((ep) >> 1)|((ep) & 1) << 7)
/* endpoint defines */
#define NUM_ENDPOINTS 4
#define MAX_PACKET_NON_ISO 64
#define MAX_PACKET_ISO (256 + 128) // Spec can go up to 1023, only ram for this though
#define ENDPOINT_NON_ISO (USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_ALLOW_INT)
static const uint32_t tx_ep_sizes[NUM_ENDPOINTS] = {
MAX_PACKET_NON_ISO,
MAX_PACKET_NON_ISO,
MAX_PACKET_NON_ISO,
MAX_PACKET_ISO
};
uint32_t HAL_PCDEx_GetTxFiFo(PCD_HandleTypeDef *hpcd, uint8_t fifo)
{
uint32_t len;
if (fifo == 0) {
len = hpcd->Instance->DIEPTXF0_HNPTXFSIZ >> 16;
}
else {
len = hpcd->Instance->DIEPTXF[fifo - 1] >> 16;
}
return len * 4;
}
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
USB_OTG_GlobalTypeDef *USBx = hpcd->Instance;
if (priv->sof_enabled) {
priv->events->sof((USBx_DEVICE->DSTS & USB_OTG_DSTS_FNSOF) >> 8);
}
}
/* this call at device reception completion on a Out Enpoint */
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
uint8_t endpoint = LOG_OUT_TO_EP(epnum);
priv->epComplete[EP_TO_IDX(endpoint)] = 1;
/* -2 endpoint 0 In out are not in call back list */
if (epnum) {
priv->events->out(endpoint);
} else {
priv->events->ep0_out();
}
}
/* this is call at device transmission completion on In endpoint */
void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
uint8_t endpoint = LOG_IN_TO_EP(epnum);
priv->epComplete[EP_TO_IDX(endpoint)] = 1;
/* -2 endpoint 0 In out are not in call back list */
if (epnum) {
priv->events->in(endpoint);
} else {
priv->events->ep0_in();
}
}
/* This is call at device set up reception */
void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
priv->events->ep0_setup();
}
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
priv->events->suspend(1);
}
void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd)
{
USBPhyHw *priv=((USBPhyHw *)(hpcd->pData));
priv->events->suspend(0);
}
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
{
// Nothing to do
}
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
// Nothing to do
}
void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd)
{
USBPhyHw *obj=((USBPhyHw *)(hpcd->pData));
unsigned int i;
for(i=0;i<hpcd->Init.dev_endpoints;i++) {
obj->epComplete[2*i]=0;
HAL_PCD_EP_Close(hpcd,IDX_TO_EP(2*i));
HAL_PCD_EP_Flush(hpcd,IDX_TO_EP(2*i));
obj->epComplete[2*i+1]=0;
HAL_PCD_EP_Close(hpcd,IDX_TO_EP(2*i+1));
HAL_PCD_EP_Flush(hpcd,IDX_TO_EP(2*i+1));
}
obj->endpoint_add(EP0IN, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
obj->endpoint_add(EP0OUT, MAX_PACKET_SIZE_EP0, USB_EP_TYPE_CTRL);
obj->events->reset();
}
/* hal pcd handler , used for STM32 HAL PCD Layer */
static USBPhyHw *instance;
USBPhy *get_usb_phy()
{
static USBPhyHw usbphy;
return &usbphy;
}
USBPhyHw::USBPhyHw(): events(NULL), sof_enabled(false)
{
}
USBPhyHw::~USBPhyHw()
{
}
void USBPhyHw::init(USBPhyEvents *events)
{
NVIC_DisableIRQ(USBHAL_IRQn);
this->events = events;
sof_enabled = false;
memset(epComplete, 0, sizeof(epComplete));
memset(&hpcd.Init, 0, sizeof(hpcd.Init));
#if defined(TARGET_DISCO_F769NI) || \
defined(TARGET_DISCO_F746NG_OTG_HS)
hpcd.Instance = USB_OTG_HS;
hpcd.Init.phy_itface = PCD_PHY_ULPI;
hpcd.Init.Sof_enable = 1;
hpcd.Init.speed = PCD_SPEED_HIGH;
#elif defined(TARGET_DISCO_F429ZI)
hpcd.Instance = USB_OTG_HS;
hpcd.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd.Init.Sof_enable = 1;
hpcd.Init.speed = PCD_SPEED_HIGH;
#else
hpcd.Instance = USB_OTG_FS;
hpcd.Init.phy_itface = PCD_PHY_EMBEDDED;
hpcd.Init.Sof_enable = 1;
hpcd.Init.speed = PCD_SPEED_FULL;
#endif
hpcd.Init.dev_endpoints = NB_ENDPOINT;
hpcd.Init.ep0_mps = MAX_PACKET_SIZE_EP0;
// Pass instance for usage inside call back
instance = this;
// Configure USB pins and other clocks
#if defined(TARGET_NUCLEO_F207ZG) || \
defined(TARGET_NUCLEO_F401RE) || \
defined(TARGET_NUCLEO_F411RE) || \
defined(TARGET_NUCLEO_F412ZG) || \
defined(TARGET_NUCLEO_F413ZH) || \
defined(TARGET_NUCLEO_F429ZI) || \
defined(TARGET_NUCLEO_F446RE) || \
defined(TARGET_NUCLEO_F446ZE) || \
defined(TARGET_NUCLEO_F767ZI) || \
defined(TARGET_NUCLEO_F746ZG) || \
defined(TARGET_NUCLEO_F756ZG) || \
defined(TARGET_DISCO_F407VG) || \
defined(TARGET_DISCO_F413ZH) || \
defined(TARGET_DISCO_F469NI) || \
defined(TARGET_DISCO_F746NG_OTG_FS)
__HAL_RCC_GPIOA_CLK_ENABLE();
pin_function(PA_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DM
pin_function(PA_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DP
#if defined(TARGET_DISCO_F746NG_OTG_FS)
__HAL_RCC_GPIOJ_CLK_ENABLE();
pin_function(PJ_12, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // VBUS
#else
pin_function(PA_9, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // VBUS
#endif
pin_function(PA_10, STM_PIN_DATA(STM_MODE_AF_OD, GPIO_PULLUP, GPIO_AF10_OTG_FS)); // ID
pin_function(PA_8, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // SOF
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
#elif defined(TARGET_DISCO_F429ZI)
__HAL_RCC_GPIOB_CLK_ENABLE();
pin_function(PB_14, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS)); // DM
pin_function(PB_15, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF12_OTG_HS_FS)); // DP
pin_function(PB_13, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); // VBUS
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
#elif defined(TARGET_DISCO_L475VG_IOT01A) || \
defined(TARGET_DISCO_L476VG)
__HAL_RCC_GPIOA_CLK_ENABLE();
pin_function(PA_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DM
pin_function(PA_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DP
__HAL_RCC_GPIOC_CLK_ENABLE();
pin_function(PC_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // VBUS
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableVddUSB();
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
#elif defined(TARGET_NUCLEO_L496ZG) || \
defined(TARGET_NUCLEO_L496ZG_P) || \
defined(TARGET_DISCO_L496AG) || \
defined(TARGET_NUCLEO_L4R5ZI)
__HAL_RCC_GPIOA_CLK_ENABLE();
pin_function(PA_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DM
pin_function(PA_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DP
pin_function(PA_10, STM_PIN_DATA(STM_MODE_AF_OD, GPIO_PULLUP, GPIO_AF10_OTG_FS)); // ID
pin_function(PA_9, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // VBUS
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWREx_EnableVddUSB();
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
#elif defined(TARGET_DISCO_F769NI) || \
defined(TARGET_DISCO_F746NG_OTG_HS)
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOI_CLK_ENABLE();
pin_function(PA_5, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // CLK
pin_function(PA_3, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D0
pin_function(PB_0, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D1
pin_function(PB_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D2
pin_function(PB_5, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D3
pin_function(PB_10, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D4
pin_function(PB_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D5
pin_function(PB_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D6
pin_function(PB_13, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // D7
pin_function(PC_0, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // STP
pin_function(PH_4, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // NXT
#if defined(TARGET_DISCO_F769NI)
pin_function(PI_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // DIR
#else
pin_function(PC_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_HS)); // DIR
#endif
__HAL_RCC_USB_OTG_HS_ULPI_CLK_ENABLE();
__HAL_RCC_USB_OTG_HS_CLK_ENABLE();
#elif defined(TARGET_STEVAL_3DP001V1)
__HAL_RCC_GPIOB_CLK_ENABLE();
pin_function(PA_11, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DM
pin_function(PA_12, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF10_OTG_FS)); // DP
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
#else
#error "USB pins are not configured !"
#endif
__HAL_RCC_SYSCFG_CLK_ENABLE();
// Configure PCD and FIFOs
hpcd.pData = (void*)this;
hpcd.State = HAL_PCD_STATE_RESET;
HAL_PCD_Init(&hpcd);
uint32_t total_bytes = 0;
/* Reserve space in the RX buffer for:
* - 1 isochonous packet
* - 2 max sized non-isochonous packets
* - setup buffer - 10 words as specified by Reference Manual
* - global nak out - 1 words as specified by Reference Manual
*/
uint32_t fifo_size = (MAX_PACKET_ISO + 4) + (MAX_PACKET_NON_ISO + 4) * 2 + (10 * 4) + (1 * 4);
HAL_PCDEx_SetRxFiFo(&hpcd, (fifo_size / 4));
total_bytes += fifo_size;
/* Reserve Tx space up front */
for (int i = 0; i < NUM_ENDPOINTS; i++) {
fifo_size = tx_ep_sizes[i] + 4;
HAL_PCDEx_SetTxFiFo(&hpcd, i, fifo_size / 4);
total_bytes += fifo_size;
}
/* 1.25 kbytes */
MBED_ASSERT(total_bytes <= 1280);
// Configure interrupt vector
NVIC_SetVector(USBHAL_IRQn, (uint32_t)&_usbisr);
NVIC_SetPriority(USBHAL_IRQn, 1);
NVIC_EnableIRQ(USBHAL_IRQn);
}
void USBPhyHw::deinit()
{
HAL_PCD_DeInit(&hpcd);
NVIC_DisableIRQ(USBHAL_IRQn);
}
bool USBPhyHw::powered()
{
return true;
}
void USBPhyHw::connect()
{
HAL_PCD_Start(&hpcd);
}
void USBPhyHw::disconnect()
{
HAL_PCD_Stop(&hpcd);
}
void USBPhyHw::configure()
{
// Not needed
}
void USBPhyHw::unconfigure()
{
// Not needed
}
void USBPhyHw::sof_enable()
{
sof_enabled = true;
}
void USBPhyHw::sof_disable()
{
sof_enabled = false;
}
void USBPhyHw::set_address(uint8_t address)
{
HAL_PCD_SetAddress(&hpcd, address);
}
void USBPhyHw::remote_wakeup()
{
}
const usb_ep_table_t *USBPhyHw::endpoint_table()
{
static const usb_ep_table_t table = {
1280, // 1.25K for endpoint buffers but space is allocated up front
{
{USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ENDPOINT_NON_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{ENDPOINT_NON_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0},
{0 | USB_EP_ATTR_DIR_IN_AND_OUT, 0, 0}
}
};
return &table;
}
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
{
// FUTURE - set endpoint 0 size and return this size
return MAX_PACKET_SIZE_EP0;
}
// read setup packet
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
{
if (size > MAX_PACKET_SIZE_SETUP) {
size = MAX_PACKET_SIZE_SETUP;
}
memcpy(buffer, hpcd.Setup, size);
memset(hpcd.Setup,0,MAX_PACKET_SIZE_SETUP);
}
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
{
HAL_StatusTypeDef ret;
epComplete[EP_TO_IDX(EP0OUT)] = 2;
ret = HAL_PCD_EP_Receive(&hpcd, EP0OUT, data, size > MAX_PACKET_SIZE_EP0 ? MAX_PACKET_SIZE_EP0 : size);
MBED_ASSERT(ret!=HAL_BUSY);
}
uint32_t USBPhyHw::ep0_read_result()
{
epComplete[EP_TO_IDX(EP0OUT)] = 0;
return HAL_PCD_EP_GetRxCount(&hpcd, 0);
}
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
{
/* check that endpoint maximum size is not exceeding TX fifo */
MBED_ASSERT(hpcd.IN_ep[0].maxpacket >= size);
endpoint_write(EP0IN, buffer, size);
}
void USBPhyHw::ep0_stall()
{
endpoint_stall(EP0IN);
endpoint_stall(EP0OUT);
}
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
{
uint32_t len;
/*
* Endpoints are configured in init since re-configuring
* fifos when endpoints are added or removed causes tests to fail.
*/
if (endpoint & 0x80) {
len = HAL_PCDEx_GetTxFiFo(&hpcd,endpoint & 0x7f);
MBED_ASSERT(len >= max_packet);
}
HAL_StatusTypeDef ret = HAL_PCD_EP_Open(&hpcd, endpoint, max_packet, type);
MBED_ASSERT(ret!=HAL_BUSY);
return (ret == HAL_OK) ? true:false;
}
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
{
HAL_StatusTypeDef ret = HAL_PCD_EP_Close(&hpcd, endpoint);
MBED_ASSERT(ret == HAL_OK);
}
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
{
HAL_StatusTypeDef ret;
ret = HAL_PCD_EP_SetStall(&hpcd, endpoint);
MBED_ASSERT(ret!=HAL_BUSY);
}
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
{
HAL_StatusTypeDef ret;
ret = HAL_PCD_EP_ClrStall(&hpcd, endpoint);
MBED_ASSERT(ret!=HAL_BUSY);
}
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
// clean reception end flag before requesting reception
HAL_StatusTypeDef ret = HAL_PCD_EP_Receive(&hpcd, endpoint, data, size);
MBED_ASSERT(ret!=HAL_BUSY);
return true;
}
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
{
if (epComplete[EP_TO_IDX(endpoint)]==0) {
/* no reception possible !!! */
return 0;
} else if ((epComplete[EP_TO_IDX(endpoint)]!=1)) {
return 0;
}
epComplete[EP_TO_IDX(endpoint)]= 0;
return HAL_PCD_EP_GetRxCount(&hpcd, endpoint);;
}
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
HAL_StatusTypeDef ret;
// clean transmission end flag before requesting transmission
epComplete[EP_TO_IDX(endpoint)] = 2;
ret = HAL_PCD_EP_Transmit(&hpcd, endpoint, data, size);
MBED_ASSERT(ret!=HAL_BUSY);
// update the status
if (ret != HAL_OK) return false;
// fix me return is too simple
return true;
}
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
{
HAL_StatusTypeDef ret = HAL_PCD_EP_Abort(&hpcd, endpoint);
MBED_ASSERT(ret==HAL_OK);
}
void USBPhyHw::process()
{
HAL_PCD_IRQHandler(&instance->hpcd);
// Re-enable interrupt
NVIC_ClearPendingIRQ(USBHAL_IRQn);
NVIC_EnableIRQ(USBHAL_IRQn);
}
void USBPhyHw::_usbisr(void) {
NVIC_DisableIRQ(USBHAL_IRQn);
instance->events->start_process();
}
#endif

View File

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t* endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
private:
USBPhyEvents *events;
static void _usbisr(void);
};
#endif

View File

@ -0,0 +1,310 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "USBPhyHw.h"
static USBPhyHw *instance;
USBPhy *get_usb_phy()
{
static USBPhyHw usbphy;
return &usbphy;
}
USBPhyHw::USBPhyHw(): events(NULL)
{
}
USBPhyHw::~USBPhyHw()
{
}
void USBPhyHw::init(USBPhyEvents *events)
{
this->events = events;
// Disable IRQ
NVIC_DisableIRQ(USB_IRQn);
// TODO - Setup clocks
// TODO - Enable USB module
// Enable IRQ
NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
NVIC_EnableIRQ(USB_IRQn);
}
void USBPhyHw::deinit()
{
// Disconnect and disable interrupt
disconnect();
NVIC_DisableIRQ(USB_IRQn);
}
bool USBPhyHw::powered()
{
// TODO - return true if powered false otherwise. Devices which don't support
// this should always return true
return true;
}
void USBPhyHw::connect()
{
// TODO - Enable endpoint interrupts
// TODO - Enable pullup on D+
}
void USBPhyHw::disconnect()
{
// TODO - Disable all endpoints
// TODO - Clear all endpoint interrupts
// TODO - Disable pullup on D+
}
void USBPhyHw::configure()
{
// TODO - set device to configured. Most device will not need this
}
void USBPhyHw::unconfigure()
{
// TODO - set device to unconfigured. Most device will not need this
}
void USBPhyHw::sof_enable()
{
// TODO - Enable SOF interrupt
}
void USBPhyHw::sof_disable()
{
// TODO - Disable SOF interrupt
}
void USBPhyHw::set_address(uint8_t address)
{
// TODO - set the device address. Address must take effect
// after the status phase of the current transfer
}
void USBPhyHw::remote_wakeup()
{
// TODO - Sent remote wakeup over USB lines (if supported)
}
const usb_ep_table_t *USBPhyHw::endpoint_table()
{
// TODO - Update the endpoint table for what your device supports
static const usb_ep_table_t template_table = {
4096 - 32 * 4, // 32 words for endpoint buffers
// +3 based added to interrupt and isochronous to ensure enough
// space for 4 byte alignment
{
{USB_EP_ATTR_ALLOW_CTRL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_ISO | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_INT | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 3},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0},
{USB_EP_ATTR_ALLOW_BULK | USB_EP_ATTR_DIR_IN_AND_OUT, 2, 0}
}
};
return &lpc_table;
}
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
{
// TODO - set endpoint 0 size and return this size
return 64;
}
// read setup packet
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
{
// TODO - read up to size bytes of the setup packet
}
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
{
// TODO - setup data buffer to receive next endpoint 0 OUT packet
}
uint32_t USBPhyHw::ep0_read_result()
{
// TODO - return the size of the last OUT packet received on endpoint 0
return 0;
}
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
{
// TODO - start transferring buffer on endpoint 0 IN
}
void USBPhyHw::ep0_stall()
{
// TODO - protocol stall endpoint 0. This stall must be automatically
// cleared by the next setup packet
}
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
{
// TODO - enable this endpoint
return true;
}
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
{
// TODO - disable and remove this endpoint
}
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
{
// TODO - stall this endpoint until it is explicitly cleared
}
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
{
// TODO - unstall this endpoint
}
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
// TODO - setup data buffer to receive next endpoint OUT packet and return true if successful
return true;
}
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
{
// TODO - return the size of the last OUT packet received on endpoint
}
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
// TODO - start transferring buffer on endpoint IN
return true;
}
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
{
// TODO - stop the current transfer on this endpoint and don't call the IN or OUT callback
}
void USBPhyHw::process()
{
// TODO - update register for your mcu
uint8_t stat = USB0->STAT;
USB0->STAT = stat; // Clear pending interrupts
// reset interrupt
if (stat & USB_STAT_RESET_MASK) {
// TODO - disable all endpoints
// TODO - clear all endpoint interrupts
// TODO - enable control endpoint
// reset bus for USBDevice layer
events->reset();
// Re-enable interrupt
NVIC_ClearPendingIRQ(USB_IRQn);
NVIC_EnableIRQ(USB_IRQn);
return;
}
// power applied
if (stat & USB_STAT_POWERED) {
events->powered(true);
}
// power lost
if (stat & USB_STAT_UNPOWERED) {
events->powered(false);
}
// sleep interrupt
if (stat & USB_STAT_SUSPEND_MASK) {
events->suspend(true);
}
// resume interrupt
if (stat & USB_STAT_RESUME_MASK) {
events->suspend(false);
}
// sof interrupt
if (stat & USB_STAT_SOF_MASK) {
// SOF event, read frame number
events->sof(USB0->FRAME);
}
// endpoint interrupt
if (stat & USB_STAT_EP_MASK) {
uint32_t ep_pending = USB->EP;
// TODO - call endpoint 0 IN callback if pending
events->ep0_in();
// TODO - call endpoint 0 OUT callback if pending
events->ep0_out();
// TODO - call endpoint 0 SETUP callback if pending
events->ep0_setup();
for (int i = 0; i < 16; i++) {
// TODO - call endpoint i IN callback if pending
events->in(0x80 | i);
}
for (int i = 0; i < 16; i++) {
// TODO - call endpoint i OUT callback if pending
events->out();
}
USB->EP = ep_pending; // clear pending
}
// Re-enable interrupt
NVIC_ClearPendingIRQ(USB_IRQn);
NVIC_EnableIRQ(USB_IRQn);
}
void USBPhyHw::_usbisr(void) {
NVIC_DisableIRQ(USB_IRQn);
instance->events->start_process();
}

View File

@ -0,0 +1,146 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "AsyncOp.h"
#include "mbed_critical.h"
#include "mbed_assert.h"
using namespace rtos;
AsyncOp::AsyncOp():
_list(NULL), _wait(NULL), _aborted(false), _timeout(false)
{
}
AsyncOp::AsyncOp(mbed::Callback<void()> &callback):
_list(NULL), _wait(NULL), _aborted(false), _timeout(false)
{
_callback = callback;
}
AsyncOp::~AsyncOp()
{
MBED_ASSERT(_list == NULL);
}
void AsyncOp::wait(rtos::Mutex *host_mutex, uint32_t milliseconds)
{
// Optimization so semaphore is only created if necessary
core_util_critical_section_enter();
bool done = _list == NULL;
core_util_critical_section_exit();
if (done) {
return;
}
// Construct semaphore to wait on
Semaphore sem(0);
core_util_critical_section_enter();
done = _list == NULL;
// Wait is only allowed to be called from one thread
MBED_ASSERT(_wait == NULL);
_wait = &sem;
core_util_critical_section_exit();
if (done) {
// Operation was signaled before semaphore was set
return;
}
if (sem.wait(milliseconds) == 1) {
// Operation completion signaled semaphore
return;
}
_host_lock(host_mutex);
_abort(true);
_host_unlock(host_mutex);
}
void AsyncOp::abort()
{
// Host lock must be held
_abort(false);
}
void AsyncOp::complete()
{
core_util_critical_section_enter();
mbed::Callback<void()> cb = _callback;
_callback = NULL;
_list = NULL;
if (_wait != NULL) {
_wait->release();
}
core_util_critical_section_exit();
if (cb) {
cb();
}
}
bool AsyncOp::timeout()
{
core_util_critical_section_enter();
bool ret = _timeout;
core_util_critical_section_exit();
return ret;
}
void AsyncOp::_abort(bool timeout)
{
// host lock must be held
core_util_critical_section_enter();
OperationListBase *list = _list;
if (list) {
_callback = NULL;
_aborted = true;
_wait = NULL;
_timeout = timeout;
_list = NULL;
}
core_util_critical_section_exit();
if (list) {
list->remove(this);
}
}
void AsyncOp::_host_lock(rtos::Mutex *host_mutex)
{
if (host_mutex) {
host_mutex->lock();
} else {
core_util_critical_section_enter();
}
}
void AsyncOp::_host_unlock(rtos::Mutex *host_mutex)
{
if (host_mutex) {
host_mutex->unlock();
} else {
core_util_critical_section_exit();
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_ASYNC_OP_H
#define MBED_ASYNC_OP_H
#include "Mutex.h"
#include "Semaphore.h"
#include "Callback.h"
#include "LinkEntry.h"
#include "OperationListBase.h"
class AsyncOp: public LinkEntry {
public:
/**
* Construct a new AsyncOp object
*/
AsyncOp();
/**
* Construct a new AsyncOp object
*
* @param callback Completion callback
*/
AsyncOp(mbed::Callback<void()> &callback);
/**
* Cleanup resources used by this AsyncOp
*/
virtual ~AsyncOp();
/**
* Wait for this asynchronous operation to complete
*
* If the timeout expires then this asynchronous operation is
* aborted and the timeout flag is set.
*
* @note - the host object's lock MUST NOT be held when this call is made
*/
void wait(rtos::Mutex *host_mutex, uint32_t milliseconds = osWaitForever);
/**
* Abort this asynchronous operation
*
* This function has no effect if the operation is complete. Otherwise
* the aborted flag is set.
*
* @note - the host object's lock MUST be held when this call is made
*/
void abort();
/**
* Check if this operation timed out
*
* @return true if this operation timed out, false otherwise
*/
bool timeout();
/**
* Check if this operation was aborted
*
* @return true if this operation was aborted, false otherwise
*/
bool aborted();
protected:
/**
* Callback indicating that something changed
*
* @return true if finished false if not
*/
virtual bool process() = 0;
/**
* Callback indicating that this event finished
*/
virtual void complete();
private:
friend class OperationListBase;
mbed::Callback<void()> _callback;
OperationListBase *_list;
rtos::Semaphore *_wait;
bool _aborted;
bool _timeout;
void _abort(bool timeout);
static void _host_lock(rtos::Mutex *host_mutex);
static void _host_unlock(rtos::Mutex *host_mutex);
};
#endif

View File

@ -0,0 +1,152 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "ByteBuffer.h"
#include "mbed_assert.h"
#include <string.h>
ByteBuffer::ByteBuffer(uint32_t size): _head(0), _tail(0), _size(0), _buf(NULL)
{
resize(_size);
}
ByteBuffer::~ByteBuffer()
{
delete[] _buf;
_buf = 0;
}
void ByteBuffer::resize(uint32_t size)
{
delete[] _buf;
_head = 0;
_tail = 0;
_size = size + 1;
_buf = new uint8_t[_size]();
}
void ByteBuffer::push(uint8_t data)
{
_buf[_tail] = data;
_tail++;
if (_tail >= _size) {
_tail -= _size;
}
// Overflow not allowed
MBED_ASSERT(_head != _tail);
}
void ByteBuffer::write(uint8_t *data, uint32_t size)
{
MBED_ASSERT(size <= free());
if (size == 0) {
return;
}
uint32_t new_tail = _tail + size;
if (new_tail >= _size) {
new_tail -= _size;
}
// Perform first memcpy
uint32_t until_end = _size - _tail;
uint32_t copy_size = until_end < size ? until_end : size;
memcpy(_buf + _tail, data, copy_size);
data += copy_size;
size -= copy_size;
// Perform second memcpy
if (size > 0) {
memcpy(_buf, data, size);
}
// Update tail
_tail = new_tail;
}
uint8_t ByteBuffer::pop()
{
// Underflow not allowed
MBED_ASSERT(_head != _tail);
uint8_t val = _buf[_head];
_head++;
if (_head >= _size) {
_head -= _size;
}
return val;
}
void ByteBuffer::read(uint8_t *data, uint32_t size)
{
MBED_ASSERT(size <= ByteBuffer::size());
if (size == 0) {
return;
}
uint32_t new_head = _head + size;
if (new_head >= _size) {
new_head -= _size;
}
// Perform first memcpy
uint32_t until_end = _size - _head;
uint32_t copy_size = until_end < size ? until_end : size;
memcpy(data, _buf + _head, copy_size);
data += copy_size;
size -= copy_size;
// Perform second memcpy
if (size > 0) {
memcpy(data, _buf, size);
}
// Update head
_head = new_head;
}
uint32_t ByteBuffer::size()
{
uint32_t size;
if (_tail < _head) {
size = _size + _tail - _head;
} else {
size = _tail - _head;
}
return size;
}
uint32_t ByteBuffer::free()
{
return _size - size() - 1;
}
bool ByteBuffer::full()
{
uint32_t next = _tail + 1;
if (next >= _size) {
next -= _size;
}
return next == _head;
}
bool ByteBuffer::empty()
{
return _head == _tail;
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 BYTE_BUFFER_H
#define BYTE_BUFFER_H
#include <stdint.h>
class ByteBuffer {
public:
/**
* Create a byte buffer of the given size
*
* @param size Number of bytes this buffer can hold
*/
ByteBuffer(uint32_t size = 0);
/**
* Delete this byte buffer
*/
~ByteBuffer();
/**
* Set the size of the buffer
*
* Buffer contents are reset.
*
* @param size New buffer size
*/
void resize(uint32_t size);
/**
* Add a single byte to this buffer
*
* There must be enough space in the buffer or the behavior is undefined.
*
* @param data byte to add
*/
void push(uint8_t data);
/**
* Write a block of data to this ByteBuffer
*
* There must be enough space in the ByteBuffer or the behavior is undefined.
*
* @param data Block of data to write
* @param size Size of data to write
*/
void write(uint8_t *data, uint32_t size);
/**
* Remove a byte from this buffer
*
* @return data byte
*/
uint8_t pop();
/**
* Read a block of data from this ByteBuffer into a buffer pointed by 'data'
*
* There must be enough data in the ByteBuffer or the behavior is undefined.
*
* @param data Block of data to read
* @param size Size of data to read
*/
void read(uint8_t *data, uint32_t size);
/**
* Return the number bytes in this byte buffer
*
* @return Number of used bytes
*/
uint32_t size();
/**
* Return the number of additional bytes this buffer can hold
*
* @return Number of free bytes
*/
uint32_t free();
/**
* Check if this byte buffer is full
*
* @return true if full, false otherwise
*/
bool full();
/**
* Check if this byte buffer is empty
*
* @return true if empty, false otherwise
*/
bool empty();
private:
uint32_t _head;
uint32_t _tail;
uint32_t _size;
uint8_t *_buf;
};
#endif

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_LINKED_ENTRY_H
#define MBED_LINKED_ENTRY_H
#include <cstddef>
class LinkEntry {
public:
LinkEntry(): _next(NULL)
{
}
private:
friend class LinkedListBase;
LinkEntry *_next;
};
#endif

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_LINKED_LIST_H
#define MBED_LINKED_LIST_H
#include "LinkEntry.h"
#include "LinkedListBase.h"
template<class T>
class LinkedList: public LinkedListBase {
public:
LinkedList() {}
~LinkedList() {}
/**
* Return the element at the head of the list
*
* @return The element at the head of the list or NULL if the list is empty
*/
T *head()
{
return static_cast<T *>(LinkedListBase::head());
}
/**
* Add an element to the tail of the list
*
* @param entry New element to add
*/
void enqueue(T *entry)
{
LinkedListBase::enqueue(static_cast<LinkEntry *>(entry));
}
/**
* Remove the element at the head of the list
*
* @return The element at the head of the list or NULL if the list is empty
*/
T *dequeue()
{
return static_cast<T *>(LinkedListBase::dequeue());
}
};
#endif

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "LinkedList.h"
#include "LinkEntry.h"
#include "mbed_assert.h"
LinkedListBase::LinkedListBase(): _head(0), _tail(0)
{
}
LinkedListBase::~LinkedListBase()
{
}
LinkEntry *LinkedListBase::head()
{
return _head;
}
void LinkedListBase::enqueue(LinkEntry *entry)
{
entry->_next = NULL;
if (_tail == NULL) {
_head = entry;
} else {
_tail->_next = entry;
}
_tail = entry;
}
LinkEntry *LinkedListBase::dequeue()
{
if (_head == NULL) {
return NULL;
}
if (_head->_next == NULL) {
_tail = NULL;
}
LinkEntry *entry = _head;
_head = _head->_next;
entry->_next = NULL;
return entry;
}
void LinkedListBase::remove(LinkEntry *entry)
{
LinkEntry *prev = NULL;
LinkEntry *cur = _head;
while (cur != entry) {
if (cur == NULL) {
// Element is not in the list
return;
}
prev = cur;
cur = cur->_next;
}
if (prev != NULL) {
prev->_next = entry->_next;
}
if (entry == _head) {
_head = entry->_next;
}
if (entry == _tail) {
_tail = prev;
}
entry->_next = NULL;
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_LINKED_LIST_BASE_H
#define MBED_LINKED_LIST_BASE_H
#include "LinkEntry.h"
class LinkedListBase {
public:
LinkedListBase();
~LinkedListBase();
/**
* Return the element at the head of the list
*
* @return The element at the head of the list or NULL if the list is empty
*/
LinkEntry *head();
/**
* Add an element to the tail of the list
*
* @param entry New element to add
*/
void enqueue(LinkEntry *entry);
/**
* Remove the element at the head of the list
*
* @return The element at the head of the list or NULL if the list is empty
*/
LinkEntry *dequeue();
/**
* Remove the specified element if it is in the list
*
* @param entry Element to remove from the list
*/
void remove(LinkEntry *entry);
private:
LinkEntry *_head;
LinkEntry *_tail;
};
#endif

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_OPERATION_LIST_H
#define MBED_OPERATION_LIST_H
#include "OperationListBase.h"
#include "AsyncOp.h"
template<class T>
class OperationList: public OperationListBase {
public:
/**
* Create a new empty operation list
*/
OperationList()
{
}
/**
* Destroy this object and abort all operations
*/
~OperationList()
{
}
/**
* Add an operation to the list
*
* If the list was empty then call process on this
* operation
*
* @param op Operation to add
*/
void add(T *op)
{
OperationListBase::add(op);
}
/**
* Remove an operation from the list
*
* If this was the head of the list then process the
* next element in the list.
*
* @param op Operation to remove
*/
void remove(T *op)
{
OperationListBase::remove(op);
}
/**
* Dequeue the head of the list
*
* Remove the head of the operation list without completing it
* or processing the next element. The caller must call the
* AsnycOp::complete() function of the returned object.
* Additionally process() must be called on this object
* if there are still elements in the list.
*
* @return The async op at the head of the list
*/
T *dequeue_raw()
{
return static_cast<AsyncOp *>(OperationListBase::dequeue_raw());
}
};
#endif

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "OperationListBase.h"
#include "AsyncOp.h"
#include "mbed_assert.h"
OperationListBase::OperationListBase()
{
}
OperationListBase::~OperationListBase()
{
remove_all();
}
bool OperationListBase::empty()
{
return _list.head() == NULL;
}
void OperationListBase::add(AsyncOp *op)
{
bool was_empty = _list.head() == NULL;
op->_list = this;
_list.enqueue(op);
if (was_empty) {
process();
}
}
void OperationListBase::process()
{
while (true) {
AsyncOp *op = static_cast<AsyncOp *>(_list.head());
if (op == NULL) {
// List empty, nothing left to do
break;
}
if (!op->process()) {
// Processing is in progress
break;
}
_list.dequeue();
op->complete();
}
}
void OperationListBase::remove(AsyncOp *op)
{
bool head = _list.head() == op;
_list.remove(op);
if (head) {
process();
}
}
AsyncOp *OperationListBase::dequeue_raw()
{
return static_cast<AsyncOp *>(_list.dequeue());
}
void OperationListBase::remove_all()
{
while (true) {
AsyncOp *op = static_cast<AsyncOp *>(_list.head());
if (op == NULL) {
// List empty, nothing left to do
break;
}
op->complete();
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_OPERATION_LIST_BASE_H
#define MBED_OPERATION_LIST_BASE_H
#include "LinkedListBase.h"
#include "Mutex.h"
class AsyncOp;
class OperationListBase {
public:
/**
* Create a new empty operation list
*/
OperationListBase();
/**
* Destroy this object and abort all operations
*/
~OperationListBase();
/**
* Check if the list is empty
*
* @return true if the list is empty false otherwise
*/
bool empty();
/**
* Add an operation to the list
*
* If the list was empty then call process on this
* operation
*
* @param op Operation to add
*/
void add(AsyncOp *op);
/**
* Remove an operation from the list
*
* If this was the head of the list then process the
* next element in the list.
*
* @param op Operation to remove
*/
void remove(AsyncOp *op);
/**
* Dequeue the head of the list
*
* Remove the head of the operation list without completing it
* or processing the next element. The caller must call the
* AsnycOp::complete() function of the returned object.
* Additionally process() must be called on this object
* if there are still elements in the list.
*
* @return The async op at the head of the list
*/
AsyncOp *dequeue_raw();
/**
* Abort all operations
*/
void remove_all();
/**
* Process the operation list
*
* This allow the operation at the head of the list to perform processing
*/
void process();
private:
friend class AsyncOp;
LinkedListBase _list;
};
#endif

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 "events/PolledQueue.h"
#include "events/mbed_events.h"
#include "platform/Callback.h"
PolledQueue::PolledQueue(mbed::Callback<void()> cb): _cb(cb)
{
}
PolledQueue::~PolledQueue()
{
}
void PolledQueue::dispatch()
{
core_util_critical_section_enter();
uint64_t buf[MBED_MAX_TASK_SIZE / sizeof(uint64_t)];
while (true) {
// Atomically dequeue the task and copy the callback
TaskBase *task = _list.dequeue();
if (!task) {
break;
}
MBED_ASSERT(sizeof(buf) >= task_size(task));
TaskBase::run_callback_t callback = task_start(task, (uint8_t *)buf, sizeof(buf));
// Run the callback outside the critical section
core_util_critical_section_exit();
callback((uint8_t *)buf);
core_util_critical_section_enter();
// Finish
task_finish(task);
task = NULL;
}
core_util_critical_section_exit();
}
void PolledQueue::attach(mbed::Callback<void()> cb)
{
core_util_critical_section_enter();
_cb = cb;
core_util_critical_section_exit();
}
void PolledQueue::post(TaskBase *task)
{
core_util_critical_section_enter();
bool empty = _list.head() == NULL;
_list.remove(task);
_list.enqueue(task);
if (empty && _cb) {
_cb();
}
core_util_critical_section_exit();
}
void PolledQueue::cancel(TaskBase *task)
{
core_util_critical_section_enter();
_list.remove(task);
core_util_critical_section_exit();
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 POLLED_QUEUE_H
#define POLLED_QUEUE_H
#include "events/TaskQueue.h"
#include "platform/Callback.h"
#include "LinkedList.h"
namespace events {
/** \addtogroup events */
/** PolledQueue
*
* This class is an implementation of TaskQueue which is
* processed synchronously by calls to dispatch.
* @ingroup events
*/
class PolledQueue: public TaskQueue {
public:
/** Create a PolledQueue
*
* Create an event queue.
*
* @param cb Callback called when dispatch needs to be called
*/
PolledQueue(mbed::Callback<void()> cb = NULL);
virtual ~PolledQueue();
virtual void post(TaskBase *event);
virtual void cancel(TaskBase *event);
/**
* Process all the events in this queue
*/
void dispatch();
/**
* Attach a callback indicating that this queue needs to be processed
*
* @param cb Callback called when dispatch needs to be called
*/
void attach(mbed::Callback<void()> cb);
protected:
mbed::Callback<void()> _cb;
LinkedList<TaskBase> _list;
};
}
#endif

View File

@ -0,0 +1,654 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* 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 MBED_TASK_H
#define MBED_TASK_H
#include "events/EventQueue.h"
#include "events/TaskBase.h"
#include "platform/mbed_assert.h"
#include "platform/Callback.h"
namespace events {
/** \addtogroup events */
template<typename F, typename A1 = void, typename A2 = void, typename A3 = void, typename A4 = void, typename A5 = void>
struct AllArgs;
template<typename B0>
struct AllArgs<B0> {
typedef AllArgs<B0> Self;
B0 b0;
AllArgs(B0 b0 = B0()): b0(b0) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0();
s->~Self();
}
};
typedef Operations<B0, void> ops;
};
template<typename B0, typename B1>
struct AllArgs<B0, B1> {
typedef AllArgs<B0, B1> Self;
B0 b0;
B1 b1;
AllArgs(B0 b0 = B0(), B1 b1 = B1()): b0(b0), b1(b1) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0(s->b1);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T *, R(U::*)()> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))();
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)() const> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))();
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)() volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))();
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)() const volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))();
s->~Self();
}
};
typedef Operations<B0, B1> ops;
};
template<typename B0, typename B1, typename B2>
struct AllArgs<B0, B1, B2> {
typedef AllArgs<B0, B1, B2> Self;
B0 b0;
B1 b1;
B2 b2;
AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2()): b0(b0), b1(b1), b2(b2) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0(s->b1, s->b2);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T *, R(U::*)(B2)> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2) const> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2) volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2) const volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2);
s->~Self();
}
};
typedef Operations<B0, B1> ops;
};
template<typename B0, typename B1, typename B2, typename B3>
struct AllArgs<B0, B1, B2, B3> {
typedef AllArgs<B0, B1, B2, B3> Self;
B0 b0;
B1 b1;
B2 b2;
B3 b3;
AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3()): b0(b0), b1(b1), b2(b2), b3(b3) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0(s->b1, s->b2, s->b3);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T *, R(U::*)(B2, B3)> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3) const> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3) volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3) const volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3);
s->~Self();
}
};
typedef Operations<B0, B1> ops;
};
template<typename B0, typename B1, typename B2, typename B3, typename B4>
struct AllArgs<B0, B1, B2, B3, B4> {
typedef AllArgs<B0, B1, B2, B3, B4> Self;
B0 b0;
B1 b1;
B2 b2;
B3 b3;
B4 b4;
AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0(s->b1, s->b2, s->b3, s->b4);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T *, R(U::*)(B2, B3, B4)> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4) const> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4) volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4) const volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4);
s->~Self();
}
};
typedef Operations<B0, B1> ops;
};
template<typename B0, typename B1, typename B2, typename B3, typename B4, typename B5>
struct AllArgs {
typedef AllArgs<B0, B1, B2, B3, B4, B5> Self;
B0 b0;
B1 b1;
B2 b2;
B3 b3;
B4 b4;
B5 b5;
AllArgs(B0 b0 = B0(), B1 b1 = B1(), B2 b2 = B2(), B3 b3 = B3(), B4 b4 = B4(), B5 b5 = B5()): b0(b0), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) {}
template <typename T, typename _>
struct Operations {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
s->b0(s->b1, s->b2, s->b3, s->b4, s->b5);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T *, R(U::*)(B2, B3, B4, B5)> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4, B5) const> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4, B5) volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5);
s->~Self();
}
};
template <typename T, typename R, typename U>
struct Operations<T, R(U::*)(B2, B3, B4, B5) const volatile> {
static void copy(void *_dest, void *_src)
{
new (_dest) Self(*(Self *)_src);
}
static void call(void *data)
{
Self *s = static_cast<Self *>(data);
((s->b0)->*(s->b1))(s->b2, s->b3, s->b4, s->b5);
s->~Self();
}
};
typedef Operations<B0, B1> ops;
};
template <typename F>
class Task;
template <typename R>
class Task<R()>: public TaskBase {
public:
Task(TaskQueue *q = NULL, mbed::Callback<R()> cb = mbed::Callback<R()>())
: TaskBase(q), _args(cb)
{
}
Task &operator=(mbed::Callback<R()> cb)
{
_args.b0 = cb;
return *this;
}
void call()
{
post();
}
protected:
virtual uint32_t size()
{
return sizeof(_args);
}
virtual run_callback_t start(void *data, uint32_t max_size)
{
All::ops::copy(data, (void *)&_args);
return &All::ops::call;
}
private:
typedef AllArgs<mbed::Callback<R()> > All;
All _args;
};
template <typename R, typename A0>
class Task<R(A0)>: public TaskBase {
public:
Task(TaskQueue *q = NULL, mbed::Callback<R(A0)> cb = mbed::Callback<R(A0)>())
: TaskBase(q), _args(cb)
{
}
Task &operator=(mbed::Callback<R(A0)> cb)
{
_args.b0 = cb;
return *this;
}
void call(A0 a0)
{
_args.b1 = a0;
post();
}
protected:
virtual uint32_t size()
{
return sizeof(_args);
}
virtual run_callback_t start(void *data, uint32_t max_size)
{
All::ops::copy(data, (void *)&_args);
return &All::ops::call;
}
private:
typedef AllArgs<mbed::Callback<R(A0)>, A0> All;
All _args;
};
/** Task
*
* Representation of a postable task
* @ingroup events
*/
template <typename R, typename A0, typename A1>
class Task<R(A0, A1)>: public TaskBase {
public:
/**
* Construct a new task
*
* @param q TaskQueue to post to
* @param cb Callback to run
*/
Task(TaskQueue *q = NULL, mbed::Callback<R(A0, A1)> cb = mbed::Callback<R(A0, A1)>())
: TaskBase(q), _args(cb)
{
}
/**
* Set the callback of this task
*
* @param cb Callback to run
*/
Task &operator=(mbed::Callback<R(A0, A1)> cb)
{
_args.b0 = cb;
return *this;
}
/**
* Post this task for execution
*
* The number of arguments to call should match
* the type of the callback. For example Task<void(int, int)>
* expects two integers as arguments to call, while Task<void()>
* expects no arguments.
*
* @param a0 First callback parameter
* @param a1 Second callback parameter
*/
void call(A0 a0, A1 a1)
{
_args.b1 = a0;
_args.b2 = a1;
post();
}
protected:
virtual uint32_t size()
{
return sizeof(_args);
}
virtual run_callback_t start(void *data, uint32_t max_size)
{
All::ops::copy(data, (void *)&_args);
return &All::ops::call;
}
private:
typedef AllArgs<mbed::Callback<R(A0, A1)>, A0, A1> All;
All _args;
};
}
/** @}*/
#endif

Some files were not shown because too many files have changed in this diff Show More