Merge remote-tracking branch 'upstream/master' into uac2
This commit is contained in:
commit
142871654e
|
@ -26,4 +26,4 @@ Steps to reproduce the behavior:
|
||||||
If applicable, add screenshots, bus capture to help explain your problem.
|
If applicable, add screenshots, bus capture to help explain your problem.
|
||||||
|
|
||||||
**Log**
|
**Log**
|
||||||
Please provide the stack's log (uart/rtt/swo) where the issue occurred, best with comments to explain the actual events. To enable logging, add `LOG=2` to to the make command if building with stock examples or set `CFG_TUSB_DEBUG=2` in your tusb_config.h. More information can be found at [example's readme](/examples/readme.md)
|
Please provide the stack's log (uart/rtt/swo) where the issue occurred, best with comments to explain the actual events. To enable logging, add `LOG=2` to to the make command if building with stock examples or set `CFG_TUSB_DEBUG=2` in your tusb_config.h. More information can be found at [example's readme](/docs/getting_started.md)
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
# Getting Started #
|
# Getting Started #
|
||||||
|
|
||||||
## Get
|
## Add TinyUSB to your project
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@github.com:hathach/tinyusb.git tinyusb
|
|
||||||
```
|
|
||||||
|
|
||||||
*examples* is the folder where all the application & project files are located. There are demos for both device and hosts. For each, there are different projects for each of supported RTOS. Click to have more information on how to [build](../examples/readme.md) and run [device](../examples/device/readme.md) demos.
|
|
||||||
|
|
||||||
## Add tinyusb to your project
|
|
||||||
|
|
||||||
It is relatively simple to incorporate tinyusb to your (existing) project
|
It is relatively simple to incorporate tinyusb to your (existing) project
|
||||||
|
|
||||||
|
@ -37,6 +29,104 @@ int main(void)
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
[//]: # "\subpage md_boards_readme"
|
## Examples
|
||||||
[//]: # "\subpage md_doxygen_started_demo"
|
|
||||||
[//]: # "\subpage md_tools_readme"
|
For your convenience, TinyUSB contains a handful of examples for both host and device with/without RTOS to quickly test the functionality as well as demonstrate how API() should be used. Most examples will work on most of [the supported Boards](boards.md). Firstly we need to `git clone` if not already
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://github.com/hathach/tinyusb tinyusb
|
||||||
|
$ cd tinyusb
|
||||||
|
```
|
||||||
|
|
||||||
|
TinyUSB examples includes external repos aka submodules to provide low-level MCU peripheral's driver as well as external libraries such as FreeRTOS to compile with. Therefore we will firstly fetch those mcu driver repo by running this command in the top folder repo
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git submodule update --init --recursive
|
||||||
|
```
|
||||||
|
|
||||||
|
It will takes a bit of time due to the number of supported MCUs, luckily we only need to do this once. Or if you only want to test with a specific mcu, you could only fetch its driver submodule.
|
||||||
|
|
||||||
|
### Build
|
||||||
|
|
||||||
|
To build example, first change directory to an example folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd examples/device/cdc_msc
|
||||||
|
```
|
||||||
|
|
||||||
|
Then compile with `make BOARD=[board_name] all`, for example
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Port Selection
|
||||||
|
|
||||||
|
If a board has several ports, one port is chosen by default in the individual board.mk file. Use option `PORT=x` To choose another port. For example to select the HS port of a STM32F746Disco board, use:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=stm32f746disco PORT=1 all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Port Speed
|
||||||
|
|
||||||
|
A MCU can support multiple operational speed. By default, the example build system will use the fastest supported on the board. Use option `SPEED=full/high` e.g To force F723 operate at full instead of default high speed
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=stm32f746disco SPEED=full all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug
|
||||||
|
|
||||||
|
To compile for debugging add `DEBUG=1`, for example
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express DEBUG=1 all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Log
|
||||||
|
|
||||||
|
Should you have an issue running example and/or submitting an bug report. You could enable TinyUSB built-in debug logging with optional `LOG=`. LOG=1 will only print out error message, LOG=2 print more information with on-going events. LOG=3 or higher is not used yet.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express LOG=2 all
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logger
|
||||||
|
|
||||||
|
By default log message is printed via on-board UART which is slow and take lots of CPU time comparing to USB speed. If your board support on-board/external debugger, it would be more efficient to use it for logging. There are 2 protocols:
|
||||||
|
|
||||||
|
- `LOGGER=rtt`: use [Segger RTT protocol](https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/)
|
||||||
|
- Cons: requires jlink as the debugger.
|
||||||
|
- Pros: work with most if not all MCUs
|
||||||
|
- Software viewer is JLink RTT Viewer/Client/Logger which is bundled with JLink driver package.
|
||||||
|
- `LOGGER=swo`: Use dedicated SWO pin of ARM Cortex SWD debug header.
|
||||||
|
- Cons: only work with ARM Cortex MCUs minus M0
|
||||||
|
- Pros: should be compatible with more debugger that support SWO.
|
||||||
|
- Software viewer should be provided along with your debugger driver.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=rtt all
|
||||||
|
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=swo all
|
||||||
|
```
|
||||||
|
|
||||||
|
### Flash
|
||||||
|
|
||||||
|
`flash` target will use the default on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary, please install those support software in advance. Some board use bootloader/DFU via serial which is required to pass to make command
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express flash
|
||||||
|
$ make SERIAL=/dev/ttyACM0 BOARD=feather_nrf52840_express flash
|
||||||
|
```
|
||||||
|
|
||||||
|
Since jlink can be used with most of the boards, there is also `flash-jlink` target for your convenience.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express flash-jlink
|
||||||
|
```
|
||||||
|
|
||||||
|
Some board use uf2 bootloader for drag & drop in to mass storage device, uf2 can be generated with `uf2` target
|
||||||
|
|
||||||
|
```
|
||||||
|
$ make BOARD=feather_nrf52840_express all uf2
|
||||||
|
```
|
||||||
|
|
|
@ -1,104 +0,0 @@
|
||||||
# Examples
|
|
||||||
|
|
||||||
## Clone this repo
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git clone https://github.com/hathach/tinyusb tinyusb
|
|
||||||
$ cd tinyusb
|
|
||||||
```
|
|
||||||
|
|
||||||
## Fetch submodule MCUs drivers
|
|
||||||
|
|
||||||
TinyUSB examples includes external repos aka submodules to provide low-level MCU peripheral's driver to compile with. Therefore we will firstly fetch those mcu driver repo by running this command in the top folder repo
|
|
||||||
|
|
||||||
```
|
|
||||||
$ git submodule update --init --recursive
|
|
||||||
```
|
|
||||||
|
|
||||||
It will takes a bit of time due to the number of supported MCUs, luckily we only need to do this once. Or if you only want to test with a specific mcu, you could only update its driver submodule
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
[Here is the list of supported Boards](docs/boards.md) that should work out of the box with provided examples (hopefully).
|
|
||||||
To build example, first change directory to example folder.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cd examples/device/cdc_msc
|
|
||||||
```
|
|
||||||
|
|
||||||
Then compile with `make BOARD=[your_board] all`, for example
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express all
|
|
||||||
```
|
|
||||||
|
|
||||||
**Port Selection**
|
|
||||||
|
|
||||||
If a board has several ports, one port is chosen by default in the individual board.mk file. Use option `PORT=x` To choose another port. For example to select the HS port of a STM32F746Disco board, use:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=stm32f746disco PORT=1 all
|
|
||||||
```
|
|
||||||
|
|
||||||
**Port Speed**
|
|
||||||
|
|
||||||
A MCU can support multiple operational speed. By default, the example build system will use the fastest supported on the board. Use option `SPEED=full/high` e.g To force F723 operate at full instead of default high speed
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=stm32f746disco SPEED=full all
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug
|
|
||||||
|
|
||||||
To compile for debugging with debug symbols add `DEBUG=1`, for example
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express DEBUG=1 all
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Log
|
|
||||||
|
|
||||||
Should you have an issue running example and/or submitting an bug report. You could enable TinyUSB built-in debug logging with optional `LOG=`. LOG=1 will only print out error message, LOG=2 print more information with on-going events. LOG=3 or higher is not used yet.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express LOG=2 all
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Logger
|
|
||||||
|
|
||||||
By default log message is printed via on-board UART which is slow and take lots of CPU time comparing to USB speed. If your board support on-board/external debugger, it would be more efficient to use it for logging. There are 2 protocols:
|
|
||||||
|
|
||||||
- `LOGGER=rtt`: use [Segger RTT protocol](https://www.segger.com/products/debug-probes/j-link/technology/about-real-time-transfer/)
|
|
||||||
- Cons: requires jlink as the debugger.
|
|
||||||
- Pros: work with most if not all MCUs
|
|
||||||
- Software viewer is JLink RTT Viewer/Client/Logger which is bundled with JLink driver package.
|
|
||||||
- `LOGGER=swo`: Use dedicated SWO pin of ARM Cortex SWD debug header.
|
|
||||||
- Cons: only work with ARM Cortex MCUs minus M0
|
|
||||||
- Pros: should be compatible with more debugger that support SWO.
|
|
||||||
- Software viewer should be provided along with your debugger driver.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=rtt all
|
|
||||||
$ make BOARD=feather_nrf52840_express LOG=2 LOGGER=swo all
|
|
||||||
```
|
|
||||||
|
|
||||||
## Flash
|
|
||||||
|
|
||||||
`flash` target will use the default on-board debugger (jlink/cmsisdap/stlink/dfu) to flash the binary, please install those support software in advance. Some board use bootloader/DFU via serial which is required to pass to make command
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express flash
|
|
||||||
$ make SERIAL=/dev/ttyACM0 BOARD=feather_nrf52840_express flash
|
|
||||||
```
|
|
||||||
|
|
||||||
Since jlink can be used with most of the boards, there is also `flash-jlink` target for your convenience.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express flash-jlink
|
|
||||||
```
|
|
||||||
|
|
||||||
Some board use uf2 bootloader for drag & drop in to mass storage device, uf2 can be generated with `uf2` target
|
|
||||||
|
|
||||||
```
|
|
||||||
$ make BOARD=feather_nrf52840_express all uf2
|
|
||||||
```
|
|
|
@ -73,18 +73,29 @@ typedef struct
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
|
CFG_TUSB_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
|
||||||
|
|
||||||
static void _prep_out_transaction (uint8_t itf)
|
static void _prep_out_transaction (cdcd_interface_t* p_cdc)
|
||||||
{
|
{
|
||||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
uint8_t const rhport = TUD_OPT_RHPORT;
|
||||||
|
uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
|
||||||
// skip if previous transfer not complete
|
|
||||||
if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_out) ) return;
|
|
||||||
|
|
||||||
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
// Prepare for incoming data but only allow what we can store in the ring buffer.
|
||||||
uint16_t max_read = tu_fifo_remaining(&p_cdc->rx_ff);
|
// TODO Actually we can still carry out the transfer, keeping count of received bytes
|
||||||
if ( max_read >= sizeof(p_cdc->epout_buf) )
|
// and slowly move it to the FIFO when read().
|
||||||
|
// This pre-check reduces endpoint claiming
|
||||||
|
TU_VERIFY(available >= sizeof(p_cdc->epout_buf), );
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_out), );
|
||||||
|
|
||||||
|
// fifo can be changed before endpoint is claimed
|
||||||
|
available = tu_fifo_remaining(&p_cdc->rx_ff);
|
||||||
|
|
||||||
|
if ( available >= sizeof(p_cdc->epout_buf) ) {
|
||||||
|
usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
|
||||||
|
}else
|
||||||
{
|
{
|
||||||
usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
|
// Release endpoint since we don't make any transfer
|
||||||
|
usbd_edpt_release(rhport, p_cdc->ep_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +134,9 @@ uint32_t tud_cdc_n_available(uint8_t itf)
|
||||||
|
|
||||||
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
|
uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
|
||||||
{
|
{
|
||||||
uint32_t num_read = tu_fifo_read_n(&_cdcd_itf[itf].rx_ff, buffer, bufsize);
|
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||||
_prep_out_transaction(itf);
|
uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, bufsize);
|
||||||
|
_prep_out_transaction(p_cdc);
|
||||||
return num_read;
|
return num_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,8 +147,9 @@ bool tud_cdc_n_peek(uint8_t itf, int pos, uint8_t* chr)
|
||||||
|
|
||||||
void tud_cdc_n_read_flush (uint8_t itf)
|
void tud_cdc_n_read_flush (uint8_t itf)
|
||||||
{
|
{
|
||||||
tu_fifo_clear(&_cdcd_itf[itf].rx_ff);
|
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||||
_prep_out_transaction(itf);
|
tu_fifo_clear(&p_cdc->rx_ff);
|
||||||
|
_prep_out_transaction(p_cdc);
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
|
@ -144,11 +157,12 @@ void tud_cdc_n_read_flush (uint8_t itf)
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
|
uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
|
||||||
{
|
{
|
||||||
uint16_t ret = tu_fifo_write_n(&_cdcd_itf[itf].tx_ff, buffer, bufsize);
|
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||||
|
uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, bufsize);
|
||||||
|
|
||||||
#if 0 // TODO issue with circuitpython's REPL
|
#if 0 // TODO issue with circuitpython's REPL
|
||||||
// flush if queue more than endpoint size
|
// flush if queue more than endpoint size
|
||||||
if ( tu_fifo_count(&_cdcd_itf[itf].tx_ff) >= CFG_TUD_CDC_EP_BUFSIZE )
|
if ( tu_fifo_count(&p_cdc->tx_ff) >= CFG_TUD_CDC_EP_BUFSIZE )
|
||||||
{
|
{
|
||||||
tud_cdc_n_write_flush(itf);
|
tud_cdc_n_write_flush(itf);
|
||||||
}
|
}
|
||||||
|
@ -161,17 +175,28 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
|
||||||
{
|
{
|
||||||
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
|
||||||
|
|
||||||
// skip if previous transfer not complete yet
|
// No data to send
|
||||||
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_cdc->ep_in), 0 );
|
if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
|
||||||
|
|
||||||
uint16_t count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
|
uint8_t const rhport = TUD_OPT_RHPORT;
|
||||||
if ( count )
|
|
||||||
|
// Claim the endpoint
|
||||||
|
TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
|
||||||
|
|
||||||
|
// Pull data from FIFO
|
||||||
|
uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
|
||||||
|
|
||||||
|
if ( count && tud_cdc_n_connected(itf) )
|
||||||
{
|
{
|
||||||
TU_VERIFY( tud_cdc_n_connected(itf), 0 ); // fifo is empty if not connected
|
TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
|
||||||
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
// Note: data is dropped if terminal is not connected
|
||||||
|
usbd_edpt_release(rhport, p_cdc->ep_in);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tud_cdc_n_write_available (uint8_t itf)
|
uint32_t tud_cdc_n_write_available (uint8_t itf)
|
||||||
|
@ -233,8 +258,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
|
||||||
|
|
||||||
// Find available interface
|
// Find available interface
|
||||||
cdcd_interface_t * p_cdc = NULL;
|
cdcd_interface_t * p_cdc = NULL;
|
||||||
uint8_t cdc_id;
|
for(uint8_t cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
|
||||||
for(cdc_id=0; cdc_id<CFG_TUD_CDC; cdc_id++)
|
|
||||||
{
|
{
|
||||||
if ( _cdcd_itf[cdc_id].ep_in == 0 )
|
if ( _cdcd_itf[cdc_id].ep_in == 0 )
|
||||||
{
|
{
|
||||||
|
@ -283,7 +307,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare for incoming data
|
// Prepare for incoming data
|
||||||
_prep_out_transaction(cdc_id);
|
_prep_out_transaction(p_cdc);
|
||||||
|
|
||||||
return drv_len;
|
return drv_len;
|
||||||
}
|
}
|
||||||
|
@ -408,7 +432,7 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
|
||||||
if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
|
if (tud_cdc_rx_cb && tu_fifo_count(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
|
||||||
|
|
||||||
// prepare for OUT transaction
|
// prepare for OUT transaction
|
||||||
_prep_out_transaction(itf);
|
_prep_out_transaction(p_cdc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data sent to host, we continue to fetch from tx fifo to send.
|
// Data sent to host, we continue to fetch from tx fifo to send.
|
||||||
|
@ -421,15 +445,18 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
|
||||||
|
|
||||||
if ( 0 == tud_cdc_n_write_flush(itf) )
|
if ( 0 == tud_cdc_n_write_flush(itf) )
|
||||||
{
|
{
|
||||||
// There is no data left, a ZLP should be sent if
|
// If there is no data left, a ZLP should be sent if
|
||||||
// xferred_bytes is multiple of EP size and not zero
|
// xferred_bytes is multiple of EP size and not zero
|
||||||
// FIXME CFG_TUD_CDC_EP_BUFSIZE is not Endpoint packet size
|
// FIXME CFG_TUD_CDC_EP_BUFSIZE is not Endpoint packet size
|
||||||
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EP_BUFSIZE)) )
|
if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_CDC_EP_BUFSIZE)) )
|
||||||
|
{
|
||||||
|
if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
|
||||||
{
|
{
|
||||||
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
|
usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// nothing to do with notif endpoint for now
|
// nothing to do with notif endpoint for now
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ uint32_t tud_cdc_n_write (uint8_t itf, void const* buffer, uint32_t bu
|
||||||
static inline
|
static inline
|
||||||
uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
|
uint32_t tud_cdc_n_write_char (uint8_t itf, char ch);
|
||||||
|
|
||||||
// Write a nul-terminated string
|
// Write a null-terminated string
|
||||||
static inline
|
static inline
|
||||||
uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
|
uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str);
|
||||||
|
|
||||||
|
|
|
@ -72,18 +72,21 @@ static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
bool tud_hid_ready(void)
|
bool tud_hid_ready(void)
|
||||||
{
|
{
|
||||||
uint8_t itf = 0;
|
uint8_t const itf = 0;
|
||||||
uint8_t const ep_in = _hidd_itf[itf].ep_in;
|
uint8_t const ep_in = _hidd_itf[itf].ep_in;
|
||||||
return tud_ready() && (ep_in != 0) && usbd_edpt_ready(TUD_OPT_RHPORT, ep_in);
|
return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(TUD_OPT_RHPORT, ep_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
|
bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
|
||||||
{
|
{
|
||||||
TU_VERIFY( tud_hid_ready() );
|
uint8_t const rhport = 0;
|
||||||
|
uint8_t const itf = 0;
|
||||||
uint8_t itf = 0;
|
|
||||||
hidd_interface_t * p_hid = &_hidd_itf[itf];
|
hidd_interface_t * p_hid = &_hidd_itf[itf];
|
||||||
|
|
||||||
|
// claim endpoint
|
||||||
|
TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
|
||||||
|
|
||||||
|
// prepare data
|
||||||
if (report_id)
|
if (report_id)
|
||||||
{
|
{
|
||||||
len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
|
len = tu_min8(len, CFG_TUD_HID_EP_BUFSIZE-1);
|
||||||
|
|
|
@ -264,7 +264,7 @@ TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t idle_rate);
|
||||||
HID_LOGICAL_MAX ( 1 ) ,\
|
HID_LOGICAL_MAX ( 1 ) ,\
|
||||||
HID_REPORT_COUNT ( 16 ) ,\
|
HID_REPORT_COUNT ( 16 ) ,\
|
||||||
HID_REPORT_SIZE ( 1 ) ,\
|
HID_REPORT_SIZE ( 1 ) ,\
|
||||||
HID_INPUT ( HID_DATA | HID_ARRAY | HID_ABSOLUTE ) ,\
|
HID_INPUT ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,\
|
||||||
/* X, Y, Z, Rz (min -127, max 127 ) */ \
|
/* X, Y, Z, Rz (min -127, max 127 ) */ \
|
||||||
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
HID_USAGE_PAGE ( HID_USAGE_PAGE_DESKTOP ) ,\
|
||||||
HID_LOGICAL_MIN ( 0x81 ) ,\
|
HID_LOGICAL_MIN ( 0x81 ) ,\
|
||||||
|
|
|
@ -153,15 +153,25 @@ void midi_rx_done_cb(midid_interface_t* midi, uint8_t const* buffer, uint32_t bu
|
||||||
|
|
||||||
static uint32_t write_flush(midid_interface_t* midi)
|
static uint32_t write_flush(midid_interface_t* midi)
|
||||||
{
|
{
|
||||||
|
// No data to send
|
||||||
|
if ( !tu_fifo_count(&midi->tx_ff) ) return 0;
|
||||||
|
|
||||||
|
uint8_t const rhport = TUD_OPT_RHPORT;
|
||||||
|
|
||||||
// skip if previous transfer not complete
|
// skip if previous transfer not complete
|
||||||
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, midi->ep_in) );
|
TU_VERIFY( usbd_edpt_claim(rhport, midi->ep_in), 0 );
|
||||||
|
|
||||||
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
|
uint16_t count = tu_fifo_read_n(&midi->tx_ff, midi->epin_buf, CFG_TUD_MIDI_EP_BUFSIZE);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, midi->ep_in, midi->epin_buf, count) );
|
TU_ASSERT( usbd_edpt_xfer(rhport, midi->ep_in, midi->epin_buf, count), 0 );
|
||||||
}
|
|
||||||
return count;
|
return count;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Release endpoint since we don't make any transfer
|
||||||
|
usbd_edpt_release(rhport, midi->ep_in);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
|
uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, uint32_t bufsize)
|
||||||
|
@ -183,7 +193,7 @@ uint32_t tud_midi_n_write(uint8_t itf, uint8_t jack_id, uint8_t const* buffer, u
|
||||||
if (data == 0xf7) {
|
if (data == 0xf7) {
|
||||||
midi->write_buffer[0] = 0x5;
|
midi->write_buffer[0] = 0x5;
|
||||||
} else {
|
} else {
|
||||||
midi->write_buffer_length = 4;
|
midi->write_target_length = 4;
|
||||||
}
|
}
|
||||||
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
|
} else if ((msg >= 0x8 && msg <= 0xB) || msg == 0xE) {
|
||||||
midi->write_buffer[0] = jack_id << 4 | msg;
|
midi->write_buffer[0] = jack_id << 4 | msg;
|
||||||
|
@ -405,20 +415,25 @@ bool midid_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32
|
||||||
if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
|
if (tud_midi_rx_cb) tud_midi_rx_cb(itf);
|
||||||
|
|
||||||
// prepare for next
|
// prepare for next
|
||||||
|
// TODO for now ep_out is not used by public API therefore there is no race condition,
|
||||||
|
// and does not need to claim like ep_in
|
||||||
TU_ASSERT(usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EP_BUFSIZE), false);
|
TU_ASSERT(usbd_edpt_xfer(rhport, p_midi->ep_out, p_midi->epout_buf, CFG_TUD_MIDI_EP_BUFSIZE), false);
|
||||||
}
|
}
|
||||||
else if ( ep_addr == p_midi->ep_in )
|
else if ( ep_addr == p_midi->ep_in )
|
||||||
{
|
{
|
||||||
if (0 == write_flush(p_midi))
|
if (0 == write_flush(p_midi))
|
||||||
{
|
{
|
||||||
// There is no data left, a ZLP should be sent if
|
// If there is no data left, a ZLP should be sent if
|
||||||
// xferred_bytes is multiple of EP size and not zero
|
// xferred_bytes is multiple of EP size and not zero
|
||||||
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
|
if ( !tu_fifo_count(&p_midi->tx_ff) && xferred_bytes && (0 == (xferred_bytes % CFG_TUD_MIDI_EP_BUFSIZE)) )
|
||||||
|
{
|
||||||
|
if ( usbd_edpt_claim(rhport, p_midi->ep_in) )
|
||||||
{
|
{
|
||||||
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
|
usbd_edpt_xfer(rhport, p_midi->ep_in, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,9 +63,11 @@ typedef struct
|
||||||
{
|
{
|
||||||
volatile bool busy : 1;
|
volatile bool busy : 1;
|
||||||
volatile bool stalled : 1;
|
volatile bool stalled : 1;
|
||||||
|
volatile bool claimed : 1;
|
||||||
|
|
||||||
// TODO merge ep2drv here, 4-bit should be sufficient
|
// TODO merge ep2drv here, 4-bit should be sufficient
|
||||||
}ep_status[8][2];
|
}ep_status[8][2];
|
||||||
|
|
||||||
}usbd_device_t;
|
}usbd_device_t;
|
||||||
|
|
||||||
static usbd_device_t _usbd_dev;
|
static usbd_device_t _usbd_dev;
|
||||||
|
@ -250,6 +252,13 @@ static inline usbd_class_driver_t const * get_driver(uint8_t drvid)
|
||||||
OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
|
OSAL_QUEUE_DEF(OPT_MODE_DEVICE, _usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
|
||||||
static osal_queue_t _usbd_q;
|
static osal_queue_t _usbd_q;
|
||||||
|
|
||||||
|
// Mutex for claiming endpoint, only needed when using with preempted RTOS
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
static osal_mutex_def_t _ubsd_mutexdef;
|
||||||
|
static osal_mutex_t _usbd_mutex;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
// Prototypes
|
// Prototypes
|
||||||
//--------------------------------------------------------------------+
|
//--------------------------------------------------------------------+
|
||||||
|
@ -364,9 +373,15 @@ bool tud_init (void)
|
||||||
|
|
||||||
tu_varclr(&_usbd_dev);
|
tu_varclr(&_usbd_dev);
|
||||||
|
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
// Init device mutex
|
||||||
|
_usbd_mutex = osal_mutex_create(&_ubsd_mutexdef);
|
||||||
|
TU_ASSERT(_usbd_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Init device queue & task
|
// Init device queue & task
|
||||||
_usbd_q = osal_queue_create(&_usbd_qdef);
|
_usbd_q = osal_queue_create(&_usbd_qdef);
|
||||||
TU_ASSERT(_usbd_q != NULL);
|
TU_ASSERT(_usbd_q);
|
||||||
|
|
||||||
// Get application driver if available
|
// Get application driver if available
|
||||||
if ( usbd_app_driver_get_cb )
|
if ( usbd_app_driver_get_cb )
|
||||||
|
@ -491,6 +506,7 @@ void tud_task (void)
|
||||||
TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
|
TU_LOG2("on EP %02X with %u bytes\r\n", ep_addr, (unsigned int) event.xfer_complete.len);
|
||||||
|
|
||||||
_usbd_dev.ep_status[epnum][ep_dir].busy = false;
|
_usbd_dev.ep_status[epnum][ep_dir].busy = false;
|
||||||
|
_usbd_dev.ep_status[epnum][ep_dir].claimed = 0;
|
||||||
|
|
||||||
if ( 0 == epnum )
|
if ( 0 == epnum )
|
||||||
{
|
{
|
||||||
|
@ -1092,6 +1108,59 @@ bool usbd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep)
|
||||||
return dcd_edpt_open(rhport, desc_ep);
|
return dcd_edpt_open(rhport, desc_ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||||
|
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||||
|
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
// pre-check to help reducing mutex lock
|
||||||
|
TU_VERIFY((_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0));
|
||||||
|
|
||||||
|
osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// can only claim the endpoint if it is not busy and not claimed yet.
|
||||||
|
bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 0);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
_usbd_dev.ep_status[epnum][dir].claimed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
osal_mutex_unlock(_usbd_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr)
|
||||||
|
{
|
||||||
|
(void) rhport;
|
||||||
|
|
||||||
|
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||||
|
uint8_t const dir = tu_edpt_dir(ep_addr);
|
||||||
|
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
osal_mutex_lock(_usbd_mutex, OSAL_TIMEOUT_WAIT_FOREVER);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// can only release the endpoint if it is claimed and not busy
|
||||||
|
bool const ret = (_usbd_dev.ep_status[epnum][dir].busy == 0) && (_usbd_dev.ep_status[epnum][dir].claimed == 1);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CFG_TUSB_OS != OPT_OS_NONE
|
||||||
|
osal_mutex_unlock(_usbd_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||||
{
|
{
|
||||||
uint8_t const epnum = tu_edpt_number(ep_addr);
|
uint8_t const epnum = tu_edpt_number(ep_addr);
|
||||||
|
@ -1099,6 +1168,9 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||||
|
|
||||||
TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
|
TU_LOG2(" Queue EP %02X with %u bytes ... ", ep_addr, total_bytes);
|
||||||
|
|
||||||
|
// Attempt to transfer on a busy endpoint, sound like an race condition !
|
||||||
|
TU_ASSERT(_usbd_dev.ep_status[epnum][dir].busy == 0);
|
||||||
|
|
||||||
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
|
// Set busy first since the actual transfer can be complete before dcd_edpt_xfer() could return
|
||||||
// and usbd task can preempt and clear the busy
|
// and usbd task can preempt and clear the busy
|
||||||
_usbd_dev.ep_status[epnum][dir].busy = true;
|
_usbd_dev.ep_status[epnum][dir].busy = true;
|
||||||
|
@ -1109,7 +1181,9 @@ bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
|
||||||
return true;
|
return true;
|
||||||
}else
|
}else
|
||||||
{
|
{
|
||||||
|
// DCD error, mark endpoint as ready to allow next transfer
|
||||||
_usbd_dev.ep_status[epnum][dir].busy = false;
|
_usbd_dev.ep_status[epnum][dir].busy = false;
|
||||||
|
_usbd_dev.ep_status[epnum][dir].claimed = 0;
|
||||||
TU_LOG2("failed\r\n");
|
TU_LOG2("failed\r\n");
|
||||||
TU_BREAKPOINT();
|
TU_BREAKPOINT();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -70,6 +70,13 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr);
|
||||||
// Submit a usb transfer
|
// Submit a usb transfer
|
||||||
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
bool usbd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||||
|
|
||||||
|
// Claim an endpoint before submitting a transfer.
|
||||||
|
// If caller does not make any transfer, it must release endpoint for others.
|
||||||
|
bool usbd_edpt_claim(uint8_t rhport, uint8_t ep_addr);
|
||||||
|
|
||||||
|
// Release an endpoint without submitting a transfer
|
||||||
|
bool usbd_edpt_release(uint8_t rhport, uint8_t ep_addr);
|
||||||
|
|
||||||
// Check if endpoint transferring is complete
|
// Check if endpoint transferring is complete
|
||||||
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr);
|
bool usbd_edpt_busy(uint8_t rhport, uint8_t ep_addr);
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ static void edpt_dma_start(volatile uint32_t* reg_startep)
|
||||||
// for the DMA complete by comparing current pending DMA with number of ENDED Events
|
// for the DMA complete by comparing current pending DMA with number of ENDED Events
|
||||||
uint32_t ended = 0;
|
uint32_t ended = 0;
|
||||||
|
|
||||||
while ( _dcd.dma_pending < ((uint8_t) ended) )
|
while ( _dcd.dma_pending > ((uint8_t) ended) )
|
||||||
{
|
{
|
||||||
ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
|
ended = NRF_USBD->EVENTS_ENDISOIN + NRF_USBD->EVENTS_ENDISOOUT;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue