Merge branch 'master' into fix-issue-207

This commit is contained in:
hathach 2019-11-05 10:20:30 +07:00 committed by GitHub
commit 5209091c28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 787 additions and 577 deletions

View File

@ -123,19 +123,29 @@ Also make sure to enable endpoint specific interrupts.
##### dcd_edpt_xfer
`dcd_edpt_xfer` is responsible for configuring the peripheral to send or receive data from the host. "xfer" is short for "transfer". **This is one of the core methods you must implement for TinyUSB to work (one other is the interrupt handler).** Data from the host is the OUT direction and data to the host is IN. In other words, direction is relative to the host.
`dcd_edpt_xfer` is responsible for configuring the peripheral to send or receive data from the host. "xfer" is short for "transfer". **This is one of the core methods you must implement for TinyUSB to work (one other is the interrupt handler).** Data from the host is the OUT direction and data to the host is IN. It is used for all endpoints including the control endpoint 0. Make sure to handle the zero-length packet STATUS packet on endpoint 0 correctly. It may be a special transaction to the peripheral.
`dcd_edpt_xfer` is used for all endpoints including the control endpoint 0. Make sure to handle the zero-length packet STATUS packet on endpoint 0 correctly. It may be a special transaction to the peripheral.
Besides that, all other transactions are relatively straight-forward. The endpoint address provides the endpoint
number and direction which usually determines where to write the buffer info. The buffer and its length are usually
written to a specific location in memory and the peripheral is told the data is valid. (Maybe by writing a 1 to a
register or setting a counter register to 0 for OUT or length for IN.)
Besides that, all other transactions are relatively straight-forward. The endpoint address provides the endpoint number and direction which usually determines where to write the buffer info. The buffer and its length are usually written to a specific location in memory and the peripheral is told the data is valid. (Maybe by writing a 1 to a register or setting a counter register to 0 for OUT or length for IN.)
The transmit buffer alignment is determined by `CFG_TUSB_MEM_ALIGN`.
TODO: can we promise the buffer is word aligned?
One potential pitfall is that the buffer may be longer than the maximum endpoint size of one USB packet. Some peripherals can handle transmitting multiple USB packets for a provided buffer (like the SAMD21). Others (like the nRF52) may need each USB packet queued individually. To make this work you'll need to track some state for yourself and queue up an intermediate USB packet from the interrupt handler.
One potential pitfall is that the buffer may be longer than the maximum endpoint size of one USB
packet. Some peripherals can handle transmitting multiple USB packets for a provided buffer (like the SAMD21).
Others (like the nRF52) may need each USB packet queued individually. To make this work you'll need to track
some state for yourself and queue up an intermediate USB packet from the interrupt handler.
Once the transaction is going, the interrupt handler will notify TinyUSB of transfer completion.
During transmission, the IN data buffer is guarenteed to remain unchanged in memory until the `dcd_xfer_complete` function is called.
TODO: who handles zero-length data packets?
The dcd_edpt_xfer function must never add zero-length-packets (ZLP) on its own to a transfer. If a ZLP is required,
then it must be explicitly sent by the stack calling dcd_edpt_xfer(), by calling dcd_edpt_xfer() a second time with len=0.
For control transfers, this is automatically done in `usbd_control.c`.
At the moment, only a single buffer can be transmitted at once. There is no provision for double-buffering. new dcd_edpt_xfer() will not
be called again on the same endpoint address until the driver calls dcd_xfer_complete() (except in cases of USB resets).
##### dcd_xfer_complete

View File

@ -79,11 +79,16 @@ CFLAGS += \
# Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -Og -ggdb -DCFG_TUSB_DEBUG=2
CFLAGS += -Og -ggdb
else
ifneq ($(BOARD), spresense)
CFLAGS += -flto -Os
else
CFLAGS += -Os
ifneq ($(BOARD),spresense)
CFLAGS += -flto -Os
else
CFLAGS += -Os
endif
endif
# TUSB Logging option
ifneq ($(LOG),)
CFLAGS += -DCFG_TUSB_DEBUG=$(LOG)
endif

View File

@ -24,8 +24,8 @@ SRC_C += \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_cortex.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_rcc.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_rcc_ex.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_gpio.c
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_gpio.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_uart.c
SRC_S += \
$(ST_CMSIS)/Source/Templates/gcc/startup_stm32f070xb.s

View File

@ -37,6 +37,24 @@
#define BUTTON_PIN GPIO_PIN_13
#define BUTTON_STATE_ACTIVE 0
#define UARTx USART2
#define UART_GPIO_PORT GPIOA
#define UART_GPIO_AF GPIO_AF1_USART2
#define UART_TX_PIN GPIO_PIN_2
#define UART_RX_PIN GPIO_PIN_3
UART_HandleTypeDef UartHandle;
// enable all LED, Button, Uart, USB clock
static void all_rcc_clk_enable(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-
__HAL_RCC_GPIOC_CLK_ENABLE(); // LED
//__HAL_RCC_GPIOA_CLK_ENABLE(); // Button
//__HAL_RCC_GPIOA_CLK_ENABLE(); // Uart tx, rx
__HAL_RCC_USART2_CLK_ENABLE(); // Uart module
}
void board_init(void)
{
#if CFG_TUSB_OS == OPT_OS_NONE
@ -74,8 +92,9 @@ void board_init(void)
// Notify runtime of frequency change.
SystemCoreClockUpdate();
all_rcc_clk_enable();
// LED
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
@ -84,13 +103,30 @@ void board_init(void)
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
// Button
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
// Uart
GPIO_InitStruct.Pin = UART_TX_PIN | UART_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = UART_GPIO_AF;
HAL_GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
UartHandle.Instance = UARTx;
UartHandle.Init.BaudRate = CFG_BOARD_UART_BAUDRATE;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&UartHandle);
// Start USB clock
__HAL_RCC_USB_CLK_ENABLE();
}
@ -117,8 +153,8 @@ int board_uart_read(uint8_t* buf, int len)
int board_uart_write(void const * buf, int len)
{
(void) buf; (void) len;
return 0;
HAL_UART_Transmit(&UartHandle, (uint8_t*) buf, len, 0xffff);
return len;
}
#if CFG_TUSB_OS == OPT_OS_NONE

View File

@ -66,8 +66,8 @@
/*#define HAL_RTC_MODULE_ENABLED */
/*#define HAL_SPI_MODULE_ENABLED */
/*#define HAL_TIM_MODULE_ENABLED */
/*#define HAL_UART_MODULE_ENABLED */
#define HAL_USART_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
/*#define HAL_USART_MODULE_ENABLED */
/*#define HAL_IRDA_MODULE_ENABLED */
/*#define HAL_SMARTCARD_MODULE_ENABLED */
/*#define HAL_SMBUS_MODULE_ENABLED */

View File

@ -24,7 +24,8 @@ SRC_C += \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_cortex.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_rcc.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_rcc_ex.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_gpio.c
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_gpio.c \
$(ST_HAL_DRIVER)/Src/stm32f0xx_hal_uart.c
SRC_S += \
$(ST_CMSIS)/Source/Templates/gcc/startup_stm32f072xb.s

View File

@ -37,6 +37,24 @@
#define BUTTON_PIN GPIO_PIN_0
#define BUTTON_STATE_ACTIVE 1
#define UARTx USART1
#define UART_GPIO_PORT GPIOA
#define UART_GPIO_AF GPIO_AF1_USART1
#define UART_TX_PIN GPIO_PIN_9
#define UART_RX_PIN GPIO_PIN_10
UART_HandleTypeDef UartHandle;
// enable all LED, Button, Uart, USB clock
static void all_rcc_clk_enable(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); // USB D+, D-
__HAL_RCC_GPIOC_CLK_ENABLE(); // LED
//__HAL_RCC_GPIOA_CLK_ENABLE(); // Button
//__HAL_RCC_GPIOA_CLK_ENABLE(); // Uart tx, rx
__HAL_RCC_USART1_CLK_ENABLE(); // Uart module
}
/**
* @brief System Clock Configuration
@ -83,12 +101,11 @@ void board_init(void)
#endif
SystemClock_Config();
// Notify runtime of frequency change.
SystemCoreClockUpdate();
all_rcc_clk_enable();
// LED
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
@ -97,16 +114,32 @@ void board_init(void)
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
// Button
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
// Uart
GPIO_InitStruct.Pin = UART_TX_PIN | UART_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = UART_GPIO_AF;
HAL_GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
UartHandle.Instance = UARTx;
UartHandle.Init.BaudRate = CFG_BOARD_UART_BAUDRATE;
UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
UartHandle.Init.StopBits = UART_STOPBITS_1;
UartHandle.Init.Parity = UART_PARITY_NONE;
UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
UartHandle.Init.Mode = UART_MODE_TX_RX;
UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&UartHandle);
// USB Pins
// Configure USB DM and DP pins. This is optional, and maintained only for user guidance.
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = (GPIO_PIN_11 | GPIO_PIN_12);
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
@ -139,8 +172,8 @@ int board_uart_read(uint8_t* buf, int len)
int board_uart_write(void const * buf, int len)
{
(void) buf; (void) len;
return 0;
HAL_UART_Transmit(&UartHandle, (uint8_t*) buf, len, 0xffff);
return len;
}
#if CFG_TUSB_OS == OPT_OS_NONE
@ -170,7 +203,8 @@ void HardFault_Handler (void)
* @retval None
*/
void assert_failed(char *file, uint32_t line)
{
{
(void) file; (void) line;
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

View File

@ -66,8 +66,8 @@
/*#define HAL_RTC_MODULE_ENABLED */
/*#define HAL_SPI_MODULE_ENABLED */
/*#define HAL_TIM_MODULE_ENABLED */
/*#define HAL_UART_MODULE_ENABLED */
#define HAL_USART_MODULE_ENABLED
#define HAL_UART_MODULE_ENABLED
//#define HAL_USART_MODULE_ENABLED
/*#define HAL_IRDA_MODULE_ENABLED */
/*#define HAL_SMARTCARD_MODULE_ENABLED */
/*#define HAL_SMBUS_MODULE_ENABLED */

View File

@ -1,326 +1,327 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_HID)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "hid_device.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out; // optional Out endpoint
uint8_t boot_protocol; // Boot mouse or keyboard
bool boot_mode; // default = false (Report)
uint8_t idle_rate; // up to application to handle idle rate
uint16_t report_desc_len;
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_BUFSIZE];
tusb_hid_descriptor_hid_t const * hid_descriptor;
} hidd_interface_t;
CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
/*------------- Helpers -------------*/
static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
{
for (uint8_t i=0; i < CFG_TUD_HID; i++ )
{
if ( itf_num == _hidd_itf[i].itf_num ) return &_hidd_itf[i];
}
return NULL;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
bool tud_hid_ready(void)
{
uint8_t itf = 0;
uint8_t const ep_in = _hidd_itf[itf].ep_in;
return tud_ready() && (ep_in != 0) && usbd_edpt_ready(TUD_OPT_RHPORT, ep_in);
}
bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
{
TU_VERIFY( tud_hid_ready() );
uint8_t itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
if (report_id)
{
len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1);
p_hid->epin_buf[0] = report_id;
memcpy(p_hid->epin_buf+1, report, len);
len++;
}else
{
// If report id = 0, skip ID field
len = tu_min8(len, CFG_TUD_HID_BUFSIZE);
memcpy(p_hid->epin_buf, report, len);
}
return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
}
bool tud_hid_boot_mode(void)
{
uint8_t itf = 0;
return _hidd_itf[itf].boot_mode;
}
//--------------------------------------------------------------------+
// KEYBOARD API
//--------------------------------------------------------------------+
bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
{
hid_keyboard_report_t report;
report.modifier = modifier;
if ( keycode )
{
memcpy(report.keycode, keycode, 6);
}else
{
tu_memclr(report.keycode, 6);
}
return tud_hid_report(report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
// MOUSE APPLICATION API
//--------------------------------------------------------------------+
bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
hid_mouse_report_t report =
{
.buttons = buttons,
.x = x,
.y = y,
.wheel = vertical,
.pan = horizontal
};
return tud_hid_report(report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
// USBD-CLASS API
//--------------------------------------------------------------------+
void hidd_init(void)
{
hidd_reset(TUD_OPT_RHPORT);
}
void hidd_reset(uint8_t rhport)
{
(void) rhport;
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
{
uint8_t const *p_desc = (uint8_t const *) desc_itf;
// TODO support multiple HID interface
uint8_t const itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType);
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in));
if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
p_hid->boot_mode = false; // default mode is REPORT
p_hid->itf_num = desc_itf->bInterfaceNumber;
memcpy(&p_hid->report_desc_len, &(p_hid->hid_descriptor->wReportLength), 2);
*p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
// Prepare for output endpoint
if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
return true;
}
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
if (p_request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE)
{
return false;
}
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid);
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
{
//------------- STD Request -------------//
uint8_t const desc_type = tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low (p_request->wValue);
(void) desc_index;
if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
{
TU_VERIFY(p_hid->hid_descriptor != NULL);
TU_VERIFY(tud_control_xfer(rhport, p_request, (void*) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
}
else if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
uint8_t const * desc_report = tud_hid_descriptor_report_cb();
tud_control_xfer(rhport, p_request, (void*) desc_report, p_hid->report_desc_len);
}
else
{
return false; // stall unsupported request
}
}
else if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
{
//------------- Class Specific Request -------------//
switch( p_request->bRequest )
{
case HID_REQ_CONTROL_GET_REPORT:
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
uint16_t xferlen = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epin_buf, p_request->wLength);
TU_ASSERT( xferlen > 0 );
tud_control_xfer(rhport, p_request, p_hid->epin_buf, xferlen);
}
break;
case HID_REQ_CONTROL_SET_REPORT:
tud_control_xfer(rhport, p_request, p_hid->epout_buf, p_request->wLength);
break;
case HID_REQ_CONTROL_SET_IDLE:
p_hid->idle_rate = tu_u16_high(p_request->wValue);
if ( tud_hid_set_idle_cb )
{
// stall request if callback return false
if ( !tud_hid_set_idle_cb(p_hid->idle_rate) ) return false;
}
tud_control_status(rhport, p_request);
break;
case HID_REQ_CONTROL_GET_IDLE:
// TODO idle rate of report
tud_control_xfer(rhport, p_request, &p_hid->idle_rate, 1);
break;
case HID_REQ_CONTROL_GET_PROTOCOL:
{
uint8_t protocol = (uint8_t)(1-p_hid->boot_mode); // 0 is Boot, 1 is Report protocol
tud_control_xfer(rhport, p_request, &protocol, 1);
}
break;
case HID_REQ_CONTROL_SET_PROTOCOL:
p_hid->boot_mode = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
if (tud_hid_boot_mode_cb) tud_hid_boot_mode_cb(p_hid->boot_mode);
tud_control_status(rhport, p_request);
break;
default: return false; // stall unsupported request
}
}else
{
return false; // stall unsupported request
}
return true;
}
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool hidd_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid);
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
p_request->bRequest == HID_REQ_CONTROL_SET_REPORT)
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength);
}
return true;
}
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
// TODO support multiple HID interface
uint8_t const itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
if (ep_addr == p_hid->ep_out)
{
tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
}
return true;
}
#endif
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_HID)
//--------------------------------------------------------------------+
// INCLUDE
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "hid_device.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out; // optional Out endpoint
uint8_t boot_protocol; // Boot mouse or keyboard
bool boot_mode; // default = false (Report)
uint8_t idle_rate; // up to application to handle idle rate
uint16_t report_desc_len;
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_BUFSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_BUFSIZE];
tusb_hid_descriptor_hid_t const * hid_descriptor;
} hidd_interface_t;
CFG_TUSB_MEM_SECTION static hidd_interface_t _hidd_itf[CFG_TUD_HID];
/*------------- Helpers -------------*/
static inline hidd_interface_t* get_interface_by_itfnum(uint8_t itf_num)
{
for (uint8_t i=0; i < CFG_TUD_HID; i++ )
{
if ( itf_num == _hidd_itf[i].itf_num ) return &_hidd_itf[i];
}
return NULL;
}
//--------------------------------------------------------------------+
// APPLICATION API
//--------------------------------------------------------------------+
bool tud_hid_ready(void)
{
uint8_t itf = 0;
uint8_t const ep_in = _hidd_itf[itf].ep_in;
return tud_ready() && (ep_in != 0) && usbd_edpt_ready(TUD_OPT_RHPORT, ep_in);
}
bool tud_hid_report(uint8_t report_id, void const* report, uint8_t len)
{
TU_VERIFY( tud_hid_ready() );
uint8_t itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
if (report_id)
{
len = tu_min8(len, CFG_TUD_HID_BUFSIZE-1);
p_hid->epin_buf[0] = report_id;
memcpy(p_hid->epin_buf+1, report, len);
len++;
}else
{
// If report id = 0, skip ID field
len = tu_min8(len, CFG_TUD_HID_BUFSIZE);
memcpy(p_hid->epin_buf, report, len);
}
return usbd_edpt_xfer(TUD_OPT_RHPORT, p_hid->ep_in, p_hid->epin_buf, len);
}
bool tud_hid_boot_mode(void)
{
uint8_t itf = 0;
return _hidd_itf[itf].boot_mode;
}
//--------------------------------------------------------------------+
// KEYBOARD API
//--------------------------------------------------------------------+
bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
{
hid_keyboard_report_t report;
report.modifier = modifier;
if ( keycode )
{
memcpy(report.keycode, keycode, 6);
}else
{
tu_memclr(report.keycode, 6);
}
return tud_hid_report(report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
// MOUSE APPLICATION API
//--------------------------------------------------------------------+
bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
hid_mouse_report_t report =
{
.buttons = buttons,
.x = x,
.y = y,
.wheel = vertical,
.pan = horizontal
};
return tud_hid_report(report_id, &report, sizeof(report));
}
//--------------------------------------------------------------------+
// USBD-CLASS API
//--------------------------------------------------------------------+
void hidd_init(void)
{
hidd_reset(TUD_OPT_RHPORT);
}
void hidd_reset(uint8_t rhport)
{
(void) rhport;
tu_memclr(_hidd_itf, sizeof(_hidd_itf));
}
bool hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t *p_len)
{
uint8_t const *p_desc = (uint8_t const *) desc_itf;
// TODO support multiple HID interface
uint8_t const itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
//------------- HID descriptor -------------//
p_desc = tu_desc_next(p_desc);
p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
TU_ASSERT(HID_DESC_TYPE_HID == p_hid->hid_descriptor->bDescriptorType);
//------------- Endpoint Descriptor -------------//
p_desc = tu_desc_next(p_desc);
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in));
if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->boot_protocol = desc_itf->bInterfaceProtocol;
p_hid->boot_mode = false; // default mode is REPORT
p_hid->itf_num = desc_itf->bInterfaceNumber;
memcpy(&p_hid->report_desc_len, &(p_hid->hid_descriptor->wReportLength), 2);
*p_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
// Prepare for output endpoint
if (p_hid->ep_out) TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
return true;
}
// Handle class control request
// return false to stall control endpoint (e.g unsupported request)
bool hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request)
{
if (p_request->bmRequestType_bit.recipient != TUSB_REQ_RCPT_INTERFACE)
{
return false;
}
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid);
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
{
//------------- STD Request -------------//
uint8_t const desc_type = tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low (p_request->wValue);
(void) desc_index;
if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
{
TU_VERIFY(p_hid->hid_descriptor != NULL);
TU_VERIFY(tud_control_xfer(rhport, p_request, (void*) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
}
else if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
uint8_t const * desc_report = tud_hid_descriptor_report_cb();
tud_control_xfer(rhport, p_request, (void*) desc_report, p_hid->report_desc_len);
}
else
{
return false; // stall unsupported request
}
}
else if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
{
//------------- Class Specific Request -------------//
switch( p_request->bRequest )
{
case HID_REQ_CONTROL_GET_REPORT:
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
uint16_t xferlen = tud_hid_get_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epin_buf, p_request->wLength);
TU_ASSERT( xferlen > 0 );
tud_control_xfer(rhport, p_request, p_hid->epin_buf, xferlen);
}
break;
case HID_REQ_CONTROL_SET_REPORT:
TU_VERIFY(p_request->wLength <= sizeof(p_hid->epout_buf));
tud_control_xfer(rhport, p_request, p_hid->epout_buf, p_request->wLength);
break;
case HID_REQ_CONTROL_SET_IDLE:
p_hid->idle_rate = tu_u16_high(p_request->wValue);
if ( tud_hid_set_idle_cb )
{
// stall request if callback return false
if ( !tud_hid_set_idle_cb(p_hid->idle_rate) ) return false;
}
tud_control_status(rhport, p_request);
break;
case HID_REQ_CONTROL_GET_IDLE:
// TODO idle rate of report
tud_control_xfer(rhport, p_request, &p_hid->idle_rate, 1);
break;
case HID_REQ_CONTROL_GET_PROTOCOL:
{
uint8_t protocol = (uint8_t)(1-p_hid->boot_mode); // 0 is Boot, 1 is Report protocol
tud_control_xfer(rhport, p_request, &protocol, 1);
}
break;
case HID_REQ_CONTROL_SET_PROTOCOL:
p_hid->boot_mode = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
if (tud_hid_boot_mode_cb) tud_hid_boot_mode_cb(p_hid->boot_mode);
tud_control_status(rhport, p_request);
break;
default: return false; // stall unsupported request
}
}else
{
return false; // stall unsupported request
}
return true;
}
// Invoked when class request DATA stage is finished.
// return false to stall control endpoint (e.g Host send non-sense DATA)
bool hidd_control_complete(uint8_t rhport, tusb_control_request_t const * p_request)
{
(void) rhport;
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid);
if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
p_request->bRequest == HID_REQ_CONTROL_SET_REPORT)
{
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
uint8_t const report_id = tu_u16_low(p_request->wValue);
tud_hid_set_report_cb(report_id, (hid_report_type_t) report_type, p_hid->epout_buf, p_request->wLength);
}
return true;
}
bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
// TODO support multiple HID interface
uint8_t const itf = 0;
hidd_interface_t * p_hid = &_hidd_itf[itf];
if (ep_addr == p_hid->ep_out)
{
tud_hid_set_report_cb(0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, xferred_bytes);
TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
}
return true;
}
#endif

