more host hid API

adding hid parser
This commit is contained in:
hathach 2021-05-14 01:02:47 +07:00
parent 641f55f1f1
commit 9ddc3bfd6d
4 changed files with 196 additions and 53 deletions

View File

@ -86,11 +86,7 @@
// Max number of reports per interface
// E.g composite HID with keyboard + mouse + gamepad will have 3 reports
#define CFG_TUH_HID_REPORT_MAX 4
// Max buffer
#define CFG_TUH_HID_REPORT_DESCRIPTOR_BUFSIZE 256
#define CFG_TUH_HID_REPORT_MAX 4
#ifdef __cplusplus
}

View File

@ -479,6 +479,7 @@ typedef enum
//--------------------------------------------------------------------+
// REPORT DESCRIPTOR
//--------------------------------------------------------------------+
//------------- ITEM & TAG -------------//
#define HID_REPORT_DATA_0(data)
#define HID_REPORT_DATA_1(data) , data
@ -488,18 +489,31 @@ typedef enum
#define HID_REPORT_ITEM(data, tag, type, size) \
(((tag) << 4) | ((type) << 2) | (size)) HID_REPORT_DATA_##size(data)
#define RI_TYPE_MAIN 0
#define RI_TYPE_GLOBAL 1
#define RI_TYPE_LOCAL 2
// Report Item Types
enum {
RI_TYPE_MAIN = 0,
RI_TYPE_GLOBAL = 1,
RI_TYPE_LOCAL = 2
};
//------------- Main Items - HID 1.11 section 6.2.2.4 -------------//
// Report Item Main group
enum {
RI_MAIN_INPUT = 8,
RI_MAIN_OUTPUT = 9,
RI_MAIN_COLLECTION = 10,
RI_MAIN_FEATURE = 11,
RI_MAIN_COLLECTION_END = 12
};
//------------- MAIN ITEMS 6.2.2.4 -------------//
#define HID_INPUT(x) HID_REPORT_ITEM(x, 8, RI_TYPE_MAIN, 1)
#define HID_OUTPUT(x) HID_REPORT_ITEM(x, 9, RI_TYPE_MAIN, 1)
#define HID_COLLECTION(x) HID_REPORT_ITEM(x, 10, RI_TYPE_MAIN, 1)
#define HID_FEATURE(x) HID_REPORT_ITEM(x, 11, RI_TYPE_MAIN, 1)
#define HID_COLLECTION_END HID_REPORT_ITEM(x, 12, RI_TYPE_MAIN, 0)
//------------- INPUT, OUTPUT, FEATURE 6.2.2.5 -------------//
//------------- Input, Output, Feature - HID 1.11 section 6.2.2.5 -------------//
#define HID_DATA (0<<0)
#define HID_CONSTANT (1<<0)
@ -527,7 +541,7 @@ typedef enum
#define HID_BITFIELD (0<<8)
#define HID_BUFFERED_BYTES (1<<8)
//------------- COLLECTION ITEM 6.2.2.6 -------------//
//------------- Collection Item - HID 1.11 section 6.2.2.6 -------------//
enum {
HID_COLLECTION_PHYSICAL = 0,
HID_COLLECTION_APPLICATION,
@ -538,7 +552,24 @@ enum {
HID_COLLECTION_USAGE_MODIFIER
};
//------------- GLOBAL ITEMS 6.2.2.7 -------------//
//------------- Global Items - HID 1.11 section 6.2.2.7 -------------//
// Report Item Global group
enum {
RI_GLOBAL_USAGE_PAGE = 0,
RI_GLOBAL_LOGICAL_MIN = 1,
RI_GLOBAL_LOGICAL_MAX = 2,
RI_GLOBAL_PHYSICAL_MIN = 3,
RI_GLOBAL_PHYSICAL_MAX = 4,
RI_GLOBAL_UNIT_EXPONENT = 5,
RI_GLOBAL_UNIT = 6,
RI_GLOBAL_REPORT_SIZE = 7,
RI_GLOBAL_REPORT_ID = 8,
RI_GLOBAL_REPORT_COUNT = 9,
RI_GLOBAL_PUSH = 10,
RI_GLOBAL_POP = 11
};
#define HID_USAGE_PAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, 1)
#define HID_USAGE_PAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_GLOBAL, n)
@ -573,6 +604,21 @@ enum {
#define HID_POP HID_REPORT_ITEM(x, 11, RI_TYPE_GLOBAL, 0)
//------------- LOCAL ITEMS 6.2.2.8 -------------//
enum {
RI_LOCAL_USAGE = 0,
RI_LOCAL_USAGE_MIN = 1,
RI_LOCAL_USAGE_MAX = 2,
RI_LOCAL_DESIGNATOR_INDEX = 3,
RI_LOCAL_DESIGNATOR_MIN = 4,
RI_LOCAL_DESIGNATOR_MAX = 5,
// 6 is reserved
RI_LOCAL_STRING_INDEX = 7,
RI_LOCAL_STRING_MIN = 8,
RI_LOCAL_STRING_MAX = 9,
RI_LOCAL_DELIMITER = 10,
};
#define HID_USAGE(x) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, 1)
#define HID_USAGE_N(x, n) HID_REPORT_ITEM(x, 0, RI_TYPE_LOCAL, n)

View File

@ -35,14 +35,15 @@
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
/* "KEYBOARD" : in_len=8 , out_len=1, usage_page=0x01, usage=0x06 # Generic Desktop, Keyboard
"MOUSE" : in_len=4 , out_len=0, usage_page=0x01, usage=0x02 # Generic Desktop, Mouse
"CONSUMER" : in_len=2 , out_len=0, usage_page=0x0C, usage=0x01 # Consumer, Consumer Control
"SYS_CONTROL" : in_len=1 , out_len=0, usage_page=0x01, usage=0x80 # Generic Desktop, Sys Control
"GAMEPAD" : in_len=6 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"DIGITIZER" : in_len=5 , out_len=0, usage_page=0x0D, usage=0x02 # Digitizers, Pen
"XAC_COMPATIBLE_GAMEPAD" : in_len=3 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"RAW" : in_len=64, out_len=0, usage_page=0xFFAF, usage=0xAF # Vendor 0xFFAF "Adafruit", 0xAF
/*
"KEYBOARD" : in_len=8 , out_len=1, usage_page=0x01, usage=0x06 # Generic Desktop, Keyboard
"MOUSE" : in_len=4 , out_len=0, usage_page=0x01, usage=0x02 # Generic Desktop, Mouse
"CONSUMER" : in_len=2 , out_len=0, usage_page=0x0C, usage=0x01 # Consumer, Consumer Control
"SYS_CONTROL" : in_len=1 , out_len=0, usage_page=0x01, usage=0x80 # Generic Desktop, Sys Control
"GAMEPAD" : in_len=6 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"DIGITIZER" : in_len=5 , out_len=0, usage_page=0x0D, usage=0x02 # Digitizers, Pen
"XAC_COMPATIBLE_GAMEPAD" : in_len=3 , out_len=0, usage_page=0x01, usage=0x05 # Generic Desktop, Game Pad
"RAW" : in_len=64, out_len=0, usage_page=0xFFAF, usage=0xAF # Vendor 0xFFAF "Adafruit", 0xAF
*/
typedef struct
{
@ -57,22 +58,14 @@ typedef struct
uint8_t boot_protocol; // None, Keyboard, Mouse
bool boot_mode; // Boot or Report protocol
uint8_t report_count; // Number of reports
struct {
uint8_t usage_page;
uint8_t usage;
// TODO just use the endpint size for now
uint8_t in_len; // length of IN report
uint8_t out_len; // length of OUT report
}reports[CFG_TUH_HID_REPORT_MAX];
tuh_hid_report_info_t report_info;
// Parsed Report ID for convenient API
uint8_t rid_keyboard;
uint8_t rid_mouse;
uint8_t rid_gamepad;
uint8_t rid_consumer;
}hidh_interface_t;
} hidh_interface_t;
typedef struct
{
@ -104,6 +97,23 @@ bool tuh_n_hid_n_mounted(uint8_t dev_addr, uint8_t instance)
return (hid_itf->ep_in != 0) || (hid_itf->ep_out != 0);
}
uint8_t tuh_n_hid_n_boot_protocol(uint8_t dev_addr, uint8_t instance)
{
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
return hid_itf->boot_protocol;
}
bool tuh_n_hid_n_boot_mode(uint8_t dev_addr, uint8_t instance)
{
hidh_interface_t* hid_itf = get_instance(dev_addr, instance);
return hid_itf->boot_mode;
}
tuh_hid_report_info_t const* tuh_n_hid_n_get_report_info(uint8_t dev_addr, uint8_t instance)
{
return &get_instance(dev_addr, instance)->report_info;
}
bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance)
{
TU_VERIFY(tuh_n_hid_n_mounted(dev_addr, instance));
@ -190,6 +200,7 @@ void hidh_close(uint8_t dev_addr)
// Enumeration
//--------------------------------------------------------------------+
static void parse_report_descriptor(hidh_interface_t* hid_itf, uint8_t const* desc_report, uint16_t desc_len);
static bool config_set_idle_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
static bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result);
@ -235,23 +246,25 @@ bool hidh_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_interface_t const *de
if ( HID_PROTOCOL_KEYBOARD == desc_itf->bInterfaceProtocol)
{
TU_LOG2(" Boot Keyboard\r\n");
// TODO boot protocol may still have more report in report mode
hid_itf->report_count = 1;
hid_itf->report_info.count = 1;
hid_itf->reports[0].usage_page = HID_USAGE_PAGE_DESKTOP;
hid_itf->reports[0].usage = HID_USAGE_DESKTOP_KEYBOARD;
hid_itf->reports[0].in_len = 8;
hid_itf->reports[0].out_len = 1;
hid_itf->report_info.info[0].usage_page = HID_USAGE_PAGE_DESKTOP;
hid_itf->report_info.info[0].usage = HID_USAGE_DESKTOP_KEYBOARD;
hid_itf->report_info.info[0].in_len = 8;
hid_itf->report_info.info[0].out_len = 1;
}
else if ( HID_PROTOCOL_MOUSE == desc_itf->bInterfaceProtocol)
{
TU_LOG2(" Boot Mouse\r\n");
// TODO boot protocol may still have more report in report mode
hid_itf->report_count = 1;
hid_itf->report_info.count = 1;
hid_itf->reports[0].usage_page = HID_USAGE_PAGE_DESKTOP;
hid_itf->reports[0].usage = HID_USAGE_DESKTOP_MOUSE;
hid_itf->reports[0].in_len = 8;
hid_itf->reports[0].out_len = 1;
hid_itf->report_info.info[0].usage_page = HID_USAGE_PAGE_DESKTOP;
hid_itf->report_info.info[0].usage = HID_USAGE_DESKTOP_MOUSE;
hid_itf->report_info.info[0].in_len = 5;
hid_itf->report_info.info[0].out_len = 0;
}
else
{
@ -326,19 +339,23 @@ bool config_set_idle_complete(uint8_t dev_addr, tusb_control_request_t const * r
bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_request_t const * request, xfer_result_t result)
{
TU_ASSERT(XFER_RESULT_SUCCESS == result);
uint8_t const itf_num = (uint8_t) request->wIndex;
uint8_t const inst = get_instance_id_by_itfnum(dev_addr, itf_num);
//hidh_interface_t* hid_itf = get_instance(dev_addr, inst);
uint8_t const itf_num = (uint8_t) 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* desc_report = usbh_get_enum_buf();
uint16_t const desc_len = request->wLength;
if (tuh_hid_descriptor_report_cb)
{
tuh_hid_descriptor_report_cb(dev_addr, inst, usbh_get_enum_buf(), request->wLength);
tuh_hid_descriptor_report_cb(dev_addr, instance, desc_report, desc_len);
}
// TODO Report descriptor parser
parse_report_descriptor(hid_itf, desc_report, desc_len);
// enumeration is complete
if (tuh_hid_mounted_cb) tuh_hid_mounted_cb(dev_addr, inst);
if (tuh_hid_mounted_cb) tuh_hid_mounted_cb(dev_addr, instance);
// notify usbh that driver enumeration is complete
usbh_driver_set_config_complete(dev_addr, itf_num);
@ -346,8 +363,69 @@ bool config_get_report_desc_complete(uint8_t dev_addr, tusb_control_request_t co
return true;
}
// Parse Report Descriptor to tuh_hid_report_info_t
static void parse_report_descriptor(hidh_interface_t* hid_itf, uint8_t const* desc_report, uint16_t desc_len)
{
enum
{
USAGE_PAGE = 0x05,
USAGE = 0x09,
USAGE_MIN = 0x19,
USAGE_MAX = 0x29,
LOGICAL_MIN = 0x15,
LOGICAL_MAX = 0x25,
REPORT_SIZE = 0x75,
REPORT_COUNT = 0x95
};
// Short Item 6.2.2.2 USB HID 1.11
union TU_ATTR_PACKED
{
uint8_t byte;
struct TU_ATTR_PACKED
{
uint8_t size : 2;
uint8_t type : 2;
uint8_t tag : 4;
};
} header;
while(desc_len)
{
header.byte = *desc_report++;
uint8_t const tag = header.tag;
uint8_t const type = header.type;
uint8_t const size = header.size;
desc_len--;
TU_LOG2("tag = %d, type = %d, size = %d, data = ", tag, type, size);
for(uint32_t i=0; i<size; i++) TU_LOG2("%02X ", desc_report[i]);
TU_LOG2("\r\n");
switch(type)
{
case RI_TYPE_MAIN:
break;
case RI_TYPE_GLOBAL:
break;
case RI_TYPE_LOCAL:
break;
// error
default: break;
}
desc_report += size;
desc_len -= size;
}
}
//--------------------------------------------------------------------+
// Instance helper
// Helper
//--------------------------------------------------------------------+
// Get Device by address

View File

@ -46,9 +46,20 @@
#define CFG_TUH_HID_REPORT_MAX 4
#endif
#ifndef CFG_TUH_HID_REPORT_DESCRIPTOR_BUFSIZE
#define CFG_TUH_HID_REPORT_DESCRIPTOR_BUFSIZE 256
#endif
typedef struct
{
uint8_t count; // number of info
struct
{
uint8_t usage_page;
uint8_t usage;
// TODO still use the endpoint size for now
uint8_t in_len; // length of IN report
uint8_t out_len; // length of OUT report
} info[CFG_TUH_HID_REPORT_MAX];
} tuh_hid_report_info_t;
//--------------------------------------------------------------------+
// Application API (Multiple devices)
@ -63,13 +74,25 @@ uint8_t tuh_n_hid_instance_count(uint8_t dev_addr);
// Check if HID instance is mounted
bool tuh_n_hid_n_mounted(uint8_t dev_addr, uint8_t instance);
// Get boot protocol check out hid_protocol_type_t for possible value
uint8_t tuh_n_hid_n_boot_protocol(uint8_t dev_addr, uint8_t instance);
// Check if current mode is Boot (true) or Report (false)
bool tuh_n_hid_n_boot_mode(uint8_t dev_addr, uint8_t instance);
// Get Report information parsed from report descriptor. Data must not be modified by application
// If report information does not match the actual device descriptor, that is because the built-in parser
// has its limit. Application could use tuh_hid_descriptor_report_cb() callback to parse descriptor by itself.
tuh_hid_report_info_t const* tuh_n_hid_n_get_report_info(uint8_t dev_addr, uint8_t instance);
// Check if the interface is ready to use
bool tuh_n_hid_n_ready(uint8_t dev_addr, uint8_t instance);
// Get Report from device
bool tuh_n_hid_n_get_report(uint8_t dev_addr, uint8_t instance, void* report, uint16_t len);
// Set Report using control endpoint
//bool tuh_n_hid_n_set_report_control(uint8_t dev_addr, uint8_t instance, void* report, uint16_t len);
//------------- -------------//
@ -93,10 +116,10 @@ TU_ATTR_WEAK void tuh_hid_mounted_cb (uint8_t dev_addr, uint8_t instance);
// Invoked when device with hid interface is un-mounted
TU_ATTR_WEAK void tuh_hid_unmounted_cb(uint8_t dev_addr, uint8_t instance);
// Invoked when received Report from device
// Invoked when received Report from device via either regular or control endpoint
TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t xferred_bytes);
// Invoked when Sent Report to device
// Invoked when Sent Report to device via either regular or control endpoint
TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t instance, uint8_t xferred_bytes);
//--------------------------------------------------------------------+