2014-03-12 14:01:38 +07:00
|
|
|
/**************************************************************************/
|
|
|
|
/*!
|
|
|
|
@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 MODE_DEVICE_SUPPORTED
|
|
|
|
|
|
|
|
#define _TINY_USB_SOURCE_FILE_
|
|
|
|
|
|
|
|
#include "tusb.h"
|
2018-03-11 12:31:24 +07:00
|
|
|
#include "usbd.h"
|
2018-03-22 14:15:16 +07:00
|
|
|
#include "device/usbd_pvt.h"
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
#define USBD_TASK_QUEUE_DEPTH 16
|
|
|
|
|
|
|
|
#ifndef CFG_TUD_TASK_STACKSIZE
|
|
|
|
#define CFG_TUD_TASK_STACKSIZE 150
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef CFG_TUD_TASK_PRIO
|
|
|
|
#define CFG_TUD_TASK_PRIO 0
|
|
|
|
#endif
|
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// MACRO CONSTANT TYPEDEF
|
|
|
|
//--------------------------------------------------------------------+
|
2018-03-23 14:57:17 +07:00
|
|
|
typedef struct {
|
2018-05-14 13:02:01 +07:00
|
|
|
uint8_t class_code;
|
|
|
|
|
2018-06-16 12:30:10 +07:00
|
|
|
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 *);
|
|
|
|
tusb_error_t (* xfer_cb ) (uint8_t rhport, uint8_t ep_addr, tusb_event_t, uint32_t);
|
|
|
|
void (* sof ) (uint8_t rhport);
|
|
|
|
void (* close ) (uint8_t);
|
2018-03-23 14:57:17 +07:00
|
|
|
} usbd_class_driver_t;
|
|
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
USBD_INTERFACE_NUM_MAX = 16 // USB specs specify up to 16 endpoints per device
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
volatile uint8_t state;
|
|
|
|
uint8_t config_num;
|
|
|
|
|
|
|
|
uint8_t interface2class[USBD_INTERFACE_NUM_MAX]; // determine interface number belongs to which class
|
|
|
|
}usbd_device_info_t;
|
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
//--------------------------------------------------------------------+
|
2018-05-17 19:19:55 +07:00
|
|
|
// Class & Device Driver
|
2014-03-12 14:01:38 +07:00
|
|
|
//--------------------------------------------------------------------+
|
2018-04-12 13:23:52 +07:00
|
|
|
CFG_TUSB_ATTR_USBRAM CFG_TUSB_MEM_ALIGN uint8_t usbd_enum_buffer[CFG_TUD_ENUM_BUFFER_SIZE];
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
tud_desc_init_t _usbd_descs[CONTROLLER_DEVICE_NUMBER];
|
|
|
|
usbd_device_info_t usbd_devices[CONTROLLER_DEVICE_NUMBER];
|
|
|
|
|
2014-03-14 17:18:05 +07:00
|
|
|
static usbd_class_driver_t const usbd_class_drivers[] =
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
#if CFG_TUD_CDC
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-06-16 12:30:10 +07:00
|
|
|
.class_code = TUSB_CLASS_CDC,
|
|
|
|
.init = cdcd_init,
|
|
|
|
.open = cdcd_open,
|
|
|
|
.control_req_st = cdcd_control_request_st,
|
|
|
|
.xfer_cb = cdcd_xfer_cb,
|
|
|
|
.sof = cdcd_sof,
|
|
|
|
.close = cdcd_close
|
2014-03-12 14:01:38 +07:00
|
|
|
},
|
2014-03-14 17:18:05 +07:00
|
|
|
#endif
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-14 13:02:01 +07:00
|
|
|
#if DEVICE_CLASS_HID
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-06-16 12:30:10 +07:00
|
|
|
.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,
|
|
|
|
.close = hidd_close
|
2014-03-12 14:01:38 +07:00
|
|
|
},
|
2014-03-14 17:18:05 +07:00
|
|
|
#endif
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-14 13:02:01 +07:00
|
|
|
#if CFG_TUD_MSC
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-06-16 12:30:10 +07:00
|
|
|
.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,
|
|
|
|
.close = mscd_close
|
|
|
|
},
|
|
|
|
#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,
|
|
|
|
.close = cusd_close
|
2014-03-12 14:01:38 +07:00
|
|
|
},
|
2014-03-14 17:18:05 +07:00
|
|
|
#endif
|
2014-03-12 14:01:38 +07:00
|
|
|
};
|
|
|
|
|
2014-03-18 16:58:24 +07:00
|
|
|
enum { USBD_CLASS_DRIVER_COUNT = sizeof(usbd_class_drivers) / sizeof(usbd_class_driver_t) };
|
2014-03-14 17:18:05 +07:00
|
|
|
|
2018-03-23 12:32:40 +07:00
|
|
|
//tusb_desc_device_qualifier_t _device_qual =
|
2018-03-14 22:01:16 +07:00
|
|
|
//{
|
2018-03-23 12:32:40 +07:00
|
|
|
// .bLength = sizeof(tusb_desc_device_qualifier_t),
|
2018-03-14 22:01:16 +07:00
|
|
|
// .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
|
|
|
// .bcdUSB = 0x0200,
|
|
|
|
// .bDeviceClass =
|
|
|
|
//};
|
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
//--------------------------------------------------------------------+
|
2018-05-17 19:19:55 +07:00
|
|
|
// DCD Event
|
2014-03-12 14:01:38 +07:00
|
|
|
//--------------------------------------------------------------------+
|
2015-05-01 18:45:22 +07:00
|
|
|
typedef enum
|
|
|
|
{
|
2018-06-22 00:40:16 +07:00
|
|
|
USBD_EVT_SETUP_RECEIVED = 1,
|
|
|
|
USBD_EVT_XFER_DONE,
|
|
|
|
USBD_EVT_SOF,
|
|
|
|
|
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
}usbd_eventid_t;
|
|
|
|
|
|
|
|
typedef struct ATTR_ALIGNED(4)
|
|
|
|
{
|
2018-03-23 12:17:47 +07:00
|
|
|
uint8_t rhport;
|
2014-03-12 14:01:38 +07:00
|
|
|
uint8_t event_id;
|
|
|
|
uint8_t sub_event_id;
|
|
|
|
uint8_t reserved;
|
|
|
|
|
|
|
|
union {
|
2018-03-08 14:38:06 +07:00
|
|
|
tusb_control_request_t setup_received;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
|
|
|
struct { // USBD_EVENTID_XFER_DONE
|
2018-03-17 02:17:40 +07:00
|
|
|
uint8_t ep_addr;
|
2014-03-12 14:01:38 +07:00
|
|
|
uint32_t xferred_byte;
|
|
|
|
}xfer_done;
|
|
|
|
};
|
2014-03-14 17:18:05 +07:00
|
|
|
} usbd_task_event_t;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-03-28 14:16:14 +07:00
|
|
|
VERIFY_STATIC(sizeof(usbd_task_event_t) <= 12, "size is not correct");
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-17 20:04:52 +07:00
|
|
|
OSAL_TASK_DEF(_usbd_task_def, "usbd", usbd_task, CFG_TUD_TASK_PRIO, CFG_TUD_TASK_STACKSIZE);
|
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
/*------------- event queue -------------*/
|
|
|
|
OSAL_QUEUE_DEF(_usbd_qdef, USBD_TASK_QUEUE_DEPTH, usbd_task_event_t);
|
|
|
|
static osal_queue_t _usbd_q;
|
2018-02-28 14:21:31 +07:00
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
/*------------- control transfer semaphore -------------*/
|
|
|
|
static osal_semaphore_def_t _usbd_sem_def;
|
|
|
|
/*static*/ osal_semaphore_t _usbd_ctrl_sem;
|
2018-02-28 14:21:31 +07:00
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// INTERNAL FUNCTION
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
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);
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// APPLICATION API
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
bool tud_n_mounted(uint8_t rhport)
|
|
|
|
{
|
|
|
|
return usbd_devices[rhport].state == TUSB_DEVICE_STATE_CONFIGURED;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tud_n_set_descriptors(uint8_t rhport, tud_desc_init_t const* desc_cfg)
|
|
|
|
{
|
|
|
|
_usbd_descs[rhport] = *desc_cfg;
|
|
|
|
return true;
|
|
|
|
}
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2014-03-31 13:12:51 +07:00
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// IMPLEMENTATION
|
|
|
|
//--------------------------------------------------------------------+
|
2018-03-23 12:17:47 +07:00
|
|
|
static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request);
|
2018-03-30 16:24:55 +07:00
|
|
|
static tusb_error_t usbd_main_st(void);
|
2014-03-31 13:12:51 +07:00
|
|
|
|
|
|
|
tusb_error_t usbd_init (void)
|
|
|
|
{
|
2018-04-14 14:08:48 +07:00
|
|
|
#if (CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE)
|
2018-03-28 13:47:58 +07:00
|
|
|
dcd_init(0);
|
2018-03-02 22:46:36 +07:00
|
|
|
#endif
|
|
|
|
|
2018-04-14 14:08:48 +07:00
|
|
|
#if (CFG_TUSB_RHPORT1_MODE & OPT_MODE_DEVICE)
|
2018-03-28 13:47:58 +07:00
|
|
|
dcd_init(1);
|
2018-03-02 22:46:36 +07:00
|
|
|
#endif
|
|
|
|
|
2014-03-31 13:12:51 +07:00
|
|
|
//------------- Task init -------------//
|
2018-05-17 19:19:55 +07:00
|
|
|
_usbd_q = osal_queue_create(&_usbd_qdef);
|
2018-03-30 16:57:18 +07:00
|
|
|
VERIFY(_usbd_q, TUSB_ERROR_OSAL_QUEUE_FAILED);
|
2014-03-31 13:12:51 +07:00
|
|
|
|
2018-05-17 19:19:55 +07:00
|
|
|
_usbd_ctrl_sem = osal_semaphore_create(&_usbd_sem_def);
|
2018-03-30 16:57:18 +07:00
|
|
|
VERIFY(_usbd_q, TUSB_ERROR_OSAL_SEMAPHORE_FAILED);
|
2014-03-31 13:12:51 +07:00
|
|
|
|
2018-05-17 20:04:52 +07:00
|
|
|
osal_task_create(&_usbd_task_def);
|
2018-02-28 14:21:31 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
//------------- Core init -------------//
|
|
|
|
arrclr_( _usbd_descs );
|
2014-03-31 13:12:51 +07:00
|
|
|
|
|
|
|
//------------- class init -------------//
|
2018-05-14 13:02:01 +07:00
|
|
|
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
|
2014-03-31 13:12:51 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
usbd_class_drivers[i].init();
|
2014-03-31 13:12:51 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return TUSB_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// To enable the TASK_ASSERT style (quick return on false condition) in a real RTOS, a task must act as a wrapper
|
|
|
|
// and is used mainly to call subtasks. Within a subtask return statement can be called freely, the task with
|
|
|
|
// forever loop cannot have any return at all.
|
2018-02-28 14:21:31 +07:00
|
|
|
void usbd_task( void* param)
|
2014-03-31 13:12:51 +07:00
|
|
|
{
|
2018-02-28 14:21:31 +07:00
|
|
|
(void) param;
|
2014-03-31 13:12:51 +07:00
|
|
|
|
2018-03-01 12:14:44 +07:00
|
|
|
OSAL_TASK_BEGIN
|
2018-03-30 16:24:55 +07:00
|
|
|
usbd_main_st();
|
2018-03-01 12:14:44 +07:00
|
|
|
OSAL_TASK_END
|
2014-03-31 13:12:51 +07:00
|
|
|
}
|
|
|
|
|
2018-03-30 16:24:55 +07:00
|
|
|
static tusb_error_t usbd_main_st(void)
|
2014-03-31 13:12:51 +07:00
|
|
|
{
|
2014-04-09 22:29:38 +07:00
|
|
|
static usbd_task_event_t event;
|
2014-03-31 13:12:51 +07:00
|
|
|
|
|
|
|
OSAL_SUBTASK_BEGIN
|
|
|
|
|
2018-04-13 16:10:42 +07:00
|
|
|
tusb_error_t err;
|
|
|
|
err = TUSB_ERROR_NONE;
|
2014-03-31 13:12:51 +07:00
|
|
|
|
2014-04-24 23:40:28 +07:00
|
|
|
memclr_(&event, sizeof(usbd_task_event_t));
|
2018-03-08 14:38:06 +07:00
|
|
|
|
2018-04-13 16:10:42 +07:00
|
|
|
osal_queue_receive(_usbd_q, &event, OSAL_TIMEOUT_WAIT_FOREVER, &err);
|
2014-03-31 13:12:51 +07:00
|
|
|
|
2018-06-22 00:40:16 +07:00
|
|
|
if ( USBD_EVT_SETUP_RECEIVED == event.event_id )
|
2014-03-31 13:12:51 +07:00
|
|
|
{
|
2018-04-13 16:10:42 +07:00
|
|
|
STASK_INVOKE( proc_control_request_st(event.rhport, &event.setup_received), err );
|
|
|
|
}
|
2018-06-22 00:40:16 +07:00
|
|
|
else if (USBD_EVT_XFER_DONE == event.event_id)
|
2014-03-31 13:12:51 +07:00
|
|
|
{
|
2018-04-13 16:10:42 +07:00
|
|
|
// TODO only call respective interface callback
|
2018-05-14 13:02:01 +07:00
|
|
|
// Call class handling function. Those does not own the endpoint should check and return
|
|
|
|
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
|
2018-03-06 16:50:50 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
if ( usbd_class_drivers[i].xfer_cb )
|
2018-03-07 15:30:32 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
usbd_class_drivers[i].xfer_cb( event.rhport, event.xfer_done.ep_addr, (tusb_event_t) event.sub_event_id, event.xfer_done.xferred_byte);
|
2018-03-07 15:30:32 +07:00
|
|
|
}
|
2018-03-06 16:50:50 +07:00
|
|
|
}
|
2018-04-13 16:10:42 +07:00
|
|
|
}
|
2018-06-22 00:40:16 +07:00
|
|
|
else if (USBD_EVT_SOF == event.event_id)
|
2018-03-08 14:38:06 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
|
2018-03-08 14:38:06 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
if ( usbd_class_drivers[i].sof )
|
2018-03-08 14:38:06 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
usbd_class_drivers[i].sof( event.rhport );
|
2018-03-08 14:38:06 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2014-04-24 23:40:28 +07:00
|
|
|
{
|
2018-03-22 16:25:24 +07:00
|
|
|
STASK_ASSERT(false);
|
2014-03-31 13:12:51 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
OSAL_SUBTASK_END
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// CONTROL REQUEST
|
|
|
|
//--------------------------------------------------------------------+
|
2018-03-23 12:17:47 +07:00
|
|
|
static tusb_error_t proc_control_request_st(uint8_t rhport, tusb_control_request_t const * const p_request)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
|
|
|
OSAL_SUBTASK_BEGIN
|
|
|
|
|
2014-03-14 17:18:05 +07:00
|
|
|
tusb_error_t error;
|
|
|
|
error = TUSB_ERROR_NONE;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
//------------- Standard Request e.g in enumeration -------------//
|
2018-03-21 15:32:35 +07:00
|
|
|
if( TUSB_REQ_RCPT_DEVICE == p_request->bmRequestType_bit.recipient &&
|
|
|
|
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-21 15:32:35 +07:00
|
|
|
if ( TUSB_REQ_GET_DESCRIPTOR == p_request->bRequest )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-22 14:15:16 +07:00
|
|
|
uint8_t const * buffer = NULL;
|
2018-03-23 12:17:47 +07:00
|
|
|
uint16_t const len = get_descriptor(rhport, p_request, &buffer);
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
if ( len )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-31 14:09:48 +07:00
|
|
|
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, (uint8_t*) buffer, len );
|
2018-03-22 14:15:16 +07:00
|
|
|
}else
|
|
|
|
{
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_stall(rhport); // stall unsupported descriptor
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
}
|
2018-03-23 14:57:17 +07:00
|
|
|
else if (TUSB_REQ_GET_CONFIGURATION == p_request->bRequest )
|
|
|
|
{
|
|
|
|
memcpy(usbd_enum_buffer, &usbd_devices[rhport].config_num, 1);
|
2018-03-31 14:09:48 +07:00
|
|
|
usbd_control_xfer_st(rhport, p_request->bmRequestType_bit.direction, (uint8_t*) usbd_enum_buffer, 1);
|
2018-03-23 14:57:17 +07:00
|
|
|
}
|
2018-03-21 15:32:35 +07:00
|
|
|
else if ( TUSB_REQ_SET_ADDRESS == p_request->bRequest )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-28 13:47:58 +07:00
|
|
|
dcd_set_address(rhport, (uint8_t) p_request->wValue);
|
2018-03-23 12:17:47 +07:00
|
|
|
usbd_devices[rhport].state = TUSB_DEVICE_STATE_ADDRESSED;
|
2018-03-14 22:01:16 +07:00
|
|
|
|
2018-04-12 13:14:59 +07:00
|
|
|
#if CFG_TUSB_MCU != OPT_MCU_NRF5X // nrf5x auto handle set address, we must not return status
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
|
2018-03-21 15:32:35 +07:00
|
|
|
#endif
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
2018-03-21 15:32:35 +07:00
|
|
|
else if ( TUSB_REQ_SET_CONFIGURATION == p_request->bRequest )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-23 12:17:47 +07:00
|
|
|
proc_set_config_req(rhport, (uint8_t) p_request->wValue);
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
|
2018-03-22 14:15:16 +07:00
|
|
|
}
|
|
|
|
else
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_stall(rhport); // Stall unsupported request
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
}
|
2018-03-22 14:15:16 +07:00
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
//------------- Class/Interface Specific Request -------------//
|
2018-03-11 21:05:27 +07:00
|
|
|
else if ( TUSB_REQ_RCPT_INTERFACE == p_request->bmRequestType_bit.recipient)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
static uint8_t drid;
|
|
|
|
uint8_t const class_code = usbd_devices[rhport].interface2class[ u16_low_u8(p_request->wIndex) ];
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-14 13:02:01 +07:00
|
|
|
for (drid = 0; drid < USBD_CLASS_DRIVER_COUNT; drid++)
|
|
|
|
{
|
|
|
|
if ( usbd_class_drivers[drid].class_code == class_code ) break;
|
|
|
|
}
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-06-16 12:30:10 +07:00
|
|
|
if ( (drid < USBD_CLASS_DRIVER_COUNT) && usbd_class_drivers[drid].control_req_st )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-06-16 12:30:10 +07:00
|
|
|
STASK_INVOKE( usbd_class_drivers[drid].control_req_st(rhport, p_request), error );
|
2014-03-12 14:01:38 +07:00
|
|
|
}else
|
|
|
|
{
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_stall(rhport); // Stall unsupported request
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------- Endpoint Request -------------//
|
2018-03-11 21:05:27 +07:00
|
|
|
else if ( TUSB_REQ_RCPT_ENDPOINT == p_request->bmRequestType_bit.recipient &&
|
2018-03-22 14:15:16 +07:00
|
|
|
TUSB_REQ_TYPE_STANDARD == p_request->bmRequestType_bit.type)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-22 14:15:16 +07:00
|
|
|
if (TUSB_REQ_CLEAR_FEATURE == p_request->bRequest )
|
|
|
|
{
|
2018-03-28 13:47:58 +07:00
|
|
|
dcd_edpt_clear_stall(rhport, u16_low_u8(p_request->wIndex) );
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_status(rhport, p_request->bmRequestType_bit.direction);
|
2018-03-22 14:15:16 +07:00
|
|
|
} else
|
|
|
|
{
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_stall(rhport); // Stall unsupported request
|
2018-03-22 14:15:16 +07:00
|
|
|
}
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
//------------- Unsupported Request -------------//
|
|
|
|
else
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-28 13:54:28 +07:00
|
|
|
dcd_control_stall(rhport); // Stall unsupported request
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
OSAL_SUBTASK_END
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO Host (windows) can get HID report descriptor before set configured
|
2014-03-23 15:39:55 +07:00
|
|
|
// may need to open interface before set configured
|
2018-03-23 12:17:47 +07:00
|
|
|
static tusb_error_t proc_set_config_req(uint8_t rhport, uint8_t config_number)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-28 13:47:58 +07:00
|
|
|
dcd_set_config(rhport, config_number);
|
2018-03-23 14:57:17 +07:00
|
|
|
|
2018-03-23 12:17:47 +07:00
|
|
|
usbd_devices[rhport].state = TUSB_DEVICE_STATE_CONFIGURED;
|
2018-03-23 14:57:17 +07:00
|
|
|
usbd_devices[rhport].config_num = config_number;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
|
|
|
//------------- parse configuration & open drivers -------------//
|
2018-04-16 13:46:28 +07:00
|
|
|
uint8_t const * p_desc_config = _usbd_descs[rhport].configuration;
|
|
|
|
TU_ASSERT(p_desc_config != NULL, TUSB_ERROR_DESCRIPTOR_CORRUPTED);
|
|
|
|
|
2018-03-23 12:32:40 +07:00
|
|
|
uint8_t const * p_desc = p_desc_config + sizeof(tusb_desc_configuration_t);
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
uint16_t const config_len = ((tusb_desc_configuration_t*)p_desc_config)->wTotalLength;
|
2014-03-23 15:39:55 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
while( p_desc < p_desc_config + config_len )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-11 21:16:51 +07:00
|
|
|
if ( TUSB_DESC_INTERFACE_ASSOCIATION == p_desc[DESCRIPTOR_OFFSET_TYPE])
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2014-03-18 18:38:15 +07:00
|
|
|
p_desc += p_desc[DESCRIPTOR_OFFSET_LENGTH]; // ignore Interface Association
|
2014-03-12 14:01:38 +07:00
|
|
|
}else
|
|
|
|
{
|
2018-03-28 14:49:00 +07:00
|
|
|
TU_ASSERT( TUSB_DESC_INTERFACE == p_desc[DESCRIPTOR_OFFSET_TYPE], TUSB_ERROR_NOT_SUPPORTED_YET );
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-14 13:02:01 +07:00
|
|
|
tusb_desc_interface_t* p_desc_itf = (tusb_desc_interface_t*) p_desc;
|
|
|
|
uint8_t const class_code = p_desc_itf->bInterfaceClass;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-05-14 13:02:01 +07:00
|
|
|
// Check if class is supported
|
|
|
|
uint8_t drid;
|
|
|
|
for (drid = 0; drid < USBD_CLASS_DRIVER_COUNT; drid++)
|
|
|
|
{
|
|
|
|
if ( usbd_class_drivers[drid].class_code == class_code ) break;
|
|
|
|
}
|
|
|
|
TU_ASSERT( drid < USBD_CLASS_DRIVER_COUNT, TUSB_ERROR_NOT_SUPPORTED_YET );
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-06-16 12:30:10 +07:00
|
|
|
// Check duplicate interface number TODO support alternate setting
|
2018-05-14 13:02:01 +07:00
|
|
|
TU_ASSERT( 0 == usbd_devices[rhport].interface2class[p_desc_itf->bInterfaceNumber], TUSB_ERROR_FAILED);
|
|
|
|
usbd_devices[rhport].interface2class[p_desc_itf->bInterfaceNumber] = class_code;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
|
|
|
uint16_t length=0;
|
2018-05-14 13:02:01 +07:00
|
|
|
TU_ASSERT_ERR( usbd_class_drivers[drid].open( rhport, p_desc_itf, &length ) );
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-03-28 14:49:00 +07:00
|
|
|
TU_ASSERT( length >= sizeof(tusb_desc_interface_t), TUSB_ERROR_FAILED );
|
2014-03-12 14:01:38 +07:00
|
|
|
p_desc += length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 16:39:33 +07:00
|
|
|
// invoke callback
|
2018-03-23 12:17:47 +07:00
|
|
|
tud_mount_cb(rhport);
|
2018-03-07 16:39:33 +07:00
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
return TUSB_ERROR_NONE;
|
|
|
|
}
|
|
|
|
|
2018-03-23 12:17:47 +07:00
|
|
|
static uint16_t get_descriptor(uint8_t rhport, tusb_control_request_t const * const p_request, uint8_t const ** pp_buffer)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-12 22:37:12 +07:00
|
|
|
tusb_desc_type_t const desc_type = (tusb_desc_type_t) u16_high_u8(p_request->wValue);
|
2014-03-12 14:01:38 +07:00
|
|
|
uint8_t const desc_index = u16_low_u8( p_request->wValue );
|
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
uint8_t const * desc_data = NULL ;
|
|
|
|
uint16_t len = 0;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
//------------- Descriptor Check -------------//
|
|
|
|
tud_desc_init_t const* descs = &_usbd_descs[rhport];
|
|
|
|
|
2014-03-23 15:39:55 +07:00
|
|
|
switch(desc_type)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-11 21:16:51 +07:00
|
|
|
case TUSB_DESC_DEVICE:
|
2018-04-16 13:46:28 +07:00
|
|
|
desc_data = descs->device;
|
2018-03-23 12:32:40 +07:00
|
|
|
len = sizeof(tusb_desc_device_t);
|
2014-03-23 15:39:55 +07:00
|
|
|
break;
|
|
|
|
|
2018-03-11 21:16:51 +07:00
|
|
|
case TUSB_DESC_CONFIGURATION:
|
2018-04-16 13:46:28 +07:00
|
|
|
desc_data = descs->configuration;
|
|
|
|
len = ((tusb_desc_configuration_t*)descs->configuration)->wTotalLength;
|
2014-03-23 15:39:55 +07:00
|
|
|
break;
|
|
|
|
|
2018-03-11 21:16:51 +07:00
|
|
|
case TUSB_DESC_STRING:
|
2018-03-22 14:15:16 +07:00
|
|
|
// windows sometimes ask for string at index 238 !!!
|
|
|
|
if ( !(desc_index < 100) ) return 0;
|
2014-03-23 15:39:55 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
desc_data = descs->string_arr[desc_index];
|
2018-03-22 14:15:16 +07:00
|
|
|
VERIFY( desc_data != NULL, 0 );
|
2014-03-23 15:39:55 +07:00
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
len = desc_data[0]; // first byte of descriptor is its size
|
2014-03-23 15:39:55 +07:00
|
|
|
break;
|
|
|
|
|
2018-03-14 22:01:16 +07:00
|
|
|
case TUSB_DESC_DEVICE_QUALIFIER:
|
|
|
|
// TODO If not highspeed capable stall this request otherwise
|
|
|
|
// return the descriptor that could work in highspeed
|
2018-03-22 14:15:16 +07:00
|
|
|
return 0;
|
2018-03-14 22:01:16 +07:00
|
|
|
break;
|
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
default: return 0;
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
TU_ASSERT( desc_data != NULL, 0);
|
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
// up to Host's length
|
|
|
|
len = min16_of(p_request->wLength, len );
|
2018-04-12 13:23:52 +07:00
|
|
|
TU_ASSERT( len <= CFG_TUD_ENUM_BUFFER_SIZE, 0);
|
2014-03-23 15:39:55 +07:00
|
|
|
|
2018-04-16 13:46:28 +07:00
|
|
|
// FIXME copy data to enum buffer
|
2018-03-22 14:15:16 +07:00
|
|
|
memcpy(usbd_enum_buffer, desc_data, len);
|
2014-03-23 15:39:55 +07:00
|
|
|
(*pp_buffer) = usbd_enum_buffer;
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-03-22 14:15:16 +07:00
|
|
|
return len;
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// USBD-CLASS API
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// USBD-DCD Callback API
|
|
|
|
//--------------------------------------------------------------------+
|
2018-03-28 13:47:58 +07:00
|
|
|
void dcd_bus_event(uint8_t rhport, usbd_bus_event_type_t bus_event)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
|
|
|
switch(bus_event)
|
|
|
|
{
|
|
|
|
case USBD_BUS_EVENT_RESET :
|
2018-03-23 12:17:47 +07:00
|
|
|
memclr_(&usbd_devices[rhport], sizeof(usbd_device_info_t));
|
2018-03-30 16:57:18 +07:00
|
|
|
osal_queue_flush(_usbd_q);
|
2018-05-17 19:19:55 +07:00
|
|
|
osal_semaphore_reset_isr(_usbd_ctrl_sem);
|
2018-05-14 13:02:01 +07:00
|
|
|
for (uint8_t i = 0; i < USBD_CLASS_DRIVER_COUNT; i++)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-05-14 13:02:01 +07:00
|
|
|
if ( usbd_class_drivers[i].close ) usbd_class_drivers[i].close( rhport );
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-03-08 14:38:06 +07:00
|
|
|
case USBD_BUS_EVENT_SOF:
|
|
|
|
{
|
|
|
|
usbd_task_event_t task_event =
|
|
|
|
{
|
2018-03-23 12:17:47 +07:00
|
|
|
.rhport = rhport,
|
2018-06-22 00:40:16 +07:00
|
|
|
.event_id = USBD_EVT_SOF,
|
2018-03-08 14:38:06 +07:00
|
|
|
};
|
2018-05-17 13:49:38 +07:00
|
|
|
osal_queue_send_isr(_usbd_q, &task_event);
|
2018-03-08 14:38:06 +07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-03-23 14:57:17 +07:00
|
|
|
case USBD_BUS_EVENT_UNPLUGGED:
|
|
|
|
// invoke callback
|
|
|
|
tud_umount_cb(rhport);
|
|
|
|
break;
|
2018-03-03 16:24:43 +07:00
|
|
|
|
2014-03-12 14:01:38 +07:00
|
|
|
case USBD_BUS_EVENT_SUSPENDED:
|
2018-03-23 12:17:47 +07:00
|
|
|
usbd_devices[rhport].state = TUSB_DEVICE_STATE_SUSPENDED;
|
2014-03-12 14:01:38 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-28 13:47:58 +07:00
|
|
|
void dcd_setup_received(uint8_t rhport, uint8_t const* p_request)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
|
|
|
usbd_task_event_t task_event =
|
|
|
|
{
|
2018-03-23 12:17:47 +07:00
|
|
|
.rhport = rhport,
|
2018-06-22 00:40:16 +07:00
|
|
|
.event_id = USBD_EVT_SETUP_RECEIVED,
|
2014-03-12 14:01:38 +07:00
|
|
|
};
|
|
|
|
|
2018-03-03 14:45:29 +07:00
|
|
|
memcpy(&task_event.setup_received, p_request, sizeof(tusb_control_request_t));
|
2018-05-17 13:49:38 +07:00
|
|
|
osal_queue_send_isr(_usbd_q, &task_event);
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
|
2018-03-28 13:47:58 +07:00
|
|
|
void dcd_xfer_complete(uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, bool succeeded)
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-03-17 02:17:40 +07:00
|
|
|
if (ep_addr == 0 )
|
2014-03-12 14:01:38 +07:00
|
|
|
{
|
2018-05-15 22:06:49 +07:00
|
|
|
// Control Transfer
|
2018-03-23 12:17:47 +07:00
|
|
|
(void) rhport;
|
2018-03-21 16:08:42 +07:00
|
|
|
(void) succeeded;
|
|
|
|
|
2018-05-15 22:06:49 +07:00
|
|
|
// only signal data stage, skip status (zero byte)
|
2018-05-17 13:49:38 +07:00
|
|
|
if (xferred_bytes) osal_semaphore_post_isr( _usbd_ctrl_sem );
|
2014-03-12 14:01:38 +07:00
|
|
|
}else
|
|
|
|
{
|
|
|
|
usbd_task_event_t task_event =
|
|
|
|
{
|
2018-03-23 12:17:47 +07:00
|
|
|
.rhport = rhport,
|
2018-06-22 00:40:16 +07:00
|
|
|
.event_id = USBD_EVT_XFER_DONE,
|
2018-03-11 13:59:37 +07:00
|
|
|
.sub_event_id = succeeded ? TUSB_EVENT_XFER_COMPLETE : TUSB_EVENT_XFER_ERROR
|
2014-03-12 14:01:38 +07:00
|
|
|
};
|
|
|
|
|
2018-03-21 16:08:42 +07:00
|
|
|
task_event.xfer_done.ep_addr = ep_addr;
|
2014-03-12 14:01:38 +07:00
|
|
|
task_event.xfer_done.xferred_byte = xferred_bytes;
|
|
|
|
|
2018-05-17 13:49:38 +07:00
|
|
|
osal_queue_send_isr(_usbd_q, &task_event);
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
2018-03-31 13:23:23 +07:00
|
|
|
|
|
|
|
TU_ASSERT(succeeded, );
|
2014-03-12 14:01:38 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// HELPER
|
|
|
|
//--------------------------------------------------------------------+
|
2018-06-16 12:30:10 +07:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
for(int i=0; i<2; i++)
|
|
|
|
{
|
|
|
|
TU_ASSERT(TUSB_DESC_ENDPOINT == p_desc_ep->bDescriptorType &&
|
|
|
|
xfer_type == p_desc_ep->bmAttributes.xfer, TUSB_ERROR_DESCRIPTOR_CORRUPTED);
|
|
|
|
|
|
|
|
TU_ASSERT( dcd_edpt_open(rhport, p_desc_ep), TUSB_ERROR_DCD_OPEN_PIPE_FAILED );
|
|
|
|
|
2018-06-18 14:31:15 +07:00
|
|
|
if ( edpt_dir(p_desc_ep->bEndpointAddress) == TUSB_DIR_IN )
|
2018-06-16 12:30:10 +07:00
|
|
|
{
|
|
|
|
(*ep_in) = p_desc_ep->bEndpointAddress;
|
|
|
|
}else
|
|
|
|
{
|
|
|
|
(*ep_out) = p_desc_ep->bEndpointAddress;
|
|
|
|
}
|
2014-03-12 14:01:38 +07:00
|
|
|
|
2018-06-16 12:30:10 +07:00
|
|
|
p_desc_ep = (tusb_desc_endpoint_t const *) descriptor_next( (uint8_t const*) p_desc_ep );
|
|
|
|
}
|
|
|
|
|
|
|
|
return TUSB_ERROR_NONE;
|
|
|
|
}
|
2014-03-12 14:01:38 +07:00
|
|
|
#endif
|