View File

@ -656,7 +656,7 @@ bool usbtmcd_control_request_cb(uint8_t rhport, tusb_control_request_t const * r
TU_VERIFY(request->wLength == sizeof(rsp));
TU_VERIFY(request->wIndex == usbtmc_state.ep_bulk_out);
TU_VERIFY(tud_usbtmc_check_abort_bulk_out_cb(&rsp));
TU_VERIFY(usbd_edpt_xfer(rhport, 0u, (void*)&rsp,sizeof(rsp)));
TU_VERIFY(tud_control_xfer(rhport, request, (void*)&rsp,sizeof(rsp)));
return true;
}

View File

@ -120,50 +120,20 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
//--------------------------------------------------------------------+
// Event API
// Event API (Implemented by device stack)
//--------------------------------------------------------------------+
// Called by DCD to notify device stack
extern void dcd_event_handler(dcd_event_t const * event, bool in_isr);
// helper to send bus signal event
static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
extern void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
// helper to send setup received
static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
extern void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
// helper to send transfer complete event
static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr);
//--------------------------------------------------------------------+
// Inline helper
//--------------------------------------------------------------------+
static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = eid, };
dcd_event_handler(&event, in_isr);
}
static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
memcpy(&event.setup_received, setup, 8);
dcd_event_handler(&event, in_isr);
}
static inline void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
event.xfer_complete.ep_addr = ep_addr;
event.xfer_complete.len = xferred_bytes;
event.xfer_complete.result = result;
dcd_event_handler(&event, in_isr);
}
extern void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr);
#ifdef __cplusplus
}

