Split out the control endpoint logic

This commit is contained in:
Scott Shawcroft 2018-11-07 23:04:34 -08:00
parent c582c0fda9
commit 7a40ec2647
No known key found for this signature in database
GPG Key ID: FD0EDC4B6C53CA59
12 changed files with 435 additions and 282 deletions

View File

@ -45,6 +45,7 @@
// INCLUDE
//--------------------------------------------------------------------+
#include "cdc_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -287,7 +288,7 @@ tusb_error_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface
return TUSB_ERROR_NONE;
}
tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
tusb_error_t cdcd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
{
//------------- Class Specific Request -------------//
if (p_request->bmRequestType_bit.type != TUSB_REQ_TYPE_CLASS) return TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT;
@ -299,7 +300,7 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
if ( (CDC_REQUEST_GET_LINE_CODING == p_request->bRequest) || (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest) )
{
uint16_t len = tu_min16(sizeof(cdc_line_coding_t), p_request->wLength);
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, (uint8_t*) &p_cdc->line_coding, len);
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, (uint8_t*) &p_cdc->line_coding, len);
// Invoke callback
if (CDC_REQUEST_SET_LINE_CODING == p_request->bRequest)
@ -309,8 +310,6 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
}
else if (CDC_REQUEST_SET_CONTROL_LINE_STATE == p_request->bRequest )
{
dcd_control_status(rhport, p_request->bmRequestType_bit.direction); // ACK control request
// CDC PSTN v1.2 section 6.3.12
// Bit 0: Indicates if DTE is present or not.
// This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
@ -323,7 +322,7 @@ tusb_error_t cdcd_control_request_st(uint8_t rhport, tusb_control_request_t cons
}
else
{
dcd_control_stall(rhport); // stall unsupported request
return TUSB_ERROR_FAILED; // stall unsupported request
}
return TUSB_ERROR_NONE;
}

View File

@ -112,7 +112,7 @@ ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_li
void cdcd_init (void);
tusb_error_t cdcd_open (uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t cdcd_control_request_st (uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t cdcd_control_request (uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
tusb_error_t cdcd_xfer_cb (uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void cdcd_reset (uint8_t rhport);

View File

@ -46,6 +46,7 @@
//--------------------------------------------------------------------+
#include "common/tusb_common.h"
#include "hid_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -402,7 +403,7 @@ tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, u
return TUSB_ERROR_NONE;
}
tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
{
hidd_interface_t* p_hid = get_interface_by_itfnum( (uint8_t) p_request->wIndex );
TU_ASSERT(p_hid, TUSB_ERROR_FAILED);
@ -416,14 +417,17 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
if (p_request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
{
// use device control buffer
TU_ASSERT ( p_hid->desc_len <= CFG_TUD_CTRL_BUFSIZE );
memcpy(_usbd_ctrl_buf, p_hid->desc_report, p_hid->desc_len);
// TODO: Handle zero length packet.
uint16_t remaining_bytes = p_hid->desc_len - bytes_already_sent;
if (remaining_bytes > 64) {
remaining_bytes = 64;
}
memcpy(_shared_control_buffer, p_hid->desc_report + bytes_already_sent, remaining_bytes);
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, p_hid->desc_len);
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, remaining_bytes);
}else
{
dcd_control_stall(rhport);
return TUSB_ERROR_FAILED;
}
}
//------------- Class Specific Request -------------//
@ -446,11 +450,11 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
}
TU_ASSERT( xferlen > 0 );
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, p_hid->report_buf, xferlen);
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, xferlen);
}
else if ( HID_REQ_CONTROL_SET_REPORT == p_request->bRequest )
{
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, p_request->wLength);
dcd_edpt_xfer(rhport, 0, _shared_control_buffer, p_request->wLength);
// wValue = Report Type | Report ID
uint8_t const report_type = tu_u16_high(p_request->wValue);
@ -458,37 +462,35 @@ tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t cons
if ( p_hid->set_report_cb )
{
p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, _usbd_ctrl_buf, p_request->wLength);
p_hid->set_report_cb(report_id, (hid_report_type_t) report_type, _shared_control_buffer, p_request->wLength);
}
}
else if (HID_REQ_CONTROL_SET_IDLE == p_request->bRequest)
{
// TODO idle rate of report
p_hid->idle_rate = tu_u16_high(p_request->wValue);
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
else if (HID_REQ_CONTROL_GET_IDLE == p_request->bRequest)
{
// TODO idle rate of report
_usbd_ctrl_buf[0] = p_hid->idle_rate;
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
_shared_control_buffer[0] = p_hid->idle_rate;
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}
else if (HID_REQ_CONTROL_GET_PROTOCOL == p_request->bRequest )
{
_usbd_ctrl_buf[0] = 1-p_hid->boot_protocol; // 0 is Boot, 1 is Report protocol
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
_shared_control_buffer[0] = 1-p_hid->boot_protocol; // 0 is Boot, 1 is Report protocol
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}
else if (HID_REQ_CONTROL_SET_PROTOCOL == p_request->bRequest )
{
p_hid->boot_protocol = 1 - p_request->wValue; // 0 is Boot, 1 is Report protocol
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}else
{
dcd_control_stall(rhport);
return TUSB_ERROR_FAILED;
}
}else
{
dcd_control_stall(rhport);
return TUSB_ERROR_FAILED;
}
return TUSB_ERROR_NONE;
}

View File

@ -378,7 +378,7 @@ ATTR_WEAK void tud_hid_mouse_set_report_cb(uint8_t report_id, hid_report_type_t
void hidd_init(void);
tusb_error_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t hidd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t hidd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
tusb_error_t hidd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void hidd_reset(uint8_t rhport);
@ -389,5 +389,3 @@ void hidd_reset(uint8_t rhport);
#endif
#endif /* _TUSB_HID_DEVICE_H_ */

View File

@ -47,6 +47,7 @@
#include "common/tusb_common.h"
#include "msc_device.h"
#include "device/control.h"
#include "device/usbd_pvt.h"
//--------------------------------------------------------------------+
@ -146,22 +147,22 @@ tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_desc_itf,
return TUSB_ERROR_NONE;
}
tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request)
tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
{
TU_ASSERT(p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS, TUSB_ERROR_DCD_CONTROL_REQUEST_NOT_SUPPORT);
if(MSC_REQ_RESET == p_request->bRequest)
{
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
// TODO: Actually reset.
}
else if (MSC_REQ_GET_MAX_LUN == p_request->bRequest)
{
// returned MAX LUN is minus 1 by specs
_usbd_ctrl_buf[0] = CFG_TUD_MSC_MAXLUN-1;
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
_shared_control_buffer[0] = CFG_TUD_MSC_MAXLUN-1;
dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK, _shared_control_buffer, 1);
}else
{
dcd_control_stall(rhport); // stall unsupported request
return TUSB_ERROR_FAILED; // stall unsupported request
}
return TUSB_ERROR_NONE;
}

View File

@ -198,7 +198,7 @@ ATTR_WEAK bool tud_lun_capacity_cb(uint8_t lun, uint32_t* last_valid_sector, uin
void mscd_init(void);
tusb_error_t mscd_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
tusb_error_t mscd_control_request_st(uint8_t rhport, tusb_control_request_t const * p_request);
tusb_error_t mscd_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
tusb_error_t mscd_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void mscd_reset(uint8_t rhport);

252
src/device/control.c Normal file
View File

@ -0,0 +1,252 @@
/**************************************************************************/
/*!
@file usbd.c
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#define _TINY_USB_SOURCE_FILE_
#include "tusb.h"
#include "control.h"
#include "device/usbd_pvt.h"
control_t control_state;
void controld_reset(uint8_t rhport) {
control_state.current_stage = CONTROL_STAGE_SETUP;
}
void controld_init(void) {
}
// Helper to send STATUS (zero length) packet
// Note dir is value of direction bit in setup packet (i.e DATA stage direction)
static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
{
// status direction is reversed to one in the setup packet
return dcd_edpt_xfer(rhport, 1-dir, NULL, 0);
}
static inline void dcd_control_stall(uint8_t rhport)
{
dcd_edpt_stall(rhport, 0 | TUSB_DIR_IN_MASK);
}
// return len of descriptor and change pointer to descriptor's buffer
static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
{
(void) rhport;
tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low( p_request->wValue );
uint8_t const * desc_data = NULL ;
uint16_t len = 0;
switch(desc_type)
{
case TUSB_DESC_DEVICE:
desc_data = (uint8_t const *) usbd_desc_set->device;
len = sizeof(tusb_desc_device_t);
break;
case TUSB_DESC_CONFIGURATION:
desc_data = (uint8_t const *) usbd_desc_set->config;
len = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
break;
case TUSB_DESC_STRING:
// String Descriptor always uses the desc set from user
if ( desc_index < tud_desc_set.string_count )
{
desc_data = tud_desc_set.string_arr[desc_index];
TU_VERIFY( desc_data != NULL, 0 );
len = desc_data[0]; // first byte of descriptor is its size
}else
{
// out of range
/* The 0xee string is indeed a Microsoft USB extension.
* It can be used to tell Windows what driver it should use for the device !!!
*/
return 0;
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
// TODO If not highspeed capable stall this request otherwise
// return the descriptor that could work in highspeed
return 0;
break;
default: return 0;
}
TU_ASSERT( desc_data != NULL, 0);
// up to Host's length
len = tu_min16(p_request->wLength, len );
(*pp_buffer) = desc_data;
return len;
}
tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes) {
if (control_state.current_stage == CONTROL_STAGE_STATUS && xferred_bytes == 0) {
control_state.current_stage = CONTROL_STAGE_SETUP;
return TUSB_ERROR_NONE;
}
tusb_error_t error = TUSB_ERROR_NONE;
control_state.total_transferred += xferred_bytes;
tusb_control_request_t const *p_request = &control_state.current_request;
if (p_request->wLength == control_state.total_transferred || xferred_bytes < 64) {
control_state.current_stage = CONTROL_STAGE_STATUS;
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
} else {
if (TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient) {
error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, control_state.total_transferred);
} else {
error = controld_process_control_request(rhport, p_request, control_state.total_transferred);
}
}
return error;
}
// This tracks the state of a control request.
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request) {
tusb_error_t error = TUSB_ERROR_NONE;
memcpy(&control_state.current_request, p_request, sizeof(tusb_control_request_t));
if (p_request->wLength == 0) {
control_state.current_stage = CONTROL_STAGE_STATUS;
} else {
control_state.current_stage = CONTROL_STAGE_DATA;
control_state.total_transferred = 0;
}
if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
{
error = tud_control_interface_control_cb(rhport, tu_u16_low(p_request->wIndex), p_request, 0);
} else {
error = controld_process_control_request(rhport, p_request, 0);
}
if (error != TUSB_ERROR_NONE) {
dcd_control_stall(rhport); // Stall errored requests
} else if (control_state.current_stage == CONTROL_STAGE_STATUS) {
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
return error;
}
// This handles the actual request and its response.
tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent)
{
tusb_error_t error = TUSB_ERROR_NONE;
uint8_t ep_addr = 0;
if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
ep_addr |= TUSB_DIR_IN_MASK;
}
//------------- Standard Request e.g in enumeration -------------//
if( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type ) {
switch (p_request->bRequest) {
case TUSB_REQ_GET_DESCRIPTOR: {
uint8_t const * buffer = NULL;
uint16_t const len = get_descriptor(rhport, p_request, &buffer);
if (len) {
uint16_t remaining_bytes = len - bytes_already_sent;
if (remaining_bytes > 64) {
remaining_bytes = 64;
}
memcpy(_shared_control_buffer, buffer + bytes_already_sent, remaining_bytes);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, remaining_bytes);
} else {
return TUSB_ERROR_FAILED;
}
break;
}
case TUSB_REQ_GET_CONFIGURATION:
memcpy(_shared_control_buffer, &control_state.config, 1);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 1);
break;
case TUSB_REQ_SET_ADDRESS:
dcd_set_address(rhport, (uint8_t) p_request->wValue);
break;
case TUSB_REQ_SET_CONFIGURATION:
control_state.config = p_request->wValue;
tud_control_set_config_cb (rhport, control_state.config);
break;
default:
return TUSB_ERROR_FAILED;
}
} else if (p_request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_ENDPOINT &&
p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
//------------- Endpoint Request -------------//
switch (p_request->bRequest) {
case TUSB_REQ_GET_STATUS: {
uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
memcpy(_shared_control_buffer, &status, 2);
dcd_edpt_xfer(rhport, ep_addr, _shared_control_buffer, 2);
break;
}
case TUSB_REQ_CLEAR_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
break;
case TUSB_REQ_SET_FEATURE:
// only endpoint feature is halted/stalled
dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
break;
default:
return TUSB_ERROR_FAILED;
}
} else {
//------------- Unsupported Request -------------//
return TUSB_ERROR_FAILED;
}
return error;
}
#endif

