Merge branch 'pigrew-ZLP_Request2' into cr1901-msp430f5529
This commit is contained in:
120
src/class/dfu/dfu_rt_device.c
Normal file
120
src/class/dfu/dfu_rt_device.c
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* 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_DFU_RT)
|
||||
|
||||
#include "dfu_rt_device.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF
|
||||
//--------------------------------------------------------------------+
|
||||
typedef enum {
|
||||
DFU_REQUEST_DETACH = 0,
|
||||
DFU_REQUEST_DNLOAD = 1,
|
||||
DFU_REQUEST_UPLOAD = 2,
|
||||
DFU_REQUEST_GETSTATUS = 3,
|
||||
DFU_REQUEST_CLRSTATUS = 4,
|
||||
DFU_REQUEST_GETSTATE = 5,
|
||||
DFU_REQUEST_ABORT = 6,
|
||||
} dfu_requests_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_rtd_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
void dfu_rtd_reset(uint8_t rhport)
|
||||
{
|
||||
(void) rhport;
|
||||
}
|
||||
|
||||
bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
// Ensure this is DFU Runtime
|
||||
TU_ASSERT(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS);
|
||||
TU_ASSERT(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT);
|
||||
|
||||
uint8_t const * p_desc = tu_desc_next( itf_desc );
|
||||
(*p_length) = sizeof(tusb_desc_interface_t);
|
||||
|
||||
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
|
||||
{
|
||||
(*p_length) += p_desc[DESC_OFFSET_LEN];
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
//------------- Class Specific Request -------------//
|
||||
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
//------------- Class Specific Request -------------//
|
||||
TU_ASSERT(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
|
||||
TU_ASSERT(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
|
||||
|
||||
switch ( request->bRequest )
|
||||
{
|
||||
case DFU_REQUEST_DETACH:
|
||||
tud_control_status(rhport, request);
|
||||
tud_dfu_rt_reboot_to_dfu();
|
||||
break;
|
||||
|
||||
default: return false; // stall unsupported request
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) ep_addr;
|
||||
(void) result;
|
||||
(void) xferred_bytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
77
src/class/dfu/dfu_rt_device.h
Normal file
77
src/class/dfu/dfu_rt_device.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TUSB_DFU_RT_DEVICE_H_
|
||||
#define _TUSB_DFU_RT_DEVICE_H_
|
||||
|
||||
#include "common/tusb_common.h"
|
||||
#include "device/usbd.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Common Definitions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// DFU Protocol
|
||||
typedef enum
|
||||
{
|
||||
DFU_PROTOCOL_RT = 1,
|
||||
DFU_PROTOCOL_DFU = 2,
|
||||
} dfu_protocol_type_t;
|
||||
|
||||
// DFU Descriptor Type
|
||||
typedef enum
|
||||
{
|
||||
DFU_DESC_FUNCTIONAL = 0x21,
|
||||
} dfu_descriptor_type_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application Callback API (weak is optional)
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Invoked when received new data
|
||||
TU_ATTR_WEAK void tud_dfu_rt_reboot_to_dfu(void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Internal Class Driver API
|
||||
//--------------------------------------------------------------------+
|
||||
void dfu_rtd_init(void);
|
||||
void dfu_rtd_reset(uint8_t rhport);
|
||||
bool dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t *p_length);
|
||||
bool dfu_rtd_control_request(uint8_t rhport, tusb_control_request_t const * request);
|
||||
bool dfu_rtd_control_complete(uint8_t rhport, tusb_control_request_t const * request);
|
||||
bool dfu_rtd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _TUSB_DFU_RT_DEVICE_H_ */
|
@@ -1,327 +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:
|
||||
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
|
||||
/*
|
||||
* 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
|
||||
|
@@ -86,6 +86,8 @@ typedef enum
|
||||
TUSB_DESC_BOS = 0x0F,
|
||||
TUSB_DESC_DEVICE_CAPABILITY = 0x10,
|
||||
|
||||
TUSB_DESC_FUNCTIONAL = 0x21,
|
||||
|
||||
// Class Specific Descriptor
|
||||
TUSB_DESC_CS_DEVICE = 0x21,
|
||||
TUSB_DESC_CS_CONFIGURATION = 0x22,
|
||||
|
@@ -79,7 +79,7 @@ typedef struct TU_ATTR_ALIGNED(4)
|
||||
};
|
||||
} dcd_event_t;
|
||||
|
||||
TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
//TU_VERIFY_STATIC(sizeof(dcd_event_t) <= 12, "size is not correct");
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Device API
|
||||
@@ -119,20 +119,51 @@ void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
// clear stall, data toggle is also reset to DATA0
|
||||
void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr);
|
||||
|
||||
/*------------------------------------------------------------------*/
|
||||
/* Event Function
|
||||
* Called by DCD to notify device stack
|
||||
*------------------------------------------------------------------*/
|
||||
void dcd_event_handler(dcd_event_t const * event, bool in_isr);
|
||||
//--------------------------------------------------------------------+
|
||||
// Event API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// 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
|
||||
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
|
||||
static inline void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr);
|
||||
|
||||
// helper to send setup received
|
||||
void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
|
||||
static inline void dcd_event_setup_received(uint8_t rhport, uint8_t const * setup, bool in_isr);
|
||||
|
||||
// helper to send transfer complete event
|
||||
void dcd_event_xfer_complete (uint8_t rhport, uint8_t ep_addr, uint32_t xferred_bytes, uint8_t result, bool 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);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// 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);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -168,6 +168,20 @@ static usbd_class_driver_t const usbd_class_drivers[] =
|
||||
.sof = NULL
|
||||
},
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU_RT
|
||||
{
|
||||
.class_code = TUD_DFU_APP_CLASS,
|
||||
//.subclass_code = TUD_DFU_APP_SUBCLASS
|
||||
.init = dfu_rtd_init,
|
||||
.reset = dfu_rtd_reset,
|
||||
.open = dfu_rtd_open,
|
||||
.control_request = dfu_rtd_control_request,
|
||||
.control_complete = dfu_rtd_control_complete,
|
||||
.xfer_cb = dfu_rtd_xfer_cb,
|
||||
.sof = NULL
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(usbd_class_drivers) };
|
||||
@@ -277,7 +291,7 @@ bool tud_remote_wakeup(void)
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Task
|
||||
//--------------------------------------------------------------------+
|
||||
bool usbd_init (void)
|
||||
bool tud_init (void)
|
||||
{
|
||||
TU_LOG2("USBD init\r\n");
|
||||
|
||||
@@ -391,7 +405,6 @@ void tud_task (void)
|
||||
|
||||
if ( 0 == epnum )
|
||||
{
|
||||
// control transfer DATA stage callback
|
||||
usbd_control_xfer_cb(event.rhport, ep_addr, event.xfer_complete.result, event.xfer_complete.len);
|
||||
}
|
||||
else
|
||||
@@ -783,6 +796,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
|
||||
|
||||
@@ -866,10 +881,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;
|
||||
@@ -883,34 +894,6 @@ void dcd_event_handler(dcd_event_t const * event, bool in_isr)
|
||||
}
|
||||
}
|
||||
|
||||
// helper to send bus signal event
|
||||
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);
|
||||
}
|
||||
|
||||
// helper to send setup received
|
||||
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);
|
||||
}
|
||||
|
||||
// helper to send transfer complete event
|
||||
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
|
||||
//--------------------------------------------------------------------+
|
||||
|
@@ -41,6 +41,9 @@
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Init device stack
|
||||
bool tud_init (void);
|
||||
|
||||
// Task function should be called in main/rtos loop
|
||||
void tud_task (void);
|
||||
|
||||
@@ -304,6 +307,21 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re
|
||||
/* Endpoint In */\
|
||||
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
|
||||
|
||||
//------------- DFU Runtime -------------//
|
||||
#define TUD_DFU_APP_CLASS (TUSB_CLASS_APPLICATION_SPECIFIC)
|
||||
#define TUD_DFU_APP_SUBCLASS 0x01u
|
||||
|
||||
// Length of template descriptr: 18 bytes
|
||||
#define TUD_DFU_RT_DESC_LEN (9 + 9)
|
||||
|
||||
// DFU runtime descriptor
|
||||
// Interface number, string index, attributes, detach timeout, transfer size
|
||||
#define TUD_DFU_RT_DESCRIPTOR(_itfnum, _stridx, _attr, _timeout, _xfer_size) \
|
||||
/* Interface */ \
|
||||
9, TUSB_DESC_INTERFACE, _itfnum, 0, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_RT, _stridx, \
|
||||
/* Function */ \
|
||||
9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@@ -42,23 +42,21 @@ typedef struct
|
||||
{
|
||||
tusb_control_request_t request;
|
||||
|
||||
void* buffer;
|
||||
uint16_t len;
|
||||
uint16_t total_transferred;
|
||||
uint16_t requested_len;
|
||||
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 _control_state;
|
||||
static usbd_control_xfer_t _ctrl_xfer;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Application API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
|
||||
{
|
||||
@@ -66,47 +64,37 @@ bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request)
|
||||
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)
|
||||
// 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(_control_state.len - _control_state.total_transferred, CFG_TUD_ENDPOINT0_SIZE);
|
||||
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 ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_IN )
|
||||
if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN )
|
||||
{
|
||||
ep_addr = EDPT_CTRL_IN;
|
||||
memcpy(_usbd_ctrl_buf, _control_state.buffer, xact_len);
|
||||
if ( xact_len ) memcpy(_usbd_ctrl_buf, _ctrl_xfer.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;
|
||||
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)
|
||||
{
|
||||
// transmitted length must be <= requested length (USB 2.0 spec: 8.5.3.1 )
|
||||
// FIXME: Should logic be here or in place that calls this function?
|
||||
if(len > request->wLength)
|
||||
len = request->wLength;
|
||||
_ctrl_xfer.request = (*request);
|
||||
_ctrl_xfer.buffer = (uint8_t*) buffer;
|
||||
_ctrl_xfer.total_xferred = 0;
|
||||
_ctrl_xfer.data_len = tu_min16(len, request->wLength);
|
||||
|
||||
_control_state.request = (*request);
|
||||
_control_state.buffer = buffer;
|
||||
_control_state.total_transferred = 0;
|
||||
_control_state.requested_len = request->wLength;
|
||||
_control_state.len = len;
|
||||
|
||||
if ( len )
|
||||
if ( _ctrl_xfer.data_len )
|
||||
{
|
||||
TU_ASSERT(buffer);
|
||||
|
||||
// Data stage
|
||||
TU_ASSERT( start_control_data_xact(rhport) );
|
||||
TU_ASSERT( _data_stage_xact(rhport) );
|
||||
}else
|
||||
{
|
||||
// Status stage
|
||||
@@ -116,38 +104,61 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
|
||||
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;
|
||||
(void) ep_addr;
|
||||
|
||||
if ( _control_state.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
|
||||
// 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_VERIFY(_control_state.buffer);
|
||||
memcpy(_control_state.buffer, _usbd_ctrl_buf, xferred_bytes);
|
||||
TU_ASSERT(0 == xferred_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
_control_state.total_transferred += xferred_bytes;
|
||||
_control_state.buffer = ((uint8_t*)_control_state.buffer) + xferred_bytes;
|
||||
if ( _ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_OUT )
|
||||
{
|
||||
TU_VERIFY(_ctrl_xfer.buffer);
|
||||
memcpy(_ctrl_xfer.buffer, _usbd_ctrl_buf, xferred_bytes);
|
||||
}
|
||||
|
||||
if ( (_control_state.requested_len == _control_state.total_transferred) || xferred_bytes < CFG_TUD_ENDPOINT0_SIZE )
|
||||
_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 ( _control_state.complete_cb )
|
||||
if ( _ctrl_xfer.complete_cb )
|
||||
{
|
||||
is_ok = _control_state.complete_cb(rhport, &_control_state.request);
|
||||
is_ok = _ctrl_xfer.complete_cb(rhport, &_ctrl_xfer.request);
|
||||
}
|
||||
|
||||
if ( is_ok )
|
||||
{
|
||||
// Send status
|
||||
TU_ASSERT( tud_control_status(rhport, &_control_state.request) );
|
||||
TU_ASSERT( tud_control_status(rhport, &_ctrl_xfer.request) );
|
||||
}else
|
||||
{
|
||||
// Stall both IN and OUT control endpoint
|
||||
@@ -158,7 +169,7 @@ bool usbd_control_xfer_cb (uint8_t rhport, uint8_t ep_addr, xfer_result_t result
|
||||
else
|
||||
{
|
||||
// More data to transfer
|
||||
TU_ASSERT( start_control_data_xact(rhport) );
|
||||
TU_ASSERT( _data_stage_xact(rhport) );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@@ -33,8 +33,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool usbd_init (void);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// USBD Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
@@ -47,7 +47,7 @@ bool tusb_init(void)
|
||||
#endif
|
||||
|
||||
#if TUSB_OPT_DEVICE_ENABLED
|
||||
TU_ASSERT ( usbd_init() ); // init device stack
|
||||
TU_ASSERT ( tud_init() ); // init device stack
|
||||
#endif
|
||||
|
||||
_initialized = true;
|
||||
|
@@ -87,6 +87,10 @@
|
||||
#if CFG_TUD_USBTMC
|
||||
#include "class/usbtmc/usbtmc_device.h"
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_DFU_RT
|
||||
#include "class/dfu/dfu_rt_device.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
@@ -192,6 +192,10 @@
|
||||
#define CFG_TUD_USBTMC 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUD_DFU_RT
|
||||
#define CFG_TUD_DFU_RT 0
|
||||
#endif
|
||||
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// HOST OPTIONS
|
||||
|
Reference in New Issue
Block a user