View File

@ -376,7 +376,7 @@ void tud_task (void)
case DCD_EVENT_SETUP_RECEIVED:
TU_LOG2(" ");
TU_LOG2_MEM(&event.setup_received, 1, 8);
TU_LOG1_MEM(&event.setup_received, 1, 8);
// Mark as connected after receiving 1st setup packet.
// But it is easier to set it every time instead of wasting time to check then set
@ -385,6 +385,7 @@ void tud_task (void)
// Process control request
if ( !process_control_request(event.rhport, &event.setup_received) )
{
TU_LOG1(" Stall EP0\r\n");
// Failed -> stall both control endpoint IN and OUT
dcd_edpt_stall(event.rhport, 0);
dcd_edpt_stall(event.rhport, 0 | TUSB_DIR_IN_MASK);
@ -404,7 +405,7 @@ void tud_task (void)
if ( 0 == epnum )
{
// control transfer DATA stage callback
TU_LOG1(" EP Addr = 0x%02X, len = %ld\r\n", ep_addr, event.xfer_complete.len);
usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
}
else
@ -588,7 +589,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// stall control endpoint if driver return false
usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_complete);
TU_LOG2(" %s control request\r\n", _usbd_driver_str[drvid]);
TU_ASSERT(usbd_class_drivers[drvid].control_request != NULL &&
TU_VERIFY(usbd_class_drivers[drvid].control_request != NULL &&
usbd_class_drivers[drvid].control_request(rhport, p_request));
break;
}
@ -598,7 +599,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
// stall control endpoint if driver return false
usbd_control_set_complete_callback(usbd_class_drivers[drvid].control_complete);
TU_LOG2(" %s control request\r\n", _usbd_driver_str[drvid]);
TU_ASSERT(usbd_class_drivers[drvid].control_request != NULL &&
TU_VERIFY(usbd_class_drivers[drvid].control_request != NULL &&
usbd_class_drivers[drvid].control_request(rhport, p_request));
}
}
@ -785,6 +786,8 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
case TUSB_DESC_CONFIGURATION:
{
tusb_desc_configuration_t const* desc_config = (tusb_desc_configuration_t const*) tud_descriptor_configuration_cb(desc_index);
TU_ASSERT(desc_config);
uint16_t total_len;
memcpy(&total_len, &desc_config->wTotalLength, 2); // possibly mis-aligned memory
@ -867,10 +870,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
break;
case DCD_EVENT_XFER_COMPLETE:
// skip zero-length control status complete event, should DCD notify us.
// TODO could cause issue with actual zero length data used by class such as DFU
if ( (0 == tu_edpt_number(event->xfer_complete.ep_addr)) && (event->xfer_complete.len == 0) ) break;
osal_queue_send(_usbd_q, event, in_isr);
TU_ASSERT(event->xfer_complete.result == XFER_RESULT_SUCCESS,);
break;
@ -884,6 +883,31 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
}
}
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = eid, };
dcd_event_handler(&event, in_isr);
}
void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SETUP_RECEIVED };
memcpy(&event.setup_received, setup, 8);
dcd_event_handler(&event, in_isr);
}
void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_XFER_COMPLETE };
event.xfer_complete.ep_addr = ep_addr;
event.xfer_complete.len = xferred_bytes;
event.xfer_complete.result = result;
dcd_event_handler(&event, in_isr);
}
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+