95
src/device/control.h Normal file
View File

@ -0,0 +1,95 @@
/**************************************************************************/
/*!
@file usbd.h
@author hathach (tinyusb.org)
@section LICENSE
Software License Agreement (BSD License)
Copyright (c) 2013, hathach (tinyusb.org)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holders nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This file is part of the tinyusb stack.
*/
/**************************************************************************/
/** \ingroup group_usbd
* @{ */
#ifndef _TUSB_CONTROL_H_
#define _TUSB_CONTROL_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "tusb.h"
typedef enum {
CONTROL_STAGE_SETUP, // Waiting for a setup token.
CONTROL_STAGE_DATA, // In the process of sending or receiving data.
CONTROL_STAGE_STATUS // In the process of transmitting the STATUS ZLP.
} control_stage_t;
typedef struct {
control_stage_t current_stage;
tusb_control_request_t current_request;
uint16_t total_transferred;
uint8_t config;
} control_t;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _shared_control_buffer[64];
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * const p_request);
// Callback when the configuration of the device is changed.
tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number);
tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent);
//--------------------------------------------------------------------+
// INTERNAL API
//--------------------------------------------------------------------+
void controld_init(void);
tusb_error_t controld_open(uint8_t rhport, tusb_desc_interface_t const * p_interface_desc, uint16_t *p_length);
// This tracks the state of a control request.
tusb_error_t controld_process_setup_request(uint8_t rhport, tusb_control_request_t const * p_request);
// This handles the actual request and its response.
tusb_error_t controld_process_control_request(uint8_t rhport, tusb_control_request_t const * p_request, uint16_t bytes_already_sent);
tusb_error_t controld_xfer_cb(uint8_t rhport, uint8_t edpt_addr, tusb_event_t event, uint32_t xferred_bytes);
void controld_reset(uint8_t rhport);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_CONTROL_H_ */
/** @} */

View File

@ -135,22 +135,6 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
bool dcd_edpt_stalled (uint8_t rhport, uint8_t ep_addr);
//------------- Control Endpoint -------------//
bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length);
// Helper to send STATUS (zero length) packet
// Note dir is value of direction bit in setup packet (i.e DATA stage direction)
static inline bool dcd_control_status(uint8_t rhport, uint8_t dir)
{
// status direction is reversed to one in the setup packet
return dcd_control_xfer(rhport, 1-dir, NULL, 0);
}
static inline void dcd_control_stall(uint8_t rhport)
{
dcd_edpt_stall(rhport, 0 | TUSB_DIR_IN_MASK);
}
#ifdef __cplusplus
}
#endif

View File

