mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #9768 from c1728p9/feature-hal-spec-usb-device
Bring USB Feature branch into masterpull/9856/head
commit
4b13c8a212
|
@ -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
|
@ -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)
|
|
@ -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).
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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));
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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*).
|
|
@ -0,0 +1,6 @@
|
|||
# Zadig device configuration
|
||||
# Mbed OS USB test device -- basic tests
|
||||
[device]
|
||||
Description = "MBED TEST DEVICE"
|
||||
VID = 0x0D28
|
||||
PID = 0x0205
|
|
@ -0,0 +1,6 @@
|
|||
# Zadig device configuration
|
||||
# Mbed OS USB test device -- endpoint tests
|
||||
[device]
|
||||
Description = "USB DEVICE"
|
||||
VID = 0x0D28
|
||||
PID = 0x0206
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
/** @}*/
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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*/
|
|
@ -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
|
@ -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
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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
Loading…
Reference in New Issue