View File

@ -1,159 +1,188 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#include "tusb.h"
#include "device/usbd_pvt.h"
#include "dcd.h"
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80
};
typedef struct
{
tusb_control_request_t request;
void* buffer;
uint16_t total_len;
uint16_t total_transferred;
bool (*complete_cb) (uint8_t, tusb_control_request_t const *);
} usbd_control_xfer_t;
static usbd_control_xfer_t _control_state;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
void usbd_control_reset (uint8_t rhport)
{
(void) rhport;
tu_varclr(&_control_state);
}
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
{
// status direction is reversed to one in the setup packet
return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0);
}
// Each transaction is up to endpoint0's max packet size
static bool start_control_data_xact(uint8_t rhport)
{
uint16_t const xact_len = tu_min16(_control_state.total_len - _control_state.total_transferred, CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_IN )
{
ep_addr = EDPT_CTRL_IN;
memcpy(_usbd_ctrl_buf, _control_state.buffer, xact_len);
}
return dcd_edpt_xfer(rhport, ep_addr, _usbd_ctrl_buf, xact_len);
}
// TODO may find a better way
void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) )
{
_control_state.complete_cb = fp;
}
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
{
_control_state.request = (*request);
_control_state.buffer = buffer;
_control_state.total_len = tu_min16(len, request->wLength);
_control_state.total_transferred = 0;
if ( len )
{
TU_ASSERT(buffer);
// Data stage
TU_ASSERT( start_control_data_xact(rhport) );
}else
{
// Status stage
TU_ASSERT( tud_control_status(rhport, request) );
}
return true;
}
// callback when a transaction complete on DATA stage of control endpoint
bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
(void) ep_addr;
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
{
TU_VERIFY(_control_state.buffer);
memcpy(_control_state.buffer, _usbd_ctrl_buf, xferred_bytes);
}
_control_state.total_transferred += xferred_bytes;
_control_state.buffer = ((uint8_t*)_control_state.buffer) + xferred_bytes;
if ( _control_state.total_len == _control_state.total_transferred || xferred_bytes < CFG_TUD_ENDPOINT0_SIZE )
{
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
if ( _control_state.complete_cb )
{
is_ok = _control_state.complete_cb(rhport, &_control_state.request);
}
if ( is_ok )
{
// Send status
TU_ASSERT( tud_control_status(rhport, &_control_state.request) );
}else
{
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
}
}
else
{
// More data to transfer
TU_ASSERT( start_control_data_xact(rhport) );
}
return true;
}
#endif
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#include "tusb.h"
#include "device/usbd_pvt.h"
#include "dcd.h"
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80
};
typedef struct
{
tusb_control_request_t request;
uint8_t* buffer;
uint16_t data_len;
uint16_t total_xferred;
bool (*complete_cb) (uint8_t, tusb_control_request_t const *);
} usbd_control_xfer_t;
static usbd_control_xfer_t _ctrl_xfer;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t _usbd_ctrl_buf[CFG_TUD_ENDPOINT0_SIZE];
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
static inline bool _status_stage_xact(uint8_t rhport, tusb_control_request_t const * request)
{
// status direction is reversed to one in the setup packet
return dcd_edpt_xfer(rhport, request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN, NULL, 0);
}
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
{
_ctrl_xfer.request = (*request);
_ctrl_xfer.buffer = NULL;
_ctrl_xfer.total_xferred = 0;
_ctrl_xfer.data_len = 0;
return _status_stage_xact(rhport, request);
}
// Transfer an transaction in Data Stage
// Each transaction has up to Endpoint0's max packet size.
// This function can also transfer an zero-length packet
static bool _data_stage_xact(uint8_t rhport)
{
uint16_t const xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_SIZE);
uint8_t ep_addr = EDPT_CTRL_OUT;
if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
{
ep_addr = EDPT_CTRL_IN;
if ( xact_len ) memcpy(_usbd_ctrl_buf, _ctrl_xfer.buffer, xact_len);
}
return dcd_edpt_xfer(rhport, ep_addr, xact_len ? _usbd_ctrl_buf : NULL, xact_len);
}
bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, void* buffer, uint16_t len)
{
_ctrl_xfer.request = (*request);
_ctrl_xfer.buffer = (uint8_t*) buffer;
_ctrl_xfer.total_xferred = 0;
_ctrl_xfer.data_len = tu_min16(len, request->wLength);
if ( _ctrl_xfer.data_len )
{
TU_ASSERT(buffer);
// Data stage
TU_ASSERT( _data_stage_xact(rhport) );
}else
{
// Status stage
TU_ASSERT( _status_stage_xact(rhport, request) );
}
return true;
}
//--------------------------------------------------------------------+
// USBD API
//--------------------------------------------------------------------+
void usbd_control_reset (uint8_t rhport)
{
(void) rhport;
tu_varclr(&_ctrl_xfer);
}
// TODO may find a better way
void usbd_control_set_complete_callback( bool (*fp) (uint8_t, tusb_control_request_t const * ) )
{
_ctrl_xfer.complete_cb = fp;
}
// callback when a transaction complete on DATA stage of control endpoint
bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) result;
// Endpoint Address is opposite to direction bit, this is Status Stage complete event
if ( tu_edpt_dir(ep_addr) != _ctrl_xfer.request.bmRequestType_bit.direction )
{
TU_ASSERT(0 == xferred_bytes);
return true;
}
if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
{
TU_VERIFY(_ctrl_xfer.buffer);
memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
}
_ctrl_xfer.total_xferred += xferred_bytes;
_ctrl_xfer.buffer += xferred_bytes;
// Data Stage is complete when all request's length are transferred or
// a short packet is sent including zero-length packet.
if ( (_ctrl_xfer.request.wLength == _ctrl_xfer.total_xferred) || xferred_bytes < CFG_TUD_ENDPOINT0_SIZE )
{
// DATA stage is complete
bool is_ok = true;
// invoke complete callback if set
// callback can still stall control in status phase e.g out data does not make sense
if ( _ctrl_xfer.complete_cb )
{
is_ok = _ctrl_xfer.complete_cb(rhport, &_ctrl_xfer.request);
}
if ( is_ok )
{
// Send status
TU_ASSERT( _status_stage_xact(rhport, &_ctrl_xfer.request) );
}else
{
// Stall both IN and OUT control endpoint
dcd_edpt_stall(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall(rhport, EDPT_CTRL_IN);
}
}
else
{
// More data to transfer
TU_ASSERT( _data_stage_xact(rhport) );
}
return true;
}
#endif

View File

@ -198,6 +198,8 @@ static inline bool osal_queue_send(osal_queue_t const qhdl, void const * data, b
_osal_q_unlock(qhdl);
}
TU_ASSERT(success);
return success;
}

View File

@ -38,9 +38,15 @@ TEST_FILE("usbd_control.c")
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
enum
{
EDPT_CTRL_OUT = 0x00,
EDPT_CTRL_IN = 0x80
};
uint8_t const rhport = 0;
tusb_desc_device_t const desc_device =
tusb_desc_device_t const data_desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
@ -65,6 +71,12 @@ tusb_desc_device_t const desc_device =
.bNumConfigurations = 0x01
};
uint8_t const data_desc_configuration[] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(0, 0, TUD_CONFIG_DESC_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
tusb_control_request_t const req_get_desc_device =
{
.bmRequestType = 0x80,
@ -74,20 +86,29 @@ tusb_control_request_t const req_get_desc_device =
.wLength = 64
};
tusb_control_request_t const req_get_desc_configuration =
{
.bmRequestType = 0x80,
.bRequest = TUSB_REQ_GET_DESCRIPTOR,
.wValue = (TUSB_DESC_CONFIGURATION << 8),
.wIndex = 0x0000,
.wLength = 256
};
uint8_t const* desc_device;
uint8_t const* desc_configuration;
//--------------------------------------------------------------------+
//
//--------------------------------------------------------------------+
uint8_t const * ptr_desc_device;
uint8_t const * tud_descriptor_device_cb(void)
{
return ptr_desc_device;
return desc_device;
}
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
TEST_FAIL();
return NULL;
return desc_configuration;
}
uint16_t const* tud_descriptor_string_cb(uint8_t index)
@ -106,8 +127,6 @@ void setUp(void)
dcd_init_Expect(rhport);
tusb_init();
}
ptr_desc_device = (uint8_t const *) &desc_device;
}
void tearDown(void)
@ -115,25 +134,103 @@ void tearDown(void)
}
//--------------------------------------------------------------------+
//
// Get Descriptor
//--------------------------------------------------------------------+
//------------- Device -------------//
void test_usbd_get_device_descriptor(void)
{
desc_device = (uint8_t const *) &data_desc_device;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_device, false);
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*)&desc_device, sizeof(tusb_desc_device_t), sizeof(tusb_desc_device_t), true);
// data
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*)&data_desc_device, sizeof(tusb_desc_device_t), sizeof(tusb_desc_device_t), true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, sizeof(tusb_desc_device_t), 0, false);
// status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
tud_task();
}
void test_usbd_get_device_descriptor_null(void)
{
ptr_desc_device = NULL;
desc_device = NULL;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_device, false);
dcd_edpt_stall_Expect(rhport, 0);
dcd_edpt_stall_Expect(rhport, 0x80);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_IN);
tud_task();
}
//------------- Configuration -------------//
void test_usbd_get_configuration_descriptor(void)
{
desc_configuration = data_desc_configuration;
uint16_t total_len = ((tusb_desc_configuration_t const*) data_desc_configuration)->wTotalLength;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
// data
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, 0x80, (uint8_t*) data_desc_configuration, total_len, total_len, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, total_len, 0, false);
// status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
tud_task();
}
void test_usbd_get_configuration_descriptor_null(void)
{
desc_configuration = NULL;
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_OUT);
dcd_edpt_stall_Expect(rhport, EDPT_CTRL_IN);
tud_task();
}
//--------------------------------------------------------------------+
// Control ZLP
//--------------------------------------------------------------------+
void test_usbd_control_in_zlp(void)
{
// 128 byte total len, with EP0 size = 64, and request length = 256
// ZLP must be return
uint8_t zlp_desc_configuration[CFG_TUD_ENDOINT0_SIZE*2] =
{
// Interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(0, 0, CFG_TUD_ENDOINT0_SIZE*2, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
desc_configuration = zlp_desc_configuration;
// request, then 1st, 2nd xact + ZLP + status
dcd_event_setup_received(rhport, (uint8_t*) &req_get_desc_configuration, false);
// 1st transaction
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN,
zlp_desc_configuration, CFG_TUD_ENDOINT0_SIZE, CFG_TUD_ENDOINT0_SIZE, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDOINT0_SIZE, 0, false);
// 2nd transaction
dcd_edpt_xfer_ExpectWithArrayAndReturn(rhport, EDPT_CTRL_IN,
zlp_desc_configuration + CFG_TUD_ENDOINT0_SIZE, CFG_TUD_ENDOINT0_SIZE, CFG_TUD_ENDOINT0_SIZE, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, CFG_TUD_ENDOINT0_SIZE, 0, false);
// Expect Zero length Packet
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_IN, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_IN, 0, 0, false);
// Status
dcd_edpt_xfer_ExpectAndReturn(rhport, EDPT_CTRL_OUT, NULL, 0, true);
dcd_event_xfer_complete(rhport, EDPT_CTRL_OUT, 0, 0, false);
tud_task();
}

View File

@ -44,15 +44,15 @@
#endif
#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED)
#else
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE
#endif
#define CFG_TUSB_OS OPT_OS_NONE
#define CFG_TUSB_OS OPT_OS_NONE
// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
#define CFG_TUSB_DEBUG 0
#define CFG_TUSB_DEBUG 0
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
@ -66,13 +66,14 @@
#endif
#ifndef CFG_TUSB_MEM_ALIGN
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
#endif
//--------------------------------------------------------------------
// DEVICE CONFIGURATION
//--------------------------------------------------------------------
#define CFG_TUD_TASK_QUEUE_SZ 100
#define CFG_TUD_ENDOINT0_SIZE 64
//------------- CLASS -------------//