@ -36,12 +36,15 @@
*/
/**************************************************************************/
// This top level class manages the bus state and delegates events to class-specific drivers.
#include "tusb_option.h"
#if TUSB_OPT_DEVICE_ENABLED
#define _TINY_USB_SOURCE_FILE_
#include "control.h"
#include "tusb.h"
#include "usbd.h"
#include "device/usbd_pvt.h"
@ -72,7 +75,6 @@ typedef struct {
uint8_t ep2drv[2][8];
}usbd_device_t;
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t _usbd_ctrl_buf[CFG_TUD_CTRL_BUFSIZE];
static usbd_device_t _usbd_dev;
@ -92,7 +94,8 @@ typedef struct {
void (* init ) (void);
tusb_error_t (* open ) (uint8_t rhport, tusb_desc_interface_t const * desc_intf, uint16_t* p_length);
tusb_error_t (* control_req_st ) (uint8_t rhport, tusb_control_request_t const *);
// Control request is called one or more times for a request and can queue multiple data packets.
tusb_error_t (* control_request ) (uint8_t rhport, tusb_control_request_t const *, uint16_t bytes_already_sent);
tusb_error_t (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, tusb_event_t, uint32_t);
void (* sof ) (uint8_t rhport);
void (* reset ) (uint8_t);
@ -100,52 +103,61 @@ typedef struct {
static usbd_class_driver_t const usbd_class_drivers[] =
{
{
.class_code = TUSB_CLASS_UNSPECIFIED,
.init = controld_init,
.open = NULL,
.control_request = NULL,
.xfer_cb = controld_xfer_cb,
.sof = NULL,
.reset = controld_reset
},
#if CFG_TUD_CDC
{
.class_code = TUSB_CLASS_CDC,
.init = cdcd_init,
.open = cdcd_open,
.control_req_st = cdcd_control_request_st,
.xfer_cb = cdcd_xfer_cb,
.sof = NULL,
.reset = cdcd_reset
.class_code = TUSB_CLASS_CDC,
.init = cdcd_init,
.open = cdcd_open,
.control_request = cdcd_control_request,
.xfer_cb = cdcd_xfer_cb,
.sof = NULL,
.reset = cdcd_reset
},
#endif
#if CFG_TUD_MSC
{
.class_code = TUSB_CLASS_MSC,
.init = mscd_init,
.open = mscd_open,
.control_req_st = mscd_control_request_st,
.xfer_cb = mscd_xfer_cb,
.sof = NULL,
.reset = mscd_reset
.class_code = TUSB_CLASS_MSC,
.init = mscd_init,
.open = mscd_open,
.control_request = mscd_control_request,
.xfer_cb = mscd_xfer_cb,
.sof = NULL,
.reset = mscd_reset
},
#endif
#if CFG_TUD_HID
{
.class_code = TUSB_CLASS_HID,
.init = hidd_init,
.open = hidd_open,
.control_req_st = hidd_control_request_st,
.xfer_cb = hidd_xfer_cb,
.sof = NULL,
.reset = hidd_reset
.class_code = TUSB_CLASS_HID,
.init = hidd_init,
.open = hidd_open,
.control_request = hidd_control_request,
.xfer_cb = hidd_xfer_cb,
.sof = NULL,
.reset = hidd_reset
},
#endif
#if CFG_TUD_CUSTOM_CLASS
{
.class_code = TUSB_CLASS_VENDOR_SPECIFIC,
.init = cusd_init,
.open = cusd_open,
.control_req_st = cusd_control_request_st,
.xfer_cb = cusd_xfer_cb,
.sof = NULL,
.reset = cusd_reset
.class_code = TUSB_CLASS_VENDOR_SPECIFIC,
.init = cusd_init,
.open = cusd_open,
.control_request = cusd_control_request,
.xfer_cb = cusd_xfer_cb,
.sof = NULL,
.reset = cusd_reset
},
#endif
};
@ -162,16 +174,10 @@ OSAL_TASK_DEF(_usbd_task_def, "usbd", usbd_task, CFG_TUD_TASK_PRIO, CFG_TUD_TASK
OSAL_QUEUE_DEF(_usbd_qdef, CFG_TUD_TASK_QUEUE_SZ, dcd_event_t);
static osal_queue_t _usbd_q;
/*------------- control transfer semaphore -------------*/
static osal_semaphore_def_t _usbd_sem_def;
osal_semaphore_t _usbd_ctrl_sem;
//--------------------------------------------------------------------+
// INTERNAL FUNCTION
//--------------------------------------------------------------------+
static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id);
static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number);
static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer);
//--------------------------------------------------------------------+
// APPLICATION API
@ -184,7 +190,6 @@ bool tud_mounted(void)
//--------------------------------------------------------------------+
// IMPLEMENTATION
//--------------------------------------------------------------------+
static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request);
static tusb_error_t usbd_main_st(void);
tusb_error_t usbd_init (void)
@ -201,9 +206,6 @@ tusb_error_t usbd_init (void)
_usbd_q = osal_queue_create(&_usbd_qdef);
TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
_usbd_ctrl_sem = osal_semaphore_create(&_usbd_sem_def);
TU_VERIFY(_usbd_q, TUSB_ERROR_OSAL_SEMAPHORE_FAILED);
osal_task_create(&_usbd_task_def);
//------------- class init -------------//
@ -217,6 +219,9 @@ static void usbd_reset(uint8_t rhport)
tu_varclr(&_usbd_dev);
memset(_usbd_dev.itf2drv, 0xff, sizeof(_usbd_dev.itf2drv)); // invalid mapping
memset(_usbd_dev.ep2drv , 0xff, sizeof(_usbd_dev.ep2drv )); // invalid mapping
// Always map the 0th endpoint to the control driver.
_usbd_dev.ep2drv[TUSB_DIR_IN][0] = 0;
_usbd_dev.ep2drv[TUSB_DIR_OUT][0] = 0;
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
{
@ -239,8 +244,6 @@ void usbd_task( void* param)
OSAL_TASK_END
}
extern uint32_t setup_count;
static tusb_error_t usbd_main_st(void)
{
dcd_event_t event;
@ -257,7 +260,8 @@ static tusb_error_t usbd_main_st(void)
if ( DCD_EVENT_SETUP_RECEIVED == event.event_id )
{
proc_control_request_st(event.rhport, &event.setup_received);
// Setup tokens are unique to the Control endpointso we delegate to it directly.
controld_process_setup_request(event.rhport, &event.setup_received);
}
else if (DCD_EVENT_XFER_COMPLETE == event.event_id)
{
@ -274,13 +278,11 @@ static tusb_error_t usbd_main_st(void)
{
usbd_reset(event.rhport);
osal_queue_reset(_usbd_q);
osal_semaphore_reset(_usbd_ctrl_sem);
}
else if (DCD_EVENT_UNPLUGGED == event.event_id)
{
usbd_reset(event.rhport);
osal_queue_reset(_usbd_q);
osal_semaphore_reset(_usbd_ctrl_sem);
tud_umount_cb(); // invoke callback
}
@ -307,113 +309,18 @@ static tusb_error_t usbd_main_st(void)
return err;
}
//--------------------------------------------------------------------+
// CONTROL REQUEST
//--------------------------------------------------------------------+
static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request)
{
tusb_error_t error = TUSB_ERROR_NONE;
//------------- Standard Request e.g in enumeration -------------//
if( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type )
{
if ( TUSB_REQ_GET_DESCRIPTOR == p_request->bRequest )
tusb_error_t tud_control_interface_control_cb(uint8_t rhport, uint8_t interface, tusb_control_request_t const * const p_request, uint16_t bytes_already_sent) {
if (_usbd_dev.itf2drv[ interface ] < USBD_CLASS_DRIVER_COUNT)
{
uint8_t const * buffer = NULL;
uint16_t const len = get_descriptor(rhport, p_request, &buffer);
if ( len )
{
TU_ASSERT( len <= CFG_TUD_CTRL_BUFSIZE );
memcpy(_usbd_ctrl_buf, buffer, len);
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, len);
}else
{
dcd_control_stall(rhport); // stall unsupported descriptor
}
return usbd_class_drivers[_usbd_dev.itf2drv[interface]].control_request(rhport, p_request, bytes_already_sent);
}
else if (TUSB_REQ_GET_CONFIGURATION == p_request->bRequest )
{
memcpy(_usbd_ctrl_buf, &_usbd_dev.config_num, 1);
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 1);
}
else if ( TUSB_REQ_SET_ADDRESS == p_request->bRequest )
{
dcd_set_address(rhport, (uint8_t) p_request->wValue);
#if CFG_TUSB_MCU != OPT_MCU_NRF5X // nrf5x auto handle set address, we must not return status
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
#endif
}
else if ( TUSB_REQ_SET_CONFIGURATION == p_request->bRequest )
{
proc_set_config_req(rhport, (uint8_t) p_request->wValue);
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
else
{
dcd_control_stall(rhport); // Stall unsupported request
}
}
//------------- Class/Interface Specific Request -------------//
else if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient )
{
if (_usbd_dev.itf2drv[ tu_u16_low(p_request->wIndex) ] < USBD_CLASS_DRIVER_COUNT)
{
error = usbd_class_drivers[ _usbd_dev.itf2drv[ tu_u16_low(p_request->wIndex) ] ].control_req_st(rhport, p_request);
}else
{
dcd_control_stall(rhport); // Stall unsupported request
}
}
//------------- Endpoint Request -------------//
else if ( TUSB_REQ_RCPT_ENDPOINT == p_request->bmRequestType_bit.recipient &&
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type)
{
if (TUSB_REQ_GET_STATUS == p_request->bRequest )
{
uint16_t status = dcd_edpt_stalled(rhport, tu_u16_low(p_request->wIndex)) ? 0x0001 : 0x0000;
memcpy(_usbd_ctrl_buf, &status, 2);
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, _usbd_ctrl_buf, 2);
}
else if (TUSB_REQ_CLEAR_FEATURE == p_request->bRequest )
{
// only endpoint feature is halted/stalled
dcd_edpt_clear_stall(rhport, tu_u16_low(p_request->wIndex));
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
else if (TUSB_REQ_SET_FEATURE == p_request->bRequest )
{
// only endpoint feature is halted/stalled
dcd_edpt_stall(rhport, tu_u16_low(p_request->wIndex));
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
}
else
{
dcd_control_stall(rhport); // Stall unsupported request
}
}
//------------- Unsupported Request -------------//
else
{
dcd_control_stall(rhport); // Stall unsupported request
}
if (error != TUSB_ERROR_NONE) {
dcd_control_stall(rhport); // Stall errored requests
}
return error;
return TUSB_ERROR_FAILED;
}
// Process Set Configure Request
// TODO Host (windows) can get HID report descriptor before set configured
// may need to open interface before set configured
static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number)
tusb_error_t tud_control_set_config_cb(uint8_t rhport, uint8_t config_number)
{
dcd_set_config(rhport, config_number);
@ -465,65 +372,6 @@ static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number)
return TUSB_ERROR_NONE;
}
// return len of descriptor and change pointer to descriptor's buffer
static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
{
(void) rhport;
tusb_desc_type_t const desc_type = (tusb_desc_type_t) tu_u16_high(p_request->wValue);
uint8_t const desc_index = tu_u16_low( p_request->wValue );
uint8_t const * desc_data = NULL ;
uint16_t len = 0;
switch(desc_type)
{
case TUSB_DESC_DEVICE:
desc_data = (uint8_t const *) usbd_desc_set->device;
len = sizeof(tusb_desc_device_t);
break;
case TUSB_DESC_CONFIGURATION:
desc_data = (uint8_t const *) usbd_desc_set->config;
len = ((tusb_desc_configuration_t const*) desc_data)->wTotalLength;
break;
case TUSB_DESC_STRING:
// String Descriptor always uses the desc set from user
if ( desc_index < tud_desc_set.string_count )
{
desc_data = tud_desc_set.string_arr[desc_index];
TU_VERIFY( desc_data != NULL, 0 );
len = desc_data[0]; // first byte of descriptor is its size
}else
{
// out of range
/* The 0xee string is indeed a Microsoft USB extension.
* It can be used to tell Windows what driver it should use for the device !!!
*/
return 0;
}
break;
case TUSB_DESC_DEVICE_QUALIFIER:
// TODO If not highspeed capable stall this request otherwise
// return the descriptor that could work in highspeed
return 0;
break;
default: return 0;
}
TU_ASSERT( desc_data != NULL, 0);
// up to Host's length
len = tu_min16(p_request->wLength, len );
(*pp_buffer) = desc_data;
return len;
}
// Helper marking endpoint of interface belongs to class driver
static void mark_interface_endpoint(uint8_t const* p_desc, uint16_t desc_len, uint8_t driver_id)
{
@ -569,18 +417,7 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
break;
case DCD_EVENT_XFER_COMPLETE:
if (event->xfer_complete.ep_addr == 0)
{
// only signal data stage, skip status (zero byte)
if (event->xfer_complete.len)
{
(void) event->xfer_complete.result; // TODO handle control error/stalled
osal_semaphore_post( _usbd_ctrl_sem, in_isr);
}
}else
{
osal_queue_send(_usbd_q, event, in_isr);
}
osal_queue_send(_usbd_q, event, in_isr);
TU_ASSERT(event->xfer_complete.result == DCD_XFER_SUCCESS,);
break;
@ -621,15 +458,6 @@ void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+
uint32_t usbd_control_xfer_st(uint8_t _rhport, uint8_t _dir, uint8_t* _buffer, uint16_t _len) {
uint32_t err = TUSB_ERROR_NONE;
if (_len) {
dcd_control_xfer(_rhport, _dir, (uint8_t*) _buffer, _len);
}
dcd_control_status(_rhport, _dir);
return err;
}
tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in)
{

View File

@ -45,10 +45,6 @@
extern "C" {
#endif
// for used by usbd_control_xfer_st() only, must not be used directly
extern osal_semaphore_t _usbd_ctrl_sem;
extern uint8_t _usbd_ctrl_buf[CFG_TUD_CTRL_BUFSIZE];
// Either point to tud_desc_set or usbd_auto_desc_set depending on CFG_TUD_DESC_AUTO
extern tud_desc_set_t const* usbd_desc_set;
@ -64,8 +60,6 @@ void usbd_task (void* param);
// helper to parse an pair of In and Out endpoint descriptors. They must be consecutive
tusb_error_t usbd_open_edpt_pair(uint8_t rhport, tusb_desc_endpoint_t const* p_desc_ep, uint8_t xfer_type, uint8_t* ep_out, uint8_t* ep_in);
uint32_t usbd_control_xfer_st(uint8_t _rhport, uint8_t _dir, uint8_t* _buffer, uint16_t _len);
/*------------------------------------------------------------------*/
/* Other Helpers
*------------------------------------------------------------------*/

View File

@ -203,9 +203,7 @@ static void xact_control_start(void)
bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t length)
{
(void) rhport;
osal_semaphore_wait( _usbd_ctrl_sem, OSAL_TIMEOUT_CONTROL_XFER);
if ( length )
{
@ -222,7 +220,9 @@ bool dcd_control_xfer (uint8_t rhport, uint8_t dir, uint8_t * buffer, uint16_t l
NRF_USBD->EPIN[0].MAXCNT = 0;
// Status Phase also require Easy DMA has to be free as well !!!!
NRF_USBD->TASKS_EP0STATUS = 1;
osal_semaphore_post(_usbd_ctrl_sem, false);
// The nRF doesn't interrupt on status transmit so we queue up a success response.
dcd_event_xfer_complete(0, 0, 0, DCD_XFER_SUCCESS, false);
}
return true;