mirror of
https://github.com/hathach/tinyusb.git
synced 2025-01-17 05:32:55 +08:00
commit
ae531a79f6
@ -35,11 +35,28 @@
|
||||
#include "bsp/board.h"
|
||||
#include "tusb.h"
|
||||
|
||||
// English
|
||||
#define LANGUAGE_ID 0x0409
|
||||
#define BUF_COUNT 4
|
||||
|
||||
|
||||
tusb_desc_device_t desc_device;
|
||||
|
||||
uint8_t buf_pool[BUF_COUNT][64];
|
||||
uint8_t buf_owner[BUF_COUNT] = { 0 }; // device address that owns buffer
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// MACRO CONSTANT TYPEDEF PROTYPES
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void);
|
||||
|
||||
static void print_utf16(uint16_t *temp_buf, size_t buf_len);
|
||||
void print_device_descriptor(tuh_xfer_t* xfer);
|
||||
void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
|
||||
|
||||
uint8_t* get_hid_buf(uint8_t daddr);
|
||||
void free_hid_buf(uint8_t daddr);
|
||||
|
||||
/*------------- MAIN -------------*/
|
||||
int main(void)
|
||||
{
|
||||
@ -58,16 +75,297 @@ int main(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------- TinyUSB Callbacks -------------*/
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
void tuh_mount_cb (uint8_t daddr)
|
||||
{
|
||||
printf("Device attached, address = %d\r\n", daddr);
|
||||
|
||||
// Get Device Descriptor sync API
|
||||
// TODO: invoking control trannsfer now has issue with mounting hub with multiple devices attached, fix later
|
||||
tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
|
||||
}
|
||||
|
||||
/// Invoked when device is unmounted (bus reset/unplugged)
|
||||
void tuh_umount_cb(uint8_t daddr)
|
||||
{
|
||||
printf("Device removed, address = %d\r\n", daddr);
|
||||
free_hid_buf(daddr);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// TinyUSB Callbacks
|
||||
// Device Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// English
|
||||
#define LANGUAGE_ID 0x0409
|
||||
void print_device_descriptor(tuh_xfer_t* xfer)
|
||||
{
|
||||
if ( XFER_RESULT_SUCCESS != xfer->result )
|
||||
{
|
||||
printf("Failed to get device descriptor\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
//uint8_t usb_buf[256] TU_ATTR_ALIGNED(4);
|
||||
TU_ATTR_ALIGNED(4)
|
||||
tusb_desc_device_t desc_device;
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
|
||||
printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
|
||||
printf("Device Descriptor:\r\n");
|
||||
printf(" bLength %u\r\n" , desc_device.bLength);
|
||||
printf(" bDescriptorType %u\r\n" , desc_device.bDescriptorType);
|
||||
printf(" bcdUSB %04x\r\n" , desc_device.bcdUSB);
|
||||
printf(" bDeviceClass %u\r\n" , desc_device.bDeviceClass);
|
||||
printf(" bDeviceSubClass %u\r\n" , desc_device.bDeviceSubClass);
|
||||
printf(" bDeviceProtocol %u\r\n" , desc_device.bDeviceProtocol);
|
||||
printf(" bMaxPacketSize0 %u\r\n" , desc_device.bMaxPacketSize0);
|
||||
printf(" idVendor 0x%04x\r\n" , desc_device.idVendor);
|
||||
printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
|
||||
printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
|
||||
|
||||
// Get String descriptor using Sync API
|
||||
uint16_t temp_buf[128];
|
||||
|
||||
printf(" iManufacturer %u " , desc_device.iManufacturer);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)) )
|
||||
{
|
||||
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" iProduct %u " , desc_device.iProduct);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)))
|
||||
{
|
||||
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" iSerialNumber %u " , desc_device.iSerialNumber);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf)))
|
||||
{
|
||||
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations);
|
||||
|
||||
// Get configuration descriptor with sync API
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_configuration_sync(daddr, 0, temp_buf, sizeof(temp_buf)))
|
||||
{
|
||||
parse_config_descriptor(daddr, (tusb_desc_configuration_t*) temp_buf);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Configuration Descriptor
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// count total length of an interface
|
||||
uint16_t count_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len);
|
||||
|
||||
void open_hid_interface(uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len);
|
||||
|
||||
// simple configuration parser to open and listen to HID Endpoint IN
|
||||
void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg)
|
||||
{
|
||||
uint8_t const* desc_end = ((uint8_t const*) desc_cfg) + tu_le16toh(desc_cfg->wTotalLength);
|
||||
uint8_t const* p_desc = tu_desc_next(desc_cfg);
|
||||
|
||||
// parse each interfaces
|
||||
while( p_desc < desc_end )
|
||||
{
|
||||
uint8_t assoc_itf_count = 1;
|
||||
|
||||
// Class will always starts with Interface Association (if any) and then Interface descriptor
|
||||
if ( TUSB_DESC_INTERFACE_ASSOCIATION == tu_desc_type(p_desc) )
|
||||
{
|
||||
tusb_desc_interface_assoc_t const * desc_iad = (tusb_desc_interface_assoc_t const *) p_desc;
|
||||
assoc_itf_count = desc_iad->bInterfaceCount;
|
||||
|
||||
p_desc = tu_desc_next(p_desc); // next to Interface
|
||||
}
|
||||
|
||||
// must be interface from now
|
||||
if( TUSB_DESC_INTERFACE != tu_desc_type(p_desc) ) return;
|
||||
tusb_desc_interface_t const* desc_itf = (tusb_desc_interface_t const*) p_desc;
|
||||
|
||||
uint16_t const drv_len = count_interface_total_len(desc_itf, assoc_itf_count, desc_end-p_desc);
|
||||
|
||||
// probably corrupted descriptor
|
||||
if(drv_len < sizeof(tusb_desc_interface_t)) return;
|
||||
|
||||
// only open and listen to HID endpoint IN
|
||||
if (desc_itf->bInterfaceClass == TUSB_CLASS_HID)
|
||||
{
|
||||
open_hid_interface(dev_addr, desc_itf, drv_len);
|
||||
}
|
||||
|
||||
// next Interface or IAD descriptor
|
||||
p_desc += drv_len;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t count_interface_total_len(tusb_desc_interface_t const* desc_itf, uint8_t itf_count, uint16_t max_len)
|
||||
{
|
||||
uint8_t const* p_desc = (uint8_t const*) desc_itf;
|
||||
uint16_t len = 0;
|
||||
|
||||
while (itf_count--)
|
||||
{
|
||||
// Next on interface desc
|
||||
len += tu_desc_len(desc_itf);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
|
||||
while (len < max_len)
|
||||
{
|
||||
// return on IAD regardless of itf count
|
||||
if ( tu_desc_type(p_desc) == TUSB_DESC_INTERFACE_ASSOCIATION ) return len;
|
||||
|
||||
if ( (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) &&
|
||||
((tusb_desc_interface_t const*) p_desc)->bAlternateSetting == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
len += tu_desc_len(p_desc);
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// HID Interface
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
void hid_report_received(tuh_xfer_t* xfer);
|
||||
|
||||
void open_hid_interface(uint8_t daddr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
// len = interface + hid + n*endpoints
|
||||
uint16_t const drv_len = sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) + desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
|
||||
|
||||
// corrupted descriptor
|
||||
if (max_len < drv_len) return;
|
||||
|
||||
uint8_t const *p_desc = (uint8_t const *) desc_itf;
|
||||
|
||||
// HID descriptor
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_hid_descriptor_hid_t const *desc_hid = (tusb_hid_descriptor_hid_t const *) p_desc;
|
||||
if(HID_DESC_TYPE_HID != desc_hid->bDescriptorType) return;
|
||||
|
||||
// Endpoint descriptor
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
|
||||
for(int i = 0; i < desc_itf->bNumEndpoints; i++)
|
||||
{
|
||||
if (TUSB_DESC_ENDPOINT != desc_ep->bDescriptorType) return;
|
||||
|
||||
if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
|
||||
{
|
||||
// skip if failed to open endpoint
|
||||
if ( ! tuh_edpt_open(daddr, desc_ep) ) return;
|
||||
|
||||
uint8_t* buf = get_hid_buf(daddr);
|
||||
if (!buf) return; // out of memory
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = daddr,
|
||||
.ep_addr = desc_ep->bEndpointAddress,
|
||||
.buflen = 64,
|
||||
.buffer = buf,
|
||||
.complete_cb = hid_report_received,
|
||||
.user_data = (uintptr_t) buf, // since buffer is not available in callback, use user data to store the buffer
|
||||
};
|
||||
|
||||
// submit transfer for this EP
|
||||
tuh_edpt_xfer(&xfer);
|
||||
|
||||
printf("Listen to [dev %u: ep %02x]\r\n", daddr, desc_ep->bEndpointAddress);
|
||||
}
|
||||
|
||||
p_desc = tu_desc_next(p_desc);
|
||||
desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_report_received(tuh_xfer_t* xfer)
|
||||
{
|
||||
// Note: not all field in xfer is available for use (i.e filled by tinyusb stack) in callback to save sram
|
||||
// For instance, xfer->buffer is NULL. We have used user_data to store buffer when submitted callback
|
||||
uint8_t* buf = (uint8_t*) xfer->user_data;
|
||||
|
||||
if (xfer->result == XFER_RESULT_SUCCESS)
|
||||
{
|
||||
printf("[dev %u: ep %02x] HID Report:", xfer->daddr, xfer->ep_addr);
|
||||
for(uint32_t i=0; i<xfer->actual_len; i++)
|
||||
{
|
||||
if (i%16 == 0) printf("\r\n ");
|
||||
printf("%02X ", buf[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
// continue to submit transfer, with updated buffer
|
||||
// other field remain the same
|
||||
xfer->buflen = 64;
|
||||
xfer->buffer = buf;
|
||||
|
||||
tuh_edpt_xfer(xfer);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Buffer helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// get an buffer from pool
|
||||
uint8_t* get_hid_buf(uint8_t daddr)
|
||||
{
|
||||
for(size_t i=0; i<BUF_COUNT; i++)
|
||||
{
|
||||
if (buf_owner[i] == 0)
|
||||
{
|
||||
buf_owner[i] = daddr;
|
||||
return buf_pool[i];
|
||||
}
|
||||
}
|
||||
|
||||
// out of memory, increase BUF_COUNT
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// free all buffer owned by device
|
||||
void free_hid_buf(uint8_t daddr)
|
||||
{
|
||||
for(size_t i=0; i<BUF_COUNT; i++)
|
||||
{
|
||||
if (buf_owner[i] == daddr) buf_owner[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Blinking Task
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
const uint32_t interval_ms = 1000;
|
||||
static uint32_t start_ms = 0;
|
||||
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < interval_ms) return; // not enough time
|
||||
start_ms += interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// String Descriptor Helper
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
|
||||
// TODO: Check for runover.
|
||||
@ -108,97 +406,11 @@ static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
|
||||
return total_bytes;
|
||||
}
|
||||
|
||||
static void utf16_to_utf8(uint16_t *temp_buf, size_t buf_len) {
|
||||
static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
|
||||
size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
|
||||
size_t utf8_len = _count_utf8_bytes(temp_buf + 1, utf16_len);
|
||||
_convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
|
||||
((uint8_t*) temp_buf)[utf8_len] = '\0';
|
||||
}
|
||||
|
||||
bool print_device_descriptor(uint8_t daddr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
{
|
||||
(void) xfer;
|
||||
|
||||
if ( XFER_RESULT_SUCCESS != result )
|
||||
{
|
||||
printf("Failed to get device descriptor\r\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
|
||||
printf("Device Descriptor:\r\n");
|
||||
printf(" bLength %u\r\n" , desc_device.bLength);
|
||||
printf(" bDescriptorType %u\r\n" , desc_device.bDescriptorType);
|
||||
printf(" bcdUSB %04x\r\n" , desc_device.bcdUSB);
|
||||
printf(" bDeviceClass %u\r\n" , desc_device.bDeviceClass);
|
||||
printf(" bDeviceSubClass %u\r\n" , desc_device.bDeviceSubClass);
|
||||
printf(" bDeviceProtocol %u\r\n" , desc_device.bDeviceProtocol);
|
||||
printf(" bMaxPacketSize0 %u\r\n" , desc_device.bMaxPacketSize0);
|
||||
printf(" idVendor 0x%04x\r\n" , desc_device.idVendor);
|
||||
printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
|
||||
printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
|
||||
|
||||
uint32_t timeout_ms = 10;
|
||||
uint16_t temp_buf[128];
|
||||
|
||||
printf(" iManufacturer %u " , desc_device.iManufacturer);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms) )
|
||||
{
|
||||
utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
printf((const char*) temp_buf);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" iProduct %u " , desc_device.iProduct);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms))
|
||||
{
|
||||
utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
printf((const char*) temp_buf);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" iSerialNumber %u " , desc_device.iSerialNumber);
|
||||
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, temp_buf, TU_ARRAY_SIZE(temp_buf), timeout_ms))
|
||||
{
|
||||
utf16_to_utf8(temp_buf, TU_ARRAY_SIZE(temp_buf));
|
||||
printf((const char*) temp_buf);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
printf(" bNumConfigurations %u\r\n" , desc_device.bNumConfigurations);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Invoked when device is mounted (configured)
|
||||
void tuh_mount_cb (uint8_t daddr)
|
||||
{
|
||||
printf("Device attached, address = %d\r\n", daddr);
|
||||
|
||||
// Get Device Descriptor using asynchronous API
|
||||
tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
|
||||
}
|
||||
|
||||
/// Invoked when device is unmounted (bus reset/unplugged)
|
||||
void tuh_umount_cb(uint8_t daddr)
|
||||
{
|
||||
printf("Device removed, address = %d\r\n", daddr);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Blinking Task
|
||||
//--------------------------------------------------------------------+
|
||||
void led_blinking_task(void)
|
||||
{
|
||||
const uint32_t interval_ms = 1000;
|
||||
static uint32_t start_ms = 0;
|
||||
|
||||
static bool led_state = false;
|
||||
|
||||
// Blink every interval ms
|
||||
if ( board_millis() - start_ms < interval_ms) return; // not enough time
|
||||
start_ms += interval_ms;
|
||||
|
||||
board_led_write(led_state);
|
||||
led_state = 1 - led_state; // toggle
|
||||
|
||||
printf((char*)temp_buf);
|
||||
}
|
||||
|
@ -81,11 +81,11 @@
|
||||
// 1 hub typically has 4 ports
|
||||
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1)
|
||||
|
||||
#define CFG_TUH_ENDPOINT_MAX 8
|
||||
// Max endpoint per device
|
||||
#define CFG_TUH_ENDPOINT_MAX 8
|
||||
|
||||
//------------- HID -------------//
|
||||
|
||||
#define CFG_TUH_HID_EP_BUFSIZE 64
|
||||
// Enable tuh_edpt_xfer() API
|
||||
#define CFG_TUH_API_EDPT_XFER 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -120,32 +120,35 @@ bool tuh_cdc_receive(uint8_t dev_addr, void * p_buffer, uint32_t length, bool is
|
||||
return usbh_edpt_xfer(dev_addr, ep_in, p_buffer, length);
|
||||
}
|
||||
|
||||
bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_xfer_cb_t complete_cb)
|
||||
bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb)
|
||||
{
|
||||
cdch_data_t const * p_cdc = get_itf(dev_addr);
|
||||
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
|
||||
.wValue = (rts ? 2 : 0) | (dtr ? 1 : 0),
|
||||
.wIndex = p_cdc->itf_num,
|
||||
.wLength = 0
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_arg = 0
|
||||
.bRequest = CDC_REQUEST_SET_CONTROL_LINE_STATE,
|
||||
.wValue = (rts ? 2 : 0) | (dtr ? 1 : 0),
|
||||
.wIndex = p_cdc->itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(dev_addr, &xfer);
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
return tuh_control_xfer(&xfer);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -158,6 +161,7 @@ void cdch_init(void)
|
||||
|
||||
bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) max_len;
|
||||
|
||||
// Only support ACM subclass
|
||||
@ -193,7 +197,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
|
||||
// notification endpoint
|
||||
tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
|
||||
TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
|
||||
TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) );
|
||||
p_cdc->ep_notif = desc_ep->bEndpointAddress;
|
||||
|
||||
drv_len += tu_desc_len(p_desc);
|
||||
@ -214,7 +218,7 @@ bool cdch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *it
|
||||
tusb_desc_endpoint_t const *desc_ep = (tusb_desc_endpoint_t const *) p_desc;
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_BULK == desc_ep->bmAttributes.xfer);
|
||||
|
||||
TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep));
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
|
||||
|
||||
if ( tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN )
|
||||
{
|
||||
|
@ -42,14 +42,14 @@
|
||||
* \defgroup CDC_Serial_Host Host
|
||||
* @{ */
|
||||
|
||||
bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_control_xfer_cb_t complete_cb);
|
||||
bool tuh_cdc_set_control_line_state(uint8_t dev_addr, bool dtr, bool rts, tuh_xfer_cb_t complete_cb);
|
||||
|
||||
static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_control_xfer_cb_t complete_cb)
|
||||
static inline bool tuh_cdc_connect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb)
|
||||
{
|
||||
return tuh_cdc_set_control_line_state(dev_addr, true, true, complete_cb);
|
||||
}
|
||||
|
||||
static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_control_xfer_cb_t complete_cb)
|
||||
static inline bool tuh_cdc_disconnect(uint8_t dev_addr, tuh_xfer_cb_t complete_cb)
|
||||
{
|
||||
return tuh_cdc_set_control_line_state(dev_addr, false, false, complete_cb);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
/** \defgroup ClassDriver_HID_Common Common Definitions
|
||||
* @{ */
|
||||
|
||||
/// USB HID Descriptor
|
||||
/// USB HID Descriptor
|
||||
typedef struct TU_ATTR_PACKED
|
||||
{
|
||||
uint8_t bLength; /**< Numeric expression that is the total size of the HID descriptor */
|
||||
|
@ -103,19 +103,54 @@ uint8_t tuh_hid_get_protocol(uint8_t dev_addr, uint8_t instance)
|
||||
return hid_itf->protocol_mode;
|
||||
}
|
||||
|
||||
static bool set_protocol_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void set_protocol_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
uint8_t const itf_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(daddr, instance);
|
||||
|
||||
if (XFER_RESULT_SUCCESS == result) hid_itf->protocol_mode = (uint8_t) xfer->request.wValue;
|
||||
if (XFER_RESULT_SUCCESS == xfer->result)
|
||||
{
|
||||
hid_itf->protocol_mode = (uint8_t) tu_le16toh(xfer->setup->wValue);
|
||||
}
|
||||
|
||||
if (tuh_hid_set_protocol_complete_cb)
|
||||
{
|
||||
tuh_hid_set_protocol_complete_cb(dev_addr, instance, hid_itf->protocol_mode);
|
||||
tuh_hid_set_protocol_complete_cb(daddr, instance, hid_itf->protocol_mode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool _hidh_set_protocol(uint8_t dev_addr, uint8_t itf_num, uint8_t protocol, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
TU_LOG2("HID Set Protocol = %d\r\n", protocol);
|
||||
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
|
||||
.wValue = protocol,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -124,49 +159,24 @@ bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t instance, uint8_t protocol)
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
TU_VERIFY(hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE);
|
||||
|
||||
TU_LOG2("HID Set Protocol = %d\r\n", protocol);
|
||||
|
||||
tuh_control_xfer_t const xfer =
|
||||
{
|
||||
.request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
|
||||
.wValue = protocol,
|
||||
.wIndex = hid_itf->itf_num,
|
||||
.wLength = 0
|
||||
},
|
||||
|
||||
.buffer = NULL,
|
||||
.complete_cb = set_protocol_complete,
|
||||
.user_arg = 0
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) );
|
||||
return true;
|
||||
return _hidh_set_protocol(dev_addr, hid_itf->itf_num, protocol, set_protocol_complete, 0);
|
||||
}
|
||||
|
||||
static bool set_report_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void set_report_complete(tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_LOG2("HID Set Report complete\r\n");
|
||||
|
||||
if (tuh_hid_set_report_complete_cb)
|
||||
{
|
||||
uint8_t const itf_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const instance = get_instance_id_by_itfnum(xfer->daddr, itf_num);
|
||||
|
||||
uint8_t const report_type = tu_u16_high(xfer->request.wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->request.wValue);
|
||||
uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
|
||||
uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
|
||||
|
||||
tuh_hid_set_report_complete_cb(dev_addr, instance, report_id, report_type, (result == XFER_RESULT_SUCCESS) ? xfer->request.wLength : 0);
|
||||
tuh_hid_set_report_complete_cb(xfer->daddr, instance, report_id, report_type,
|
||||
(xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, uint8_t report_type, void* report, uint16_t len)
|
||||
@ -174,28 +184,64 @@ bool tuh_hid_set_report(uint8_t dev_addr, uint8_t instance, uint8_t report_id, u
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
TU_LOG2("HID Set Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
|
||||
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_REPORT,
|
||||
.wValue = tu_u16(report_type, report_id),
|
||||
.wIndex = hid_itf->itf_num,
|
||||
.wLength = len
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
|
||||
.buffer = report,
|
||||
.complete_cb = set_report_complete,
|
||||
.user_arg = 0
|
||||
.bRequest = HID_REQ_CONTROL_SET_REPORT,
|
||||
.wValue = tu_u16(report_type, report_id),
|
||||
.wIndex = hid_itf->itf_num,
|
||||
.wLength = len
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) );
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = report,
|
||||
.complete_cb = set_report_complete,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool _hidh_set_idle(uint8_t dev_addr, uint8_t itf_num, uint16_t idle_rate, tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
// SET IDLE request, device can stall if not support this request
|
||||
TU_LOG2("HID Set Idle \r\n");
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_IDLE,
|
||||
.wValue = idle_rate,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -210,7 +256,13 @@ bool tuh_hid_receive_report(uint8_t dev_addr, uint8_t instance)
|
||||
// claim endpoint
|
||||
TU_VERIFY( usbh_edpt_claim(dev_addr, hid_itf->ep_in) );
|
||||
|
||||
return usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size);
|
||||
if ( !usbh_edpt_xfer(dev_addr, hid_itf->ep_in, hid_itf->epin_buf, hid_itf->epin_size) )
|
||||
{
|
||||
usbh_edpt_claim(dev_addr, hid_itf->ep_in);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance)
|
||||
@ -270,14 +322,9 @@ void hidh_close(uint8_t dev_addr)
|
||||
// Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool config_set_protocol (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static bool config_get_report_desc (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static bool config_get_report_desc_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
|
||||
static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
|
||||
|
||||
bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
(void) max_len;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass);
|
||||
@ -309,7 +356,7 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
|
||||
for(int i = 0; i < desc_itf->bNumEndpoints; i++)
|
||||
{
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType);
|
||||
TU_ASSERT( usbh_edpt_open(rhport, dev_addr, desc_ep) );
|
||||
TU_ASSERT( tuh_edpt_open(dev_addr, desc_ep) );
|
||||
|
||||
if(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN)
|
||||
{
|
||||
@ -341,121 +388,93 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Set Configure
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
enum {
|
||||
CONFG_SET_IDLE,
|
||||
CONFIG_SET_PROTOCOL,
|
||||
CONFIG_GET_REPORT_DESC,
|
||||
CONFIG_COMPLETE
|
||||
};
|
||||
|
||||
static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
|
||||
static void process_set_config(tuh_xfer_t* xfer);
|
||||
|
||||
bool hidh_set_config(uint8_t dev_addr, uint8_t itf_num)
|
||||
{
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
tusb_control_request_t request;
|
||||
request.wIndex = tu_htole16((uint16_t) itf_num);
|
||||
|
||||
// Idle rate = 0 mean only report when there is changes
|
||||
uint16_t const idle_rate = 0;
|
||||
tuh_xfer_t xfer;
|
||||
xfer.daddr = dev_addr;
|
||||
xfer.result = XFER_RESULT_SUCCESS;
|
||||
xfer.setup = &request;
|
||||
xfer.user_data = CONFG_SET_IDLE;
|
||||
|
||||
// SET IDLE request, device can stall if not support this request
|
||||
TU_LOG2("HID Set Idle \r\n");
|
||||
tuh_control_xfer_t const xfer =
|
||||
{
|
||||
.request =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_IDLE,
|
||||
.wValue = idle_rate,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 0
|
||||
},
|
||||
|
||||
.buffer = NULL,
|
||||
.complete_cb = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? config_set_protocol : config_get_report_desc,
|
||||
.user_arg = 0
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) );
|
||||
// fake request to kick-off the set config process
|
||||
process_set_config(&xfer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Force device to work in BOOT protocol
|
||||
static bool config_set_protocol(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void process_set_config(tuh_xfer_t* xfer)
|
||||
{
|
||||
// Stall is a valid response for SET_IDLE, therefore we could ignore its result
|
||||
(void) result;
|
||||
|
||||
uint8_t const itf_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
|
||||
TU_LOG2("HID Set Protocol to Boot Mode\r\n");
|
||||
hid_itf->protocol_mode = HID_PROTOCOL_BOOT;
|
||||
tuh_control_xfer_t const new_xfer =
|
||||
if ( xfer->setup->bRequest != HID_REQ_CONTROL_SET_IDLE )
|
||||
{
|
||||
.request =
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
}
|
||||
|
||||
uintptr_t const state = xfer->user_data;
|
||||
uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
|
||||
uint8_t const instance = get_instance_id_by_itfnum(daddr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(daddr, instance);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case CONFG_SET_IDLE:
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
// Idle rate = 0 mean only report when there is changes
|
||||
const uint16_t idle_rate = 0;
|
||||
const uintptr_t next_state = (hid_itf->itf_protocol != HID_ITF_PROTOCOL_NONE) ? CONFIG_SET_PROTOCOL : CONFIG_GET_REPORT_DESC;
|
||||
_hidh_set_idle(daddr, itf_num, idle_rate, process_set_config, next_state);
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_SET_PROTOCOL:
|
||||
_hidh_set_protocol(daddr, hid_itf->itf_num, HID_PROTOCOL_BOOT, process_set_config, CONFIG_GET_REPORT_DESC);
|
||||
break;
|
||||
|
||||
case CONFIG_GET_REPORT_DESC:
|
||||
// Get Report Descriptor if possible
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HID_REQ_CONTROL_SET_PROTOCOL,
|
||||
.wValue = HID_PROTOCOL_BOOT,
|
||||
.wIndex = hid_itf->itf_num,
|
||||
.wLength = 0
|
||||
},
|
||||
TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len);
|
||||
|
||||
.buffer = NULL,
|
||||
.complete_cb = config_get_report_desc,
|
||||
.user_arg = 0
|
||||
};
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(daddr, instance, NULL, 0);
|
||||
}else
|
||||
{
|
||||
tuh_descriptor_get_hid_report(daddr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, process_set_config, CONFIG_COMPLETE);
|
||||
}
|
||||
break;
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(dev_addr, &new_xfer) );
|
||||
return true;
|
||||
}
|
||||
case CONFIG_COMPLETE:
|
||||
{
|
||||
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||
uint16_t const desc_len = tu_le16toh(xfer->setup->wLength);
|
||||
|
||||
static bool config_get_report_desc(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
{
|
||||
// We can be here after SET_IDLE or SET_PROTOCOL (boot device)
|
||||
// Trigger assert if result is not successful with set protocol
|
||||
if ( xfer->request.bRequest != HID_REQ_CONTROL_SET_IDLE )
|
||||
{
|
||||
TU_ASSERT(result == XFER_RESULT_SUCCESS);
|
||||
config_driver_mount_complete(daddr, instance, desc_report, desc_len);
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
uint8_t const itf_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
|
||||
|
||||
// Get Report Descriptor if possible
|
||||
// using usbh enumeration buffer since report descriptor can be very long
|
||||
if( hid_itf->report_desc_len > CFG_TUH_ENUMERATION_BUFSIZE )
|
||||
{
|
||||
TU_LOG2("HID Skip Report Descriptor since it is too large %u bytes\r\n", hid_itf->report_desc_len);
|
||||
|
||||
// Driver is mounted without report descriptor
|
||||
config_driver_mount_complete(dev_addr, instance, NULL, 0);
|
||||
}else
|
||||
{
|
||||
TU_ASSERT(tuh_descriptor_get_hid_report(dev_addr, itf_num, hid_itf->report_desc_type, 0, usbh_get_enum_buf(), hid_itf->report_desc_len, config_get_report_desc_complete, 0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_get_report_desc_complete(uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
{
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
|
||||
uint8_t const itf_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const instance = get_instance_id_by_itfnum(dev_addr, itf_num);
|
||||
|
||||
uint8_t const* desc_report = usbh_get_enum_buf();
|
||||
uint16_t const desc_len = xfer->request.wLength;
|
||||
|
||||
config_driver_mount_complete(dev_addr, instance, desc_report, desc_len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
|
||||
|
@ -358,13 +358,14 @@ bool msch_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t event, uint32
|
||||
// MSC Enumeration
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool config_get_maxlun_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static void config_get_maxlun_complete (tuh_xfer_t* xfer);
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
|
||||
static bool config_request_sense_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
|
||||
static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw);
|
||||
|
||||
bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *desc_itf, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
TU_VERIFY (MSC_SUBCLASS_SCSI == desc_itf->bInterfaceSubClass &&
|
||||
MSC_PROTOCOL_BOT == desc_itf->bInterfaceProtocol);
|
||||
|
||||
@ -378,7 +379,7 @@ bool msch_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
|
||||
for(uint32_t i=0; i<2; i++)
|
||||
{
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == ep_desc->bDescriptorType && TUSB_XFER_BULK == ep_desc->bmAttributes.xfer);
|
||||
TU_ASSERT(usbh_edpt_open(rhport, dev_addr, ep_desc));
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, ep_desc));
|
||||
|
||||
if ( tu_edpt_dir(ep_desc->bEndpointAddress) == TUSB_DIR_IN )
|
||||
{
|
||||
@ -405,47 +406,47 @@ bool msch_set_config(uint8_t dev_addr, uint8_t itf_num)
|
||||
|
||||
//------------- Get Max Lun -------------//
|
||||
TU_LOG2("MSC Get Max Lun\r\n");
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||
.wValue = 0,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 1
|
||||
.recipient = TUSB_REQ_RCPT_INTERFACE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = MSC_REQ_GET_MAX_LUN,
|
||||
.wValue = 0,
|
||||
.wIndex = itf_num,
|
||||
.wLength = 1
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = &p_msc->max_lun,
|
||||
.complete_cb = config_get_maxlun_complete,
|
||||
.user_arg = 0
|
||||
.user_data = 0
|
||||
};
|
||||
TU_ASSERT(tuh_control_xfer(dev_addr, &xfer));
|
||||
TU_ASSERT(tuh_control_xfer(&xfer));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_get_maxlun_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void config_get_maxlun_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
(void) xfer;
|
||||
|
||||
msch_interface_t* p_msc = get_itf(dev_addr);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
msch_interface_t* p_msc = get_itf(daddr);
|
||||
|
||||
// STALL means zero
|
||||
p_msc->max_lun = (XFER_RESULT_SUCCESS == result) ? _msch_buffer[0] : 0;
|
||||
p_msc->max_lun = (XFER_RESULT_SUCCESS == xfer->result) ? _msch_buffer[0] : 0;
|
||||
p_msc->max_lun++; // MAX LUN is minus 1 by specs
|
||||
|
||||
// TODO multiple LUN support
|
||||
TU_LOG2("SCSI Test Unit Ready\r\n");
|
||||
uint8_t const lun = 0;
|
||||
tuh_msc_test_unit_ready(dev_addr, lun, config_test_unit_ready_complete);
|
||||
|
||||
return true;
|
||||
tuh_msc_test_unit_ready(daddr, lun, config_test_unit_ready_complete);
|
||||
}
|
||||
|
||||
static bool config_test_unit_ready_complete(uint8_t dev_addr, msc_cbw_t const* cbw, msc_csw_t const* csw)
|
||||
@ -483,7 +484,7 @@ static bool config_read_capacity_complete(uint8_t dev_addr, msc_cbw_t const* cbw
|
||||
// Capacity response field: Block size and Last LBA are both Big-Endian
|
||||
scsi_read_capacity10_resp_t* resp = (scsi_read_capacity10_resp_t*) ((void*) _msch_buffer);
|
||||
p_msc->capacity[cbw->lun].block_count = tu_ntohl(resp->last_lba) + 1;
|
||||
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
|
||||
p_msc->capacity[cbw->lun].block_size = tu_ntohl(resp->block_size);
|
||||
|
||||
// Mark enumeration is complete
|
||||
p_msc->mounted = true;
|
||||
|
227
src/host/hub.c
227
src/host/hub.c
@ -78,89 +78,98 @@ static char const* const _hub_feature_str[] =
|
||||
// HUB
|
||||
//--------------------------------------------------------------------+
|
||||
bool hub_port_clear_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg)
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_CLEAR_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_CLEAR_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_arg = user_arg
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_LOG2("HUB Clear Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT( tuh_control_xfer(hub_addr, &xfer) );
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_port_set_feature(uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg)
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_SET_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_OUT
|
||||
},
|
||||
.bRequest = HUB_REQUEST_SET_FEATURE,
|
||||
.wValue = feature,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 0
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = NULL,
|
||||
.complete_cb = complete_cb,
|
||||
.user_arg = user_arg
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_LOG2("HUB Set Feature: %s, addr = %u port = %u\r\n", _hub_feature_str[feature], hub_addr, hub_port);
|
||||
TU_ASSERT( tuh_control_xfer(hub_addr, &xfer) );
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hub_port_get_status(uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg)
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HUB_REQUEST_GET_STATUS,
|
||||
.wValue = 0,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 4
|
||||
.recipient = TUSB_REQ_RCPT_OTHER,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HUB_REQUEST_GET_STATUS,
|
||||
.wValue = 0,
|
||||
.wIndex = hub_port,
|
||||
.wLength = 4
|
||||
};
|
||||
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = hub_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = resp,
|
||||
.complete_cb = complete_cb,
|
||||
.user_arg = user_arg
|
||||
.user_data = user_data
|
||||
};
|
||||
|
||||
TU_LOG2("HUB Get Port Status: addr = %u port = %u\r\n", hub_addr, hub_port);
|
||||
TU_ASSERT( tuh_control_xfer( hub_addr, &xfer) );
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -174,6 +183,8 @@ void hub_init(void)
|
||||
|
||||
bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
|
||||
{
|
||||
(void) rhport;
|
||||
|
||||
TU_VERIFY(TUSB_CLASS_HUB == itf_desc->bInterfaceClass &&
|
||||
0 == itf_desc->bInterfaceSubClass);
|
||||
|
||||
@ -190,7 +201,7 @@ bool hub_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *itf
|
||||
TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType &&
|
||||
TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0);
|
||||
|
||||
TU_ASSERT(usbh_edpt_open(rhport, dev_addr, desc_ep));
|
||||
TU_ASSERT(tuh_edpt_open(dev_addr, desc_ep));
|
||||
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
|
||||
@ -219,8 +230,8 @@ bool hub_edpt_status_xfer(uint8_t dev_addr)
|
||||
// Set Configure
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static bool config_port_power_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static void config_set_port_power (tuh_xfer_t* xfer);
|
||||
static void config_port_power_complete (tuh_xfer_t* xfer);
|
||||
|
||||
bool hub_set_config(uint8_t dev_addr, uint8_t itf_num)
|
||||
{
|
||||
@ -228,38 +239,41 @@ bool hub_set_config(uint8_t dev_addr, uint8_t itf_num)
|
||||
TU_ASSERT(itf_num == p_hub->itf_num);
|
||||
|
||||
// Get Hub Descriptor
|
||||
tuh_control_xfer_t const xfer =
|
||||
tusb_control_request_t const request =
|
||||
{
|
||||
.request =
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.bmRequestType_bit =
|
||||
{
|
||||
.recipient = TUSB_REQ_RCPT_DEVICE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
.bRequest = HUB_REQUEST_GET_DESCRIPTOR,
|
||||
.wValue = 0,
|
||||
.wIndex = 0,
|
||||
.wLength = sizeof(descriptor_hub_desc_t)
|
||||
.recipient = TUSB_REQ_RCPT_DEVICE,
|
||||
.type = TUSB_REQ_TYPE_CLASS,
|
||||
.direction = TUSB_DIR_IN
|
||||
},
|
||||
|
||||
.buffer = _hub_buffer,
|
||||
.complete_cb = config_set_port_power,
|
||||
.user_arg = 0
|
||||
.bRequest = HUB_REQUEST_GET_DESCRIPTOR,
|
||||
.wValue = 0,
|
||||
.wIndex = 0,
|
||||
.wLength = sizeof(descriptor_hub_desc_t)
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(dev_addr, &xfer) );
|
||||
tuh_xfer_t xfer =
|
||||
{
|
||||
.daddr = dev_addr,
|
||||
.ep_addr = 0,
|
||||
.setup = &request,
|
||||
.buffer = _hub_buffer,
|
||||
.complete_cb = config_set_port_power,
|
||||
.user_data = 0
|
||||
};
|
||||
|
||||
TU_ASSERT( tuh_control_xfer(&xfer) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void config_set_port_power (tuh_xfer_t* xfer)
|
||||
{
|
||||
(void) xfer;
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
|
||||
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_itf(daddr);
|
||||
|
||||
// only use number of ports in hub descriptor
|
||||
descriptor_hub_desc_t const* desc_hub = (descriptor_hub_desc_t const*) _hub_buffer;
|
||||
@ -269,38 +283,38 @@ static bool config_set_port_power (uint8_t dev_addr, tuh_control_xfer_t const *
|
||||
|
||||
// Set Port Power to be able to detect connection, starting with port 1
|
||||
uint8_t const hub_port = 1;
|
||||
return hub_port_set_feature(dev_addr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
}
|
||||
|
||||
static bool config_port_power_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void config_port_power_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == result);
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
TU_ASSERT(XFER_RESULT_SUCCESS == xfer->result, );
|
||||
|
||||
if (xfer->request.wIndex == p_hub->port_count)
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_itf(daddr);
|
||||
|
||||
if (xfer->setup->wIndex == p_hub->port_count)
|
||||
{
|
||||
// All ports are power -> queue notification status endpoint and
|
||||
// complete the SET CONFIGURATION
|
||||
TU_ASSERT( usbh_edpt_xfer(dev_addr, p_hub->ep_in, &p_hub->status_change, 1) );
|
||||
TU_ASSERT( usbh_edpt_xfer(daddr, p_hub->ep_in, &p_hub->status_change, 1), );
|
||||
|
||||
usbh_driver_set_config_complete(dev_addr, p_hub->itf_num);
|
||||
usbh_driver_set_config_complete(daddr, p_hub->itf_num);
|
||||
}else
|
||||
{
|
||||
// power next port
|
||||
uint8_t const hub_port = (uint8_t) (xfer->request.wIndex + 1);
|
||||
return hub_port_set_feature(dev_addr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
uint8_t const hub_port = (uint8_t) (xfer->setup->wIndex + 1);
|
||||
hub_port_set_feature(daddr, hub_port, HUB_FEATURE_PORT_POWER, config_port_power_complete, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Connection Changes
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static bool connection_clear_conn_change_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static bool connection_port_reset_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
static void connection_get_status_complete (tuh_xfer_t* xfer);
|
||||
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer);
|
||||
static void connection_port_reset_complete (tuh_xfer_t* xfer);
|
||||
|
||||
// callback as response of interrupt endpoint polling
|
||||
bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
|
||||
@ -328,12 +342,13 @@ bool hub_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void connection_get_status_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_ASSERT(result == XFER_RESULT_SUCCESS);
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
uint8_t const port_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_itf(daddr);
|
||||
uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
|
||||
// Connection change
|
||||
if (p_hub->port_status.change.connection)
|
||||
@ -342,7 +357,7 @@ static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t
|
||||
//TU_VERIFY(port_status.status_current.port_power && port_status.status_current.port_enable, );
|
||||
|
||||
// Acknowledge Port Connection Change
|
||||
hub_port_clear_feature(dev_addr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
|
||||
hub_port_clear_feature(daddr, port_num, HUB_FEATURE_PORT_CONNECTION_CHANGE, connection_clear_conn_change_complete, 0);
|
||||
}else
|
||||
{
|
||||
// Other changes are: Enable, Suspend, Over Current, Reset, L1 state
|
||||
@ -350,65 +365,61 @@ static bool connection_get_status_complete (uint8_t dev_addr, tuh_control_xfer_t
|
||||
|
||||
// prepare for next hub status
|
||||
// TODO continue with status_change, or maybe we can do it again with status
|
||||
hub_edpt_status_xfer(dev_addr);
|
||||
hub_edpt_status_xfer(daddr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool connection_clear_conn_change_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void connection_clear_conn_change_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_ASSERT(result == XFER_RESULT_SUCCESS);
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
|
||||
hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
uint8_t const port_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
hub_interface_t* p_hub = get_itf(daddr);
|
||||
uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
|
||||
if ( p_hub->port_status.status.connection )
|
||||
{
|
||||
// Reset port if attach event
|
||||
hub_port_reset(dev_addr, port_num, connection_port_reset_complete, 0);
|
||||
hub_port_reset(daddr, port_num, connection_port_reset_complete, 0);
|
||||
}else
|
||||
{
|
||||
// submit detach event
|
||||
hcd_event_t event =
|
||||
{
|
||||
.rhport = usbh_get_rhport(dev_addr),
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = HCD_EVENT_DEVICE_REMOVE,
|
||||
.connection =
|
||||
{
|
||||
.hub_addr = dev_addr,
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
|
||||
hcd_event_handler(&event, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool connection_port_reset_complete (uint8_t dev_addr, tuh_control_xfer_t const * xfer, xfer_result_t result)
|
||||
static void connection_port_reset_complete (tuh_xfer_t* xfer)
|
||||
{
|
||||
TU_ASSERT(result == XFER_RESULT_SUCCESS);
|
||||
TU_ASSERT(xfer->result == XFER_RESULT_SUCCESS, );
|
||||
|
||||
// hub_interface_t* p_hub = get_itf(dev_addr);
|
||||
uint8_t const port_num = (uint8_t) xfer->request.wIndex;
|
||||
uint8_t const daddr = xfer->daddr;
|
||||
// hub_interface_t* p_hub = get_itf(daddr);
|
||||
uint8_t const port_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
|
||||
|
||||
// submit attach event
|
||||
hcd_event_t event =
|
||||
{
|
||||
.rhport = usbh_get_rhport(dev_addr),
|
||||
.rhport = usbh_get_rhport(daddr),
|
||||
.event_id = HCD_EVENT_DEVICE_ATTACH,
|
||||
.connection =
|
||||
{
|
||||
.hub_addr = dev_addr,
|
||||
.hub_addr = daddr,
|
||||
.hub_port = port_num
|
||||
}
|
||||
};
|
||||
|
||||
hcd_event_handler(&event, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -173,31 +173,31 @@ TU_VERIFY_STATIC( sizeof(hub_port_status_response_t) == 4, "size is not correct"
|
||||
|
||||
// Clear feature
|
||||
bool hub_port_clear_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Set feature
|
||||
bool hub_port_set_feature (uint8_t hub_addr, uint8_t hub_port, uint8_t feature,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get port status
|
||||
bool hub_port_get_status (uint8_t hub_addr, uint8_t hub_port, void* resp,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get status from Interrupt endpoint
|
||||
bool hub_edpt_status_xfer(uint8_t dev_addr);
|
||||
|
||||
// Reset a port
|
||||
static inline bool hub_port_reset(uint8_t hub_addr, uint8_t hub_port,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg)
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_arg);
|
||||
return hub_port_set_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET, complete_cb, user_data);
|
||||
}
|
||||
|
||||
// Clear Reset Change
|
||||
static inline bool hub_port_clear_reset_change(uint8_t hub_addr, uint8_t hub_port,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg)
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data)
|
||||
{
|
||||
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_arg);
|
||||
return hub_port_clear_feature(hub_addr, hub_port, HUB_FEATURE_PORT_RESET_CHANGE, complete_cb, user_data);
|
||||
}
|
||||
|
||||
|
||||
|
1132
src/host/usbh.c
1132
src/host/usbh.c
File diff suppressed because it is too large
Load Diff
@ -39,17 +39,34 @@
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// forward declaration
|
||||
struct tuh_control_xfer_s;
|
||||
typedef struct tuh_control_xfer_s tuh_control_xfer_t;
|
||||
struct tuh_xfer_s;
|
||||
typedef struct tuh_xfer_s tuh_xfer_t;
|
||||
|
||||
typedef bool (*tuh_control_xfer_cb_t)(uint8_t daddr, tuh_control_xfer_t const * xfer, xfer_result_t result);
|
||||
typedef void (*tuh_xfer_cb_t)(tuh_xfer_t* xfer);
|
||||
|
||||
struct tuh_control_xfer_s
|
||||
// Note1: layout and order of this will be changed in near future
|
||||
// it is advised to initialize it using member name
|
||||
// Note2: not all field is available/meaningful in callback, some info is not saved by
|
||||
// usbh to save SRAM
|
||||
struct tuh_xfer_s
|
||||
{
|
||||
tusb_control_request_t request TU_ATTR_ALIGNED(4);
|
||||
uint8_t* buffer;
|
||||
tuh_control_xfer_cb_t complete_cb;
|
||||
uintptr_t user_arg;
|
||||
uint8_t daddr;
|
||||
uint8_t ep_addr;
|
||||
|
||||
xfer_result_t result;
|
||||
uint32_t actual_len; // excluding setup packet
|
||||
|
||||
union
|
||||
{
|
||||
tusb_control_request_t const* setup; // setup packet pointer if control transfer
|
||||
uint32_t buflen; // expected length if not control transfer (not available in callback)
|
||||
};
|
||||
|
||||
uint8_t* buffer; // not available in callback if not control transfer
|
||||
tuh_xfer_cb_t complete_cb;
|
||||
uintptr_t user_data;
|
||||
|
||||
// uint32_t timeout_ms; // place holder, not supported yet
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
@ -104,21 +121,28 @@ static inline bool tuh_ready(uint8_t daddr)
|
||||
return tuh_mounted(daddr) && !tuh_suspended(daddr);
|
||||
}
|
||||
|
||||
// Carry out a control transfer
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable
|
||||
bool tuh_control_xfer (uint8_t daddr, tuh_control_xfer_t const* xfer);
|
||||
//--------------------------------------------------------------------+
|
||||
// Transfer API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Sync (blocking) version of tuh_control_xfer()
|
||||
// return transfer result
|
||||
uint8_t tuh_control_xfer_sync(uint8_t daddr, tuh_control_xfer_t const* xfer, uint32_t timeout_ms);
|
||||
// Submit a control transfer
|
||||
// - async: complete callback invoked when finished.
|
||||
// - sync : blocking if complete callback is NULL.
|
||||
bool tuh_control_xfer(tuh_xfer_t* xfer);
|
||||
|
||||
// Submit a bulk/interrupt transfer
|
||||
// - async: complete callback invoked when finished.
|
||||
// - sync : blocking if complete callback is NULL.
|
||||
bool tuh_edpt_xfer(tuh_xfer_t* xfer);
|
||||
|
||||
// Open an non-control endpoint
|
||||
bool tuh_edpt_open(uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep);
|
||||
|
||||
// Set Configuration (control transfer)
|
||||
// config_num = 0 will un-configure device. Note: config_num = config_descriptor_index + 1
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable
|
||||
bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors Asynchronous (non-blocking)
|
||||
@ -127,43 +151,43 @@ bool tuh_configuration_set(uint8_t daddr, uint8_t config_num,
|
||||
// Get an descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get device descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_device(uint8_t daddr, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get configuration descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_configuration(uint8_t daddr, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get HID report descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_hid_report(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
// Blocking if complete callback is NULL, in this case 'user_arg' must contain xfer_result_t variable
|
||||
// Blocking if complete callback is NULL, in this case 'user_data' must contain xfer_result_t variable
|
||||
bool tuh_descriptor_get_string(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get manufacturer string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_manufacturer_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get product string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_product_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
// Get serial string descriptor (control transfer)
|
||||
// true on success, false if there is on-going control transfer or incorrect parameters
|
||||
bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len,
|
||||
tuh_control_xfer_cb_t complete_cb, uintptr_t user_arg);
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Descriptors Synchronous (blocking)
|
||||
@ -171,35 +195,35 @@ bool tuh_descriptor_get_serial_string(uint8_t daddr, uint16_t language_id, void*
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_sync(uint8_t daddr, uint8_t type, uint8_t index, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_device()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_device_sync(uint8_t daddr, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_configuration()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_configuration_sync(uint8_t daddr, uint8_t index, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_hid_report()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_hid_report_sync(uint8_t daddr, uint8_t itf_num, uint8_t desc_type, uint8_t index, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_string()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_string_sync(uint8_t daddr, uint8_t index, uint16_t language_id, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_manufacturer_string()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_manufacturer_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_product_string()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_product_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
|
||||
|
||||
// Sync (blocking) version of tuh_descriptor_get_serial_string()
|
||||
// return transfer result
|
||||
uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len, uint8_t timeout_ms);
|
||||
uint8_t tuh_descriptor_get_serial_string_sync(uint8_t daddr, uint16_t language_id, void* buffer, uint16_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -63,11 +63,16 @@ void usbh_int_set(bool enabled);
|
||||
// USBH Endpoint API
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Open an endpoint
|
||||
bool usbh_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const * desc_ep);
|
||||
// Submit a usb transfer with callback support, require CFG_TUH_API_EDPT_XFER
|
||||
bool usbh_edpt_xfer_with_callback(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes,
|
||||
tuh_xfer_cb_t complete_cb, uintptr_t user_data);
|
||||
|
||||
TU_ATTR_ALWAYS_INLINE
|
||||
static inline bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
|
||||
{
|
||||
return usbh_edpt_xfer_with_callback(dev_addr, ep_addr, buffer, total_bytes, NULL, 0);
|
||||
}
|
||||
|
||||
// Submit a usb transfer
|
||||
bool usbh_edpt_xfer(uint8_t dev_addr, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes);
|
||||
|
||||
// Claim an endpoint before submitting a transfer.
|
||||
// If caller does not make any transfer, it must release endpoint for others.
|
||||
|
@ -656,6 +656,26 @@ static void xfer_error_isr(uint8_t hostid)
|
||||
}
|
||||
}
|
||||
|
||||
#if CFG_TUSB_DEBUG >= EHCI_DBG
|
||||
|
||||
static inline void print_portsc(ehci_registers_t* regs)
|
||||
{
|
||||
TU_LOG_HEX(EHCI_DBG, regs->portsc);
|
||||
TU_LOG(EHCI_DBG, " Current Connect Status: %u\r\n", regs->portsc_bm.current_connect_status);
|
||||
TU_LOG(EHCI_DBG, " Connect Status Change : %u\r\n", regs->portsc_bm.connect_status_change);
|
||||
TU_LOG(EHCI_DBG, " Port Enabled : %u\r\n", regs->portsc_bm.port_enabled);
|
||||
TU_LOG(EHCI_DBG, " Port Enabled Change : %u\r\n", regs->portsc_bm.port_enable_change);
|
||||
|
||||
TU_LOG(EHCI_DBG, " Port Reset : %u\r\n", regs->portsc_bm.port_reset);
|
||||
TU_LOG(EHCI_DBG, " Port Power : %u\r\n", regs->portsc_bm.port_power);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define print_portsc(_reg)
|
||||
|
||||
#endif
|
||||
|
||||
//------------- Host Controller Driver's Interrupt Handler -------------//
|
||||
void hcd_int_handler(uint8_t rhport)
|
||||
{
|
||||
@ -675,9 +695,8 @@ void hcd_int_handler(uint8_t rhport)
|
||||
|
||||
if (int_status & EHCI_INT_MASK_PORT_CHANGE)
|
||||
{
|
||||
uint32_t port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
|
||||
|
||||
TU_LOG_HEX(EHCI_DBG, regs->portsc);
|
||||
uint32_t const port_status = regs->portsc & EHCI_PORTSC_MASK_ALL;
|
||||
print_portsc(regs);
|
||||
|
||||
if (regs->portsc_bm.connect_status_change)
|
||||
{
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include "osal/osal.h"
|
||||
#include "common/tusb_fifo.h"
|
||||
|
||||
#include "class/hid/hid.h"
|
||||
|
||||
//------------- HOST -------------//
|
||||
#if CFG_TUH_ENABLED
|
||||
#include "host/usbh.h"
|
||||
|
@ -392,6 +392,10 @@
|
||||
#define CFG_TUH_VENDOR 0
|
||||
#endif
|
||||
|
||||
#ifndef CFG_TUH_API_EDPT_XFER
|
||||
#define CFG_TUH_API_EDPT_XFER 0
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Configuration Validation
|
||||
//------------------------------------------------------------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user