This commit is contained in:
fruit-bat 2022-07-19 17:12:55 +07:00 committed by GitHub
commit c10d01c5fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 3089 additions and 223 deletions

View File

@ -15,6 +15,9 @@ add_executable(${PROJECT})
# Example source
target_sources(${PROJECT} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_host_utils.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_host_info.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_host_joy.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
)

View File

@ -16,6 +16,8 @@ CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
SRC_C += \
src/class/cdc/cdc_host.c \
src/class/hid/hid_host.c \
src/class/hid/hid_rip.c \
src/class/hid/hid_ri.c \
src/class/msc/msc_host.c \
src/host/hub.c \
src/host/usbh.c \

View File

@ -25,6 +25,8 @@
#include "bsp/board.h"
#include "tusb.h"
#include "hid_host_joy.h"
#include "hid_host_info.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@ -38,13 +40,6 @@
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
// Each HID instance can has multiple reports
static struct
{
uint8_t report_count;
tuh_hid_report_info_t report_info[MAX_REPORT];
}hid_info[CFG_TUH_HID];
static void process_kbd_report(hid_keyboard_report_t const *report);
static void process_mouse_report(hid_mouse_report_t const * report);
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
@ -54,6 +49,61 @@ void hid_app_task(void)
// nothing to do
}
void handle_kbd_report(tusb_hid_host_info_t* info, const uint8_t* report, uint8_t report_length, uint8_t report_id)
{
// Stop unused parameter errors
(void) info;
(void) report_length;
(void) report_id;
TU_LOG1("HID receive keyboard report\r\n");
process_kbd_report((hid_keyboard_report_t const *)report);
}
void handle_mouse_report(tusb_hid_host_info_t* info, const uint8_t* report, uint8_t report_length, uint8_t report_id)
{
// Stop unused parameter errors
(void) info;
(void) report_length;
(void) report_id;
TU_LOG1("HID receive mouse report\r\n");
process_mouse_report((hid_mouse_report_t const *)report);
}
void handle_joystick_report(tusb_hid_host_info_t* info, const uint8_t* report, uint8_t report_length, uint8_t report_id)
{
TU_LOG1("HID receive joystick report\r\n");
tusb_hid_simple_joysick_t* simple_joystick = tuh_hid_get_simple_joystick(
info->key.elements.dev_addr,
info->key.elements.instance,
report_id);
if (simple_joystick != NULL) {
tusb_hid_simple_joysick_process_report(simple_joystick, report, report_length);
tusb_hid_print_simple_joysick_report(simple_joystick);
}
}
void handle_joystick_unmount(tusb_hid_host_info_t* info) {
TU_LOG1("HID joystick unmount\n");
// Free up joystick definitions
tuh_hid_free_simple_joysticks_for_instance(info->key.elements.dev_addr, info->key.elements.instance);
}
void handle_gamepad_report(tusb_hid_host_info_t* info, const uint8_t* report, uint8_t report_length, uint8_t report_id)
{
// Stop unused parameter errors
(void) info;
(void) report_id;
TU_LOG1("HID receive gamepad report ");
for(int i = 0; i < report_length; ++i) {
printf("%02x", report[i]);
}
printf("\r\n");
}
//--------------------------------------------------------------------+
// TinyUSB Callbacks
//--------------------------------------------------------------------+
@ -67,6 +117,10 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
{
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
printf("HID Report Description \r\n");
for(int i = 0; i < desc_len; ++i) printf("%02X ", desc_report[i]);
printf("\r\n");
// Interface protocol (hid_interface_protocol_enum_t)
const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
@ -77,8 +131,50 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
// Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
if ( itf_protocol == HID_ITF_PROTOCOL_NONE )
{
hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len);
printf("HID has %u reports \r\n", hid_info[instance].report_count);
tuh_hid_report_info_t reports[MAX_REPORT];
uint8_t report_count = tuh_hid_parse_report_descriptor(reports, MAX_REPORT, desc_report, desc_len);
printf("HID has %u reports \r\n", report_count);
for (uint8_t i = 0; i < report_count; ++i) {
tuh_hid_report_info_t *report = &reports[i];
bool has_report_id = report_count > 1 || (report[0].report_id > 0);
printf("HID report usage_page=%d, usage=%d, has_report_id=%d dev=%d instance=%d\n", report->usage_page, report->usage, has_report_id, dev_addr, instance);
if (report->usage_page == HID_USAGE_PAGE_DESKTOP)
{
switch (report->usage)
{
case HID_USAGE_DESKTOP_KEYBOARD: {
printf("HID receive keyboard report description\r\n");
tuh_hid_allocate_info(dev_addr, instance, has_report_id, &handle_kbd_report, NULL);
break;
}
case HID_USAGE_DESKTOP_JOYSTICK: {
printf("HID receive joystick report description\r\n");
if(tuh_hid_allocate_info(dev_addr, instance, has_report_id, &handle_joystick_report, handle_joystick_unmount)) {
tuh_hid_joystick_parse_report_descriptor(desc_report, desc_len, dev_addr, instance);
}
break;
}
case HID_USAGE_DESKTOP_MOUSE: {
printf("HID receive mouse report description\r\n");
tuh_hid_allocate_info(dev_addr, instance, has_report_id, &handle_mouse_report, NULL);
break;
}
case HID_USAGE_DESKTOP_GAMEPAD: {
printf("HID receive gamepad report description\r\n");
tuh_hid_allocate_info(dev_addr, instance, has_report_id, &handle_gamepad_report, NULL);
// May be able to handle this in the same was as a the joystick. Needs a little investigation
break;
}
default: {
TU_LOG1("HID usage unknown usage:%d\r\n", report->usage);
break;
}
}
}
}
}
// request to receive report
@ -93,6 +189,9 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
// Invoke unmount functions adn free up host info structure
tuh_hid_free_info(dev_addr, instance);
}
// Invoked when received report from device via interrupt endpoint
@ -113,6 +212,7 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
break;
default:
TU_LOG2("HID receive boot generic report\r\n");
// Generic report requires matching ReportID and contents with previous parsed report info
process_generic_report(dev_addr, instance, report, len);
break;
@ -232,65 +332,21 @@ static void process_mouse_report(hid_mouse_report_t const * report)
//--------------------------------------------------------------------+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{
(void) dev_addr;
uint8_t const rpt_count = hid_info[instance].report_count;
tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info;
tuh_hid_report_info_t* rpt_info = NULL;
if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0)
tusb_hid_host_info_t* info = tuh_hid_get_info(dev_addr, instance);
if (info == NULL)
{
// Simple report without report ID as 1st byte
rpt_info = &rpt_info_arr[0];
}else
{
// Composite report, 1st byte is report ID, data starts from 2nd byte
uint8_t const rpt_id = report[0];
// Find report id in the arrray
for(uint8_t i=0; i<rpt_count; i++)
{
if (rpt_id == rpt_info_arr[i].report_id )
{
rpt_info = &rpt_info_arr[i];
break;
}
}
report++;
len--;
}
if (!rpt_info)
{
printf("Couldn't find the report info for this report !\r\n");
printf("Couldn't find the host report info for dev_addr=%d instance=%d\r\n", dev_addr, instance);
return;
}
uint8_t rpt_id = 0;
// For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
// - Keyboard : Desktop, Keyboard
// - Mouse : Desktop, Mouse
// - Gamepad : Desktop, Gamepad
// - Consumer Control (Media Key) : Consumer, Consumer Control
// - System Control (Power key) : Desktop, System Control
// - Generic (vendor) : 0xFFxx, xx
if ( rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP )
{
switch (rpt_info->usage)
{
case HID_USAGE_DESKTOP_KEYBOARD:
TU_LOG1("HID receive keyboard report\r\n");
// Assume keyboard follow boot report layout
process_kbd_report( (hid_keyboard_report_t const*) report );
break;
case HID_USAGE_DESKTOP_MOUSE:
TU_LOG1("HID receive mouse report\r\n");
// Assume mouse follow boot report layout
process_mouse_report( (hid_mouse_report_t const*) report );
break;
default: break;
}
if (info->has_report_id) {
rpt_id = report[0];
report++;
len--;
}
info->handler(info, report, len, rpt_id);
}

View File

@ -0,0 +1,95 @@
/*
* The MIT License (MIT)
*
* 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.
*
*/
#include "hid_host_info.h"
static tusb_hid_host_info_t hid_info[TUP_DCD_ENDPOINT_MAX];
tusb_hid_host_info_t* tuh_hid_get_info(uint8_t dev_addr, uint8_t instance)
{
tusb_hid_host_info_key_t key;
key.elements.dev_addr = dev_addr;
key.elements.instance = instance;
key.elements.in_use = 1;
uint32_t combined = key.combined;
// Linear search for endpoint, which is fine while TUP_DCD_ENDPOINT_MAX
// is small. Perhaps a 'bsearch' version should be used if TUP_DCD_ENDPOINT_MAX
// is large.
for(uint8_t i = 0; i < TUP_DCD_ENDPOINT_MAX; ++i) {
tusb_hid_host_info_t* info = &hid_info[i];
if (info->key.combined == combined) return info;
}
return NULL;
}
void tuh_hid_free_sinlge_info(tusb_hid_host_info_t* info)
{
if (info->key.elements.in_use && info->unmount != NULL) {
info->unmount(info);
}
info->key.elements.in_use = 0;
}
void tuh_hid_free_all_info(void)
{
for(uint8_t i = 0; i < TUP_DCD_ENDPOINT_MAX; ++i) {
tuh_hid_free_sinlge_info(&hid_info[i]);
}
}
void tuh_hid_free_info(uint8_t dev_addr, uint8_t instance)
{
for(uint8_t i = 0; i < TUP_DCD_ENDPOINT_MAX; ++i) {
tusb_hid_host_info_t* info = &hid_info[i];
if (info->key.elements.instance == instance && info->key.elements.dev_addr == dev_addr && info->key.elements.in_use) {
tuh_hid_free_sinlge_info(info);
}
}
}
tusb_hid_host_info_t* tuh_hid_allocate_info(
uint8_t dev_addr,
uint8_t instance,
bool has_report_id,
void (*handler)(struct tusb_hid_host_info* info, const uint8_t* report, uint8_t report_length, uint8_t report_id),
void (*unmount)(struct tusb_hid_host_info* info))
{
for(uint8_t i = 0; i < TUP_DCD_ENDPOINT_MAX; ++i) {
tusb_hid_host_info_t* info = &hid_info[i];
if (!info->key.elements.in_use) {
tu_memclr(info, sizeof(tusb_hid_host_info_t));
info->key.elements.in_use = true;
info->key.elements.dev_addr = dev_addr;
info->key.elements.instance = instance;
info->has_report_id = has_report_id;
info->handler = handler;
info->unmount = unmount;
return info;
}
}
TU_LOG1("FAILED TO ALLOCATE INFO for dev_addr=%d, instance=%d\r\n", dev_addr, instance);
return NULL;
}

View File

@ -0,0 +1,79 @@
/*
* The MIT License (MIT)
*
* 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 module provides a place to store information about host endpoints.
*
* It records:
* A flag which indicates whether reports start with an identifier byte
* A 'handler' function to process a report
* An optional 'unmount' function called when the instance is removed
*
* Each 'info' record is indexed by the device address and instance number.
*
* [ It might be nice if the main library gave some support for managing
* state like this... particularly if it avoided more arrays & lookups ]
*/
#ifndef _TUSB_HID_HOST_INFO_H_
#define _TUSB_HID_HOST_INFO_H_
#include "tusb.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef union TU_ATTR_PACKED
{
uint32_t combined;
struct TU_ATTR_PACKED
{
uint8_t instance :8;
uint8_t dev_addr :8;
uint16_t in_use :16;
} elements;
} tusb_hid_host_info_key_t;
typedef struct tusb_hid_host_info {
tusb_hid_host_info_key_t key;
bool has_report_id;
void (*handler)(struct tusb_hid_host_info* info, const uint8_t* report, uint8_t report_length, uint8_t report_id);
void (*unmount)(struct tusb_hid_host_info* info);
} tusb_hid_host_info_t;
tusb_hid_host_info_t* tuh_hid_get_info(uint8_t dev_addr, uint8_t instance);
tusb_hid_host_info_t* tuh_hid_allocate_info(
uint8_t dev_addr,
uint8_t instance,
bool has_report_id,
void (*handler)(struct tusb_hid_host_info* info, const uint8_t* report, uint8_t report_length, uint8_t report_id),
void (*unmount)(struct tusb_hid_host_info* info)
);
void tuh_hid_free_info(uint8_t dev_addr, uint8_t instance);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_HOST_INFO_H_ */

View File

@ -0,0 +1,288 @@
/*
* The MIT License (MIT)
*
* 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.
*
* Test me with:
*
* ceedling test:pattern[hid_host_joy]
*/
#include "hid_host_joy.h"
#include "hid_host_utils.h"
static tusb_hid_simple_joysick_t hid_simple_joysicks[HID_MAX_JOYSTICKS];
static bool tuh_hid_joystick_check_usage(uint32_t eusage)
{
// Check outer usage
switch(eusage) {
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_JOYSTICK): return true;
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_GAMEPAD): return true; // TODO not sure about this one
default: return false;
}
}
tusb_hid_simple_joysick_t* tuh_hid_get_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id)
{
tusb_hid_simple_joysick_key_t key;
key.elements.dev_addr = dev_addr;
key.elements.instance = instance;
key.elements.report_id = report_id;
key.elements.in_use = 1;
uint32_t combined = key.combined;
for(uint8_t i = 0; i < HID_MAX_JOYSTICKS; ++i) {
tusb_hid_simple_joysick_t* simple_joystick = &hid_simple_joysicks[i];
if (simple_joystick->key.combined == combined) return simple_joystick;
}
return NULL;
}
void tuh_hid_free_simple_joysticks(void) {
for(uint8_t i = 0; i < HID_MAX_JOYSTICKS; ++i) {
hid_simple_joysicks[i].key.elements.in_use = false;
}
}
void tuh_hid_free_simple_joysticks_for_instance(uint8_t dev_addr, uint8_t instance) {
for(uint8_t i = 0; i < HID_MAX_JOYSTICKS; ++i) {
tusb_hid_simple_joysick_t* simple_joystick = &hid_simple_joysicks[i];
if (simple_joystick->key.elements.dev_addr == dev_addr && simple_joystick->key.elements.instance == instance) simple_joystick->key.elements.in_use = 0;
}
}
tusb_hid_simple_joysick_t* tuh_hid_allocate_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id) {
for(uint8_t i = 0; i < HID_MAX_JOYSTICKS; ++i) {
tusb_hid_simple_joysick_t* simple_joystick = &hid_simple_joysicks[i];
if (!simple_joystick->key.elements.in_use) {
tu_memclr(simple_joystick, sizeof(tusb_hid_simple_joysick_t));
simple_joystick->key.elements.in_use = 1;;
simple_joystick->key.elements.instance = instance;
simple_joystick->key.elements.report_id = report_id;
simple_joystick->key.elements.dev_addr = dev_addr;
return simple_joystick;
}
}
return NULL;
}
// get or create
tusb_hid_simple_joysick_t* tuh_hid_obtain_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id) {
tusb_hid_simple_joysick_t* jdata = tuh_hid_get_simple_joystick(dev_addr, instance, report_id);
return jdata ? jdata : tuh_hid_allocate_simple_joystick(dev_addr, instance, report_id);
}
// Fetch some data from the HID parser
//
// The data fetched here may be relevant to multiple usage items
//
// returns false if obviously not of interest
bool tuh_hid_joystick_get_data(
tuh_hid_rip_state_t *pstate, // The current HID report parser state
const uint8_t* ri_input, // Pointer to the input item we have arrived at
tuh_hid_joystick_data_t* jdata) // Data structure to complete
{
const uint8_t* ri_report_size = tuh_hid_rip_global(pstate, RI_GLOBAL_REPORT_SIZE);
const uint8_t* ri_report_count = tuh_hid_rip_global(pstate, RI_GLOBAL_REPORT_COUNT);
const uint8_t* ri_report_id = tuh_hid_rip_global(pstate, RI_GLOBAL_REPORT_ID);
const uint8_t* ri_logical_min = tuh_hid_rip_global(pstate, RI_GLOBAL_LOGICAL_MIN);
const uint8_t* ri_logical_max = tuh_hid_rip_global(pstate, RI_GLOBAL_LOGICAL_MAX);
const uint8_t* ri_usage_page = tuh_hid_rip_global(pstate, RI_GLOBAL_USAGE_PAGE);
const uint8_t* ri_usage_min = tuh_hid_rip_local(pstate, RI_LOCAL_USAGE_MIN);
const uint8_t* ri_usage_max = tuh_hid_rip_local(pstate, RI_LOCAL_USAGE_MAX);
// We need to know how the size of the data
if (ri_report_size == NULL || ri_report_count == NULL || ri_usage_page == NULL) return false;
jdata->report_size = tuh_hid_ri_short_udata32(ri_report_size);
jdata->report_count = tuh_hid_ri_short_udata32(ri_report_count);
jdata->report_id = ri_report_id ? tuh_hid_ri_short_udata8(ri_report_id) : 0;
jdata->logical_min = ri_logical_min ? tuh_hid_ri_short_data32(ri_logical_min) : 0;
jdata->logical_max = ri_logical_max ? tuh_hid_ri_short_data32(ri_logical_max) : 0;
jdata->input_flags.byte = tuh_hid_ri_short_udata8(ri_input);
jdata->usage_page = tuh_hid_ri_short_udata32(ri_usage_page);
jdata->usage_min = ri_usage_min ? tuh_hid_ri_short_udata32(ri_usage_min) : 0;
jdata->usage_max = ri_usage_max ? tuh_hid_ri_short_udata32(ri_usage_max) : 0;
jdata->usage_is_range = (ri_usage_min != NULL) && (ri_usage_max != NULL);
return true;
}
static void tuh_hid_joystick_process_axis(
tuh_hid_joystick_data_t* jdata,
uint32_t bitpos,
tusb_hid_simple_axis_t* simple_axis)
{
simple_axis->start = bitpos;
simple_axis->length = jdata->report_size;
simple_axis->flags.is_signed = jdata->logical_min < 0;
simple_axis->logical_min = jdata->logical_min;
simple_axis->logical_max = jdata->logical_max;
}
void tuh_hid_joystick_process_usages(
tuh_hid_rip_state_t *pstate,
tuh_hid_joystick_data_t* jdata,
uint32_t bitpos,
uint8_t dev_addr,
uint8_t instance)
{
if (jdata->input_flags.data_const) return;
// If there are no specific usages look for a range
// TODO What is the correct behaviour if there are both?
if (pstate->usage_count == 0 && !jdata->usage_is_range) {
printf("no usage - skipping bits %lu \n", jdata->report_size * jdata->report_count);
return;
}
tusb_hid_simple_joysick_t* simple_joystick = tuh_hid_obtain_simple_joystick(dev_addr, instance, jdata->report_id);
if (simple_joystick == NULL) {
printf("Failed to allocate joystick for HID dev_addr %d, instance %d, report ID %d\n", dev_addr, instance, jdata->report_id);
return;
}
// Update the report length in bytes
simple_joystick->report_length = (uint8_t)((bitpos + (jdata->report_size * jdata->report_count) + 7) >> 3);
// Naive, assumes buttons are defined in a range
if (jdata->usage_is_range) {
if (jdata->usage_page == HID_USAGE_PAGE_BUTTON) {
tusb_hid_simple_buttons_t* simple_buttons = &simple_joystick->buttons;
simple_buttons->start = bitpos;
simple_buttons->length = jdata->report_count;
return;
}
}
for (uint8_t i = 0; i < jdata->report_count; ++i) {
uint32_t eusage = pstate->usages[i < pstate->usage_count ? i : pstate->usage_count - 1];
switch (eusage) {
// Seems to be common usage for gamepads.
// Probably needs a lot more thought...
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_X):
tuh_hid_joystick_process_axis(jdata, bitpos, &simple_joystick->axis_x1);
break;
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_Y):
tuh_hid_joystick_process_axis(jdata, bitpos, &simple_joystick->axis_y1);
break;
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_Z):
tuh_hid_joystick_process_axis(jdata, bitpos, &simple_joystick->axis_x2);
break;
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_RZ):
tuh_hid_joystick_process_axis(jdata, bitpos, &simple_joystick->axis_y2);
break;
case HID_RIP_EUSAGE(HID_USAGE_PAGE_DESKTOP, HID_USAGE_DESKTOP_HAT_SWITCH):
tuh_hid_joystick_process_axis(jdata, bitpos, &simple_joystick->hat);
break;
default: break;
}
bitpos += jdata->report_size;
}
}
void tuh_hid_joystick_parse_report_descriptor(uint8_t const* desc_report, uint16_t desc_len, uint8_t dev_addr, uint8_t instance)
{
uint32_t eusage = 0;
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, desc_report, desc_len);
const uint8_t *ri;
uint32_t bitpos = 0;
while((ri = tuh_hid_rip_next_short_item(&pstate)) != NULL)
{
uint8_t const type_and_tag = tuh_hid_ri_short_type_and_tag(ri);
switch(type_and_tag)
{
case HID_RI_TYPE_AND_TAG(RI_TYPE_MAIN, RI_MAIN_INPUT): {
if (tuh_hid_joystick_check_usage(eusage)) {
tuh_hid_joystick_data_t joystick_data;
if(tuh_hid_joystick_get_data(&pstate, ri, &joystick_data)) {
tuh_hid_joystick_process_usages(&pstate, &joystick_data, bitpos, dev_addr, instance);
bitpos += joystick_data.report_size * joystick_data.report_count;
}
}
break;
}
case HID_RI_TYPE_AND_TAG(RI_TYPE_LOCAL, RI_LOCAL_USAGE): {
if (pstate.collections_count == 0) {
eusage = pstate.usages[pstate.usage_count - 1];
}
break;
}
case HID_RI_TYPE_AND_TAG(RI_TYPE_GLOBAL, RI_GLOBAL_REPORT_ID): {
bitpos = 0;
break;
}
default: break;
}
}
}
int32_t tuh_hid_simple_joystick_get_axis_value(tusb_hid_simple_axis_t* simple_axis, const uint8_t* report)
{
return tuh_hid_report_i32(report, simple_axis->start, simple_axis->length, simple_axis->flags.is_signed);
}
void tusb_hid_simple_joysick_process_report(tusb_hid_simple_joysick_t* simple_joystick, const uint8_t* report, uint8_t report_length)
{
if (simple_joystick->report_length > report_length) {
TU_LOG1("HID joystick report too small\r\n");
return;
}
tusb_hid_simple_joysick_values_t* values = &simple_joystick->values;
values->x1 = tuh_hid_simple_joystick_get_axis_value(&simple_joystick->axis_x1, report);
values->y1 = tuh_hid_simple_joystick_get_axis_value(&simple_joystick->axis_y1, report);
values->x2 = tuh_hid_simple_joystick_get_axis_value(&simple_joystick->axis_x2, report);
values->y2 = tuh_hid_simple_joystick_get_axis_value(&simple_joystick->axis_y2, report);
values->hat = tuh_hid_simple_joystick_get_axis_value(&simple_joystick->hat, report);
values->buttons = tuh_hid_report_bits_u32(report, simple_joystick->buttons.start, simple_joystick->buttons.length);
simple_joystick->has_values = true;
}
void tusb_hid_print_simple_joysick_report(tusb_hid_simple_joysick_t* simple_joystick)
{
if (simple_joystick->has_values) {
printf("dev_addr=%3d, instance=%3d, report_id=%3d, x1=%4ld, y1=%4ld, x2=%4ld, y2=%4ld, hat=%01lX, buttons=%04lX\n",
simple_joystick->key.elements.dev_addr,
simple_joystick->key.elements.instance,
simple_joystick->key.elements.report_id,
simple_joystick->values.x1,
simple_joystick->values.y1,
simple_joystick->values.x2,
simple_joystick->values.y2,
simple_joystick->values.hat,
simple_joystick->values.buttons);
}
}
uint8_t tuh_hid_get_simple_joysticks(tusb_hid_simple_joysick_t** simple_joysticks, uint8_t max_simple_joysticks)
{
uint8_t j = 0;
for(uint8_t i = 0; i < HID_MAX_JOYSTICKS && j < max_simple_joysticks; ++i) {
tusb_hid_simple_joysick_t* simple_joystick = &hid_simple_joysicks[i];
if (simple_joystick->key.elements.in_use) {
simple_joysticks[j++] = simple_joystick;
}
}
return j;
}

View File

@ -0,0 +1,193 @@
/*
* The MIT License (MIT)
*
* 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 module contains an example of mapping a HID report to a simplified
* local joystick definition. This simple model has:
* 2x XY axes (X, Y and Z, Rz)
* 1x HAT control
* up to 32x Buttons (but have to be defined in one range in the HID description)
*
* Applications will still need to allow mapping of axis and buttons to
* particlar functions.
*
* There are many ways a HID report can describe a joystick and this code
* only copes with a few of them.
*/
#ifndef _TUSB_HID_HOST_JOY_H_
#define _TUSB_HID_HOST_JOY_H_
#include "tusb.h"
#include "class/hid/hid_rip.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HID_MAX_JOYSTICKS 4
typedef union TU_ATTR_PACKED
{
uint8_t byte;
struct TU_ATTR_PACKED
{
bool data_const : 1;
bool array_variable : 1;
bool absolute_relative : 1;
bool nowrap_wrap : 1;
bool linear_nonlinear : 1;
bool prefered_noprefered : 1;
bool nonull_null : 1;
};
} tusb_hid_ri_intput_flags_t;
typedef struct {
union TU_ATTR_PACKED
{
uint8_t byte;
struct TU_ATTR_PACKED
{
bool is_signed : 1;
};
} flags;
uint16_t start;
uint16_t length;
int32_t logical_min;
int32_t logical_max;
} tusb_hid_simple_axis_t;
typedef struct {
uint16_t start;
uint16_t length;
} tusb_hid_simple_buttons_t;
// Very simple representation of a joystick to try and map to
// (and this will be quite tricky enough).
typedef struct {
int32_t x1;
int32_t y1;
int32_t x2;
int32_t y2;
int32_t hat;
uint32_t buttons;
} tusb_hid_simple_joysick_values_t;
typedef union TU_ATTR_PACKED
{
uint32_t combined;
struct TU_ATTR_PACKED
{
uint8_t instance :8;
uint8_t dev_addr :8;
uint8_t report_id :8;
uint8_t in_use :8;
} elements;
} tusb_hid_simple_joysick_key_t;
// Simple joystick definitions and values
typedef struct {
tusb_hid_simple_joysick_key_t key;
uint8_t report_length; // requied report length in bytes
bool has_values;
tusb_hid_simple_axis_t axis_x1;
tusb_hid_simple_axis_t axis_y1;
tusb_hid_simple_axis_t axis_x2;
tusb_hid_simple_axis_t axis_y2;
tusb_hid_simple_axis_t hat;
tusb_hid_simple_buttons_t buttons;
tusb_hid_simple_joysick_values_t values;
} tusb_hid_simple_joysick_t;
// Intermediate data structure used while parsing joystick HID report descriptors
typedef struct {
uint32_t report_size;
uint32_t report_count;
int32_t logical_min;
int32_t logical_max;
uint16_t usage_page;
uint16_t usage_min;
uint16_t usage_max;
uint8_t report_id;
tusb_hid_ri_intput_flags_t input_flags;
bool usage_is_range;
} tuh_hid_joystick_data_t;
// Fetch some data from the HID parser
//
// The data fetched here may be relevant to multiple usage items
//
// returns false if obviously not of interest
bool tuh_hid_joystick_get_data(
tuh_hid_rip_state_t *pstate, // The current HID report parser state
const uint8_t* ri_input, // Pointer to the input item we have arrived at
tuh_hid_joystick_data_t* jdata // Data structure to complete
);
// Process the HID descriptor usages
// These are handled when an 'input' item is encountered.
void tuh_hid_joystick_process_usages(
tuh_hid_rip_state_t *pstate,
tuh_hid_joystick_data_t* jdata,
uint32_t bitpos,
uint8_t dev_addr,
uint8_t instance
);
// Parse a HID report description for a joystick
void tuh_hid_joystick_parse_report_descriptor(uint8_t const* desc_report, uint16_t desc_len, uint8_t dev_addr, uint8_t instance);
// Fetch a previously allocated simple joystick
tusb_hid_simple_joysick_t* tuh_hid_get_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
// Free a previously allocated simple joystick
void tuh_hid_free_simple_joysticks_for_instance(uint8_t dev_addr, uint8_t instance);
// Free all previously allocated simple joysticks
void tuh_hid_free_simple_joysticks(void);
// Allocate a new simple joystick
tusb_hid_simple_joysick_t* tuh_hid_allocate_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
// If it exists, return an exisiting simple joystick, else allocate a new one
tusb_hid_simple_joysick_t* tuh_hid_obtain_simple_joystick(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
// Process a HID report
// The report pointer should be advanced beyond the report ID byte.
// The length should not include the report ID byte.
// The length should be in bytes.
void tusb_hid_simple_joysick_process_report(tusb_hid_simple_joysick_t* simple_joystick, const uint8_t* report, uint8_t report_length);
// Send an axis and button report to stdout
//
// e.g.
// dev_addr= 1, instance= 0, report_id= 0, x1= 127, y1= 127, x2= 127, y2= 127, hat=F, buttons=0000
//
void tusb_hid_print_simple_joysick_report(tusb_hid_simple_joysick_t* simple_joystick);
// Populate an array of the attached joysticks
uint8_t tuh_hid_get_simple_joysticks(tusb_hid_simple_joysick_t** simple_joysticks, uint8_t max_simple_joysticks);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_HOST_JOY_H_ */

View File

@ -0,0 +1,100 @@
/*
* The MIT License (MIT)
*
* 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.
*
* Test me with:
*
* ceedling test:pattern[hid_host_utils]
*/
#include "hid_host_utils.h"
uint32_t tuh_hid_report_bits_u32(uint8_t const* report, uint16_t start, uint16_t length)
{
const int16_t bit_offset_start = start & 7;
const int16_t l = length + bit_offset_start;
const uint8_t *p = report + (start >> 3);
uint32_t acc = ((uint32_t)*p++) >> bit_offset_start;
for(uint16_t i = 1; (i << 3) < l; ++i) {
acc |= ((uint32_t)*p++) << ((i << 3) - bit_offset_start);
}
const uint32_t lp0 = ((uint32_t)1) << (length - 1);
const uint32_t lp1 = lp0 << 1;
return acc & (lp1 - 1);
}
int32_t tuh_hid_report_bits_i32(uint8_t const* report, uint16_t start, uint16_t length)
{
const int16_t bit_offset_start = start & 7;
const int16_t l = length + bit_offset_start;
const uint8_t *p = report + (start >> 3);
uint32_t acc = ((uint32_t)*p++) >> bit_offset_start;
for(uint16_t i = 1; (i << 3) < l; ++i) {
acc |= ((uint32_t)*p++) << ((i << 3) - bit_offset_start);
}
const uint32_t lp0 = ((uint32_t)1) << (length - 1);
const uint32_t lp1 = lp0 << 1;
// Mask or sign extend
return acc & lp0 ? acc | -lp1 : acc & (lp1 - 1);
}
// Helper to get some bytes from a HID report as an unsigned 32 bit number
uint32_t tuh_hid_report_bytes_u32(uint8_t const* report, uint16_t start, uint16_t length)
{
const uint8_t *p = report + start;
if (length == 1) return (uint32_t)*p;
uint32_t acc = 0;
for(uint16_t i = 0; i < length; ++i) {
acc |= ((uint32_t)*p++) << (i << 3);
}
return acc;
}
// Helper to get some bytes from a HID report as a signed 32 bit number
int32_t tuh_hid_report_bytes_i32(uint8_t const* report, uint16_t start, uint16_t length)
{
const uint8_t *p = report + start;
if (length == 1) return (int32_t)(int8_t)*p;
uint32_t acc = 0;
for(uint16_t i = 0; i < length; ++i) {
acc |= ((uint32_t)*p++) << (i << 3);
}
const uint32_t lp0 = ((uint32_t)1) << ((length << 3) - 1);
const uint32_t lp1 = lp0 << 1;
// sign extend
return acc & lp0 ? acc | -lp1 : acc;
}
// Helper to get a value from a HID report
int32_t tuh_hid_report_i32(const uint8_t* report, uint16_t start, uint16_t length, bool is_signed)
{
if (length == 0) return 0;
if ((start | length) & 7) {
return is_signed ?
tuh_hid_report_bits_i32(report, start, length):
(int32_t)tuh_hid_report_bits_u32(report, start, length);
}
else {
return is_signed ?
tuh_hid_report_bytes_i32(report, start >> 3, length >> 3):
(int32_t)tuh_hid_report_bytes_u32(report, start >> 3, length >> 3);
}
}

View File

@ -0,0 +1,60 @@
/*
* The MIT License (MIT)
*
* 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.
*
*/
#ifndef _TUSB_HID_HOST_UTILS_H_
#define _TUSB_HID_HOST_UTILS_H_
#include "tusb.h"
#include "class/hid/hid_rip.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Helpers for extracting values from a HID Report
//--------------------------------------------------------------------+
// Helper to get some bits from a HID report as an unsigned 32 bit number
uint32_t tuh_hid_report_bits_u32(uint8_t const* report, uint16_t start, uint16_t length);
// Helper to get some bits from a HID report as a signed 32 bit number
int32_t tuh_hid_report_bits_i32(uint8_t const* report, uint16_t start, uint16_t length);
// Helper to get some bytes from a HID report as an unsigned 32 bit number
uint32_t tuh_hid_report_bytes_u32(uint8_t const* report, uint16_t start, uint16_t length);
// Helper to get some bytes from a HID report as a signed 32 bit number
int32_t tuh_hid_report_bytes_i32(uint8_t const* report, uint16_t start, uint16_t length);
// Helper to get a value from a HID report
// Not suitable if the value can be > 2^31
// Chooses between bytewise and bitwise fetches
int32_t tuh_hid_report_i32(const uint8_t* report, uint16_t start, uint16_t length, bool is_signed);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_HOST_UTILS_H_ */

View File

@ -19,6 +19,8 @@ CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
SRC_C += \
src/class/cdc/cdc_host.c \
src/class/hid/hid_host.c \
src/class/hid/hid_ri.c \
src/class/hid/hid_rip.c \
src/class/msc/msc_host.c \
src/host/hub.c \
src/host/usbh.c \

View File

@ -91,6 +91,8 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
${TOP}/src/host/usbh.c
${TOP}/src/host/hub.c
${TOP}/src/class/cdc/cdc_host.c
${TOP}/src/class/hid/hid_ri.c
${TOP}/src/class/hid/hid_rip.c
${TOP}/src/class/hid/hid_host.c
${TOP}/src/class/msc/msc_host.c
${TOP}/src/class/vendor/vendor_host.c

View File

@ -32,6 +32,7 @@
#include "host/usbh_classdriver.h"
#include "hid_host.h"
#include "hid_rip.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
@ -489,148 +490,6 @@ static void config_driver_mount_complete(uint8_t dev_addr, uint8_t instance, uin
usbh_driver_set_config_complete(dev_addr, hid_itf->itf_num);
}
//--------------------------------------------------------------------+
// Report Descriptor Parser
//--------------------------------------------------------------------+
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
{
// Report 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;
tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
uint8_t report_num = 0;
tuh_hid_report_info_t* info = report_info_arr;
// current parsed report count & size from descriptor
// uint8_t ri_report_count = 0;
// uint8_t ri_report_size = 0;
uint8_t ri_collection_depth = 0;
while(desc_len && report_num < arr_count)
{
header.byte = *desc_report++;
desc_len--;
uint8_t const tag = header.tag;
uint8_t const type = header.type;
uint8_t const size = header.size;
uint8_t const data8 = desc_report[0];
TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
for(uint32_t i=0; i<size; i++) TU_LOG(3, "%02X ", desc_report[i]);
TU_LOG(3, "\r\n");
switch(type)
{
case RI_TYPE_MAIN:
switch (tag)
{
case RI_MAIN_INPUT: break;
case RI_MAIN_OUTPUT: break;
case RI_MAIN_FEATURE: break;
case RI_MAIN_COLLECTION:
ri_collection_depth++;
break;
case RI_MAIN_COLLECTION_END:
ri_collection_depth--;
if (ri_collection_depth == 0)
{
info++;
report_num++;
}
break;
default: break;
}
break;
case RI_TYPE_GLOBAL:
switch(tag)
{
case RI_GLOBAL_USAGE_PAGE:
// only take in account the "usage page" before REPORT ID
if ( ri_collection_depth == 0 ) memcpy(&info->usage_page, desc_report, size);
break;
case RI_GLOBAL_LOGICAL_MIN : break;
case RI_GLOBAL_LOGICAL_MAX : break;
case RI_GLOBAL_PHYSICAL_MIN : break;
case RI_GLOBAL_PHYSICAL_MAX : break;
case RI_GLOBAL_REPORT_ID:
info->report_id = data8;
break;
case RI_GLOBAL_REPORT_SIZE:
// ri_report_size = data8;
break;
case RI_GLOBAL_REPORT_COUNT:
// ri_report_count = data8;
break;
case RI_GLOBAL_UNIT_EXPONENT : break;
case RI_GLOBAL_UNIT : break;
case RI_GLOBAL_PUSH : break;
case RI_GLOBAL_POP : break;
default: break;
}
break;
case RI_TYPE_LOCAL:
switch(tag)
{
case RI_LOCAL_USAGE:
// only take in account the "usage" before starting REPORT ID
if ( ri_collection_depth == 0 ) info->usage = data8;
break;
case RI_LOCAL_USAGE_MIN : break;
case RI_LOCAL_USAGE_MAX : break;
case RI_LOCAL_DESIGNATOR_INDEX : break;
case RI_LOCAL_DESIGNATOR_MIN : break;
case RI_LOCAL_DESIGNATOR_MAX : break;
case RI_LOCAL_STRING_INDEX : break;
case RI_LOCAL_STRING_MIN : break;
case RI_LOCAL_STRING_MAX : break;
case RI_LOCAL_DELIMITER : break;
default: break;
}
break;
// error
default: break;
}
desc_report += size;
desc_len -= size;
}
for ( uint8_t i = 0; i < report_num; i++ )
{
info = report_info_arr+i;
TU_LOG2("%u: id = %u, usage_page = %u, usage = %u\r\n", i, info->report_id, info->usage_page, info->usage);
}
return report_num;
}
//--------------------------------------------------------------------+
// Helper
//--------------------------------------------------------------------+

View File

@ -28,6 +28,7 @@
#define _TUSB_HID_HOST_H_
#include "hid.h"
#include "hid_rip.h"
#ifdef __cplusplus
extern "C" {
@ -46,18 +47,6 @@
#define CFG_TUH_HID_EPOUT_BUFSIZE 64
#endif
typedef struct
{
uint8_t report_id;
uint8_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
// uint8_t in_len; // length of IN report
// uint8_t out_len; // length of OUT report
} tuh_hid_report_info_t;
//--------------------------------------------------------------------+
// Interface API
//--------------------------------------------------------------------+
@ -71,10 +60,6 @@ bool tuh_hid_mounted(uint8_t dev_addr, uint8_t instance);
// Get interface supported protocol (bInterfaceProtocol) check out hid_interface_protocol_enum_t for possible values
uint8_t tuh_hid_interface_protocol(uint8_t dev_addr, uint8_t instance);
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
//--------------------------------------------------------------------+
// Control Endpoint API
//--------------------------------------------------------------------+

115
src/class/hid/hid_ri.c Normal file
View File

@ -0,0 +1,115 @@
/*
* The MIT License (MIT)
*
* 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_HOST_ENABLED && CFG_TUH_HID) || _UNITY_TEST_)
#include "hid_ri.h"
uint8_t tuh_hid_ri_short_data_length(const uint8_t *ri) {
// 0 -> 0, 1 -> 1, 2 -> 2, 3 -> 4
return (1 << (*ri & 3)) >> 1;
}
uint8_t tuh_hid_ri_short_type(const uint8_t *ri) {
return (*ri >> 2) & 3;
}
uint8_t tuh_hid_ri_short_tag(const uint8_t *ri) {
return (*ri >> 4) & 15;
}
uint8_t tuh_hid_ri_short_type_and_tag(const uint8_t *ri) {
return (*ri) & 0xfc;
}
bool tuh_hid_ri_is_long(const uint8_t *ri) {
return *ri == 0xfe;
}
uint32_t tuh_hid_ri_short_udata32(const uint8_t *ri) {
uint32_t d = 0;
uint8_t l = tuh_hid_ri_short_data_length(ri++);
for(uint8_t i = 0; i < l; ++i) d |= ((uint32_t)(*ri++)) << (i << 3);
return d;
}
uint8_t tuh_hid_ri_short_udata8(const uint8_t *ri) {
return tuh_hid_ri_short_data_length(ri) > 0 ? ri[1] : 0;
}
int32_t tuh_hid_ri_short_data32(const uint8_t *ri) {
int32_t d = 0;
uint8_t l = tuh_hid_ri_short_data_length(ri++);
bool negative = false;
for(uint8_t i = 0; i < 4; ++i) {
if (i < l) {
uint32_t b = *ri++;
d |= b << (i << 3);
negative = ((b >> 7) == 1);
}
else if (negative) {
d |= 0xff << (i << 3);
}
}
return d;
}
uint8_t tuh_hid_ri_long_data_length(const uint8_t *ri) {
return ri[1];
}
uint8_t tuh_hid_ri_long_tag(const uint8_t *ri) {
return ri[2];
}
const uint8_t* tuh_hid_ri_long_item_data(const uint8_t *ri) {
return ri + 3;
}
int16_t tuh_hid_ri_size(const uint8_t *ri, uint16_t l) {
// Make sure there is enough room for the header
if (l < 1) return HID_RI_EOF;
// Calculate the short item length
uint16_t sl = 1 + tuh_hid_ri_short_data_length(ri);
// check it fits
if (l < sl) return HID_RI_ERR_MISSING_SHORT;
// Check if we need to worry about a long item
if (tuh_hid_ri_is_long(ri)) {
uint16_t ll = tuh_hid_ri_long_data_length(ri);
uint16_t tl = sl + ll;
if (l < tl) return HID_RI_ERR_MISSING_LONG;
return tl;
}
else {
return sl;
}
}
void tuh_hid_ri_split_usage(uint32_t eusage, uint16_t *usage, uint16_t *usage_page) {
*usage = eusage & 0xffff;
*usage_page = eusage >> 16;
}
#endif

104
src/class/hid/hid_ri.h Normal file
View File

@ -0,0 +1,104 @@
/*
* The MIT License (MIT)
*
* 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_HID_RI_H_
#define _TUSB_HID_RI_H_
#include "tusb.h"
#ifdef __cplusplus
extern "C" {
#endif
#define HID_RI_TYPE_AND_TAG(TYPE, TAG) ((TAG << 4) | (TYPE << 2))
//--------------------------------------------------------------------+
// HID Report Description Item functions
//
// Implementation intended to minimise memory footprint,
// mildly at the expense of performance.
//
// See:
// https://www.usb.org/sites/default/files/hid1_11.pdf
// https://eleccelerator.com/usbdescreqparser/
// https://usb.org/sites/default/files/hut1_2.pdf
// https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/
//--------------------------------------------------------------------+
// Get the length of a short item
uint8_t tuh_hid_ri_short_data_length(const uint8_t *ri);
// Get the type of a short item
uint8_t tuh_hid_ri_short_type(const uint8_t *ri);
// Get the tag from a short item
uint8_t tuh_hid_ri_short_tag(const uint8_t *ri);
// Get the tag and type with length bits set to 0
uint8_t tuh_hid_ri_short_type_and_tag(const uint8_t *ri);
// Test if the item is a long item
bool tuh_hid_ri_is_long(const uint8_t *ri);
// Get the short item data unsigned
uint32_t tuh_hid_ri_short_udata32(const uint8_t *ri);
// Get the short item data unsigned
uint8_t tuh_hid_ri_short_udata8(const uint8_t *ri);
// Get the short item data signed (with sign extend to uint32)
int32_t tuh_hid_ri_short_data32(const uint8_t *ri);
// Get the data length of a long item
uint8_t tuh_hid_ri_long_data_length(const uint8_t *ri);
// Get the tag from a long item
uint8_t tuh_hid_ri_long_tag(const uint8_t *ri);
// Get a pointer to the data in a long item
const uint8_t* tuh_hid_ri_long_item_data(const uint8_t *ri);
#define HID_RI_EOF 0
#define HID_RI_ERR_MISSING_SHORT -1
#define HID_RI_ERR_MISSING_LONG -2
// Get the size of the item in bytes
//
// Important:
// To prevent buffer overflow call this before accessing short/long data
// and check the return code for eof or error.
//
// return values:
// HID_RI_EOF : no more values
// HID_RI_ERR_MISSING_SHORT : missing short bytes,
// HID_RI_ERR_MISSING_LONG : missing long bytes
int16_t tuh_hid_ri_size(const uint8_t *ri, uint16_t l);
// Split an extended usage into local and page values
void tuh_hid_ri_split_usage(uint32_t eusage, uint16_t *usage, uint16_t *usage_page);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_RI_H_ */

278
src/class/hid/hid_rip.c Normal file
View File

@ -0,0 +1,278 @@
/*
* The MIT License (MIT)
*
* 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_HOST_ENABLED && CFG_TUH_HID) || _UNITY_TEST_)
#include "hid_rip.h"
#include "hid.h"
void tuh_hid_rip_init_state(tuh_hid_rip_state_t *state, const uint8_t *report, uint16_t length)
{
state->cursor = report;
state->length = length;
state->item_length = 0;
state->stack_index = 0;
state->usage_count = 0;
state->collections_count = 0;
tu_memclr(&state->global_items, sizeof(uint8_t*) * HID_REPORT_STACK_SIZE * 16);
tu_memclr(&state->local_items, sizeof(uint8_t*) * 16);
state->status = HID_RIP_INIT;
}
const uint8_t* tuh_hid_rip_next_item(tuh_hid_rip_state_t *state)
{
// Already at eof
if (state->length == 0) return NULL;
const uint8_t *ri = state->cursor;
int16_t il = state->item_length;
// Previous error encountered so do nothing
if (il < 0) return NULL;
if (il > 0 && tuh_hid_ri_short_type(ri) == RI_TYPE_MAIN) {
// Clear down local state after a main item
tu_memclr(&state->local_items, sizeof(uint8_t*) * 16);
state->usage_count = 0;
}
// Advance to the next report item
ri += il;
state->cursor = ri;
state->length -= il;
// Normal eof
if (state->length == 0) {
state->item_length = 0;
state->status = HID_RIP_EOF;
return NULL;
}
// Check the report item is valid
state->item_length = il = tuh_hid_ri_size(ri, state->length);
if (il <= 0) {
state->status = HID_RIP_ITEM_ERR;
TU_LOG2("HID Report item parser: Attempt to read HID Report item returned %d\r\n", il);
return NULL;
}
if (il > 0 && !tuh_hid_ri_is_long(ri)) { // For now ignore long items.
uint8_t short_type = tuh_hid_ri_short_type(ri);
uint8_t short_tag = tuh_hid_ri_short_tag(ri);
switch (short_type) {
case RI_TYPE_GLOBAL:
state->global_items[state->stack_index][short_tag] = ri;
switch (short_tag) {
case RI_GLOBAL_PUSH:
if (++state->stack_index == HID_REPORT_STACK_SIZE) {
state->status = HID_RIP_STACK_OVERFLOW;
TU_LOG2("HID Report item parser: stack overflow\r\n");
return NULL;
}
memcpy(&state->global_items[state->stack_index], &state->global_items[state->stack_index - 1], sizeof(uint8_t*) * 16);
break;
case RI_GLOBAL_POP:
if (state->stack_index-- == 0) {
state->status = HID_RIP_STACK_UNDERFLOW;
TU_LOG2("HID Report item parser: stack underflow\r\n");
return NULL;
}
break;
default:
break;
}
break;
case RI_TYPE_LOCAL:
switch(short_tag) {
case RI_LOCAL_USAGE: {
uint32_t usage = tuh_hid_ri_short_udata32(ri);
if (tuh_hid_ri_short_data_length(ri) <= 2) {
const uint8_t* usage_page_item = state->global_items[state->stack_index][RI_GLOBAL_USAGE_PAGE];
uint32_t usage_page = usage_page_item ? tuh_hid_ri_short_udata32(usage_page_item) : 0;
usage |= usage_page << 16;
}
if (state->usage_count == HID_REPORT_MAX_USAGES) {
state->status = HID_RIP_USAGES_OVERFLOW;
TU_LOG2("HID Report item parser: usage overflow\r\n");
return NULL;
}
state->usages[state->usage_count++] = usage;
break;
}
default:
state->local_items[short_tag] = ri;
break;
}
break;
case RI_TYPE_MAIN: {
switch(short_tag) {
case RI_MAIN_COLLECTION: {
if (state->collections_count == HID_REPORT_MAX_COLLECTION_DEPTH) {
state->status = HID_RIP_COLLECTIONS_OVERFLOW;
TU_LOG2("HID Report item parser: collections overflow\r\n");
return NULL;
}
state->collections[state->collections_count++] = ri;
break;
}
case RI_MAIN_COLLECTION_END:
if (state->collections_count-- == 0) {
state->status = HID_RIP_COLLECTIONS_UNDERFLOW;
TU_LOG2("HID Report item parser: collections underflow\r\n");
return NULL;
}
break;
default:
break;
}
break;
}
default:
break;
}
}
state->status = HID_RIP_TTEM_OK;
return ri;
}
const uint8_t* tuh_hid_rip_next_short_item(tuh_hid_rip_state_t *state)
{
const uint8_t* ri;
while((ri = tuh_hid_rip_next_item(state)) != NULL) if (!tuh_hid_ri_is_long(ri)) break;
return ri;
}
const uint8_t* tuh_hid_rip_global(tuh_hid_rip_state_t *state, uint8_t tag)
{
return state->global_items[state->stack_index][tag];
}
const uint8_t* tuh_hid_rip_local(tuh_hid_rip_state_t *state, uint8_t tag)
{
return state->local_items[tag];
}
const uint8_t* tuh_hid_rip_current_item(tuh_hid_rip_state_t *state)
{
return state->cursor;
}
uint32_t tuh_hid_rip_report_total_size_bits(tuh_hid_rip_state_t *state)
{
const uint8_t* ri_report_size = tuh_hid_rip_global(state, RI_GLOBAL_REPORT_SIZE);
const uint8_t* ri_report_count = tuh_hid_rip_global(state, RI_GLOBAL_REPORT_COUNT);
if (ri_report_size != NULL && ri_report_count != NULL)
{
uint32_t report_size = tuh_hid_ri_short_udata32(ri_report_size);
uint32_t report_count = tuh_hid_ri_short_udata32(ri_report_count);
return report_size * report_count;
}
else
{
TU_LOG2("HID report parser cannot calc total report size in bits\r\n");
return 0;
}
}
//--------------------------------------------------------------------+
// Report Descriptor Parser
//--------------------------------------------------------------------+
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len)
{
// Prepare the summary array
tu_memclr(report_info_arr, arr_count*sizeof(tuh_hid_report_info_t));
uint8_t report_num = 0;
uint16_t usage = 0;
uint16_t usage_page = 0;
tuh_hid_report_info_t* info = report_info_arr;
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, desc_report, desc_len);
const uint8_t *ri;
while((ri = tuh_hid_rip_next_short_item(&pstate)) != NULL)
{
uint8_t const type_and_tag = tuh_hid_ri_short_type_and_tag(ri);
switch(type_and_tag)
{
case HID_RI_TYPE_AND_TAG(RI_TYPE_MAIN, RI_MAIN_INPUT): {
if (report_num >= arr_count) {
TU_LOG1("HID report description contains more than the maximum %d reports\r\n", arr_count);
return report_num;
}
info->in_len += tuh_hid_rip_report_total_size_bits(&pstate);
info->usage = usage;
info->usage_page = usage_page;
break;
}
case HID_RI_TYPE_AND_TAG(RI_TYPE_MAIN, RI_MAIN_OUTPUT): {
if (report_num >= arr_count) {
TU_LOG1("HID report description contains more than the maximum %d reports\r\n", arr_count);
return report_num;
}
info->out_len += tuh_hid_rip_report_total_size_bits(&pstate);
info->usage = usage;
info->usage_page = usage_page;
break;
}
case HID_RI_TYPE_AND_TAG(RI_TYPE_GLOBAL, RI_GLOBAL_REPORT_ID): {
if (report_num >= arr_count) {
TU_LOG1("HID report description contains more than the maximum %d reports\r\n", arr_count);
return report_num;
}
if (info->report_id > 0 || info->in_len > 0 || info->out_len > 0) {
info++;
report_num++;
}
info->report_id = tuh_hid_ri_short_udata8(ri);
break;
}
case HID_RI_TYPE_AND_TAG(RI_TYPE_LOCAL, RI_LOCAL_USAGE): {
// only take into account the "usage" before starting REPORT ID
if ( pstate.collections_count == 0 ) {
uint32_t eusage = pstate.usages[pstate.usage_count - 1];
tuh_hid_ri_split_usage(eusage, &usage, &usage_page);
}
break;
}
default: break;
}
}
for ( uint8_t i = 0; i < report_num + 1; i++ )
{
info = report_info_arr+i;
TU_LOG2("%u: id = %02X, usage_page = %04X, usage = %04X, in_len = %u, out_len = %u\r\n", i, info->report_id, info->usage_page, info->usage, info->in_len, info->out_len);
}
return report_num + 1;
}
#endif

154
src/class/hid/hid_rip.h Normal file
View File

@ -0,0 +1,154 @@
/*
* The MIT License (MIT)
*
* 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_HID_RIP_H_
#define _TUSB_HID_RIP_H_
#include "tusb.h"
#include "hid_ri.h"
#include "hid.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// HID Report Description Parser functions
//
// Implementation intended to minimise memory footprint,
// mildly at the expense of performance.
//
// Iterates through report items and manages state rules.
//
// Expected usage:
//
// tuh_hid_rip_state_t pstate;
// tuh_hid_rip_init_state(&pstate, desc_report, desc_len);
// const uint8_t *ri;
// while((ri = tuh_hid_rip_next_item(&pstate)) != NULL)
// {
// // ri points to the current hid report item
// ...
// }
//
// See:
// https://www.usb.org/sites/default/files/hid1_11.pdf
// https://eleccelerator.com/usbdescreqparser/
// https://usb.org/sites/default/files/hut1_2.pdf
// https://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/
//--------------------------------------------------------------------+
#define HID_REPORT_STACK_SIZE 10
#define HID_REPORT_MAX_USAGES 20
#define HID_REPORT_MAX_COLLECTION_DEPTH 20
#define HID_RIP_EUSAGE(G, L) ((G << 16) | L)
typedef enum tuh_hid_rip_status {
HID_RIP_INIT = 0, // Initial state
HID_RIP_EOF, // No more items
HID_RIP_TTEM_OK, // Last item parsed ok
HID_RIP_ITEM_ERR, // Issue decoding a single report item
HID_RIP_STACK_OVERFLOW, // Too many pushes
HID_RIP_STACK_UNDERFLOW, // Too many pops
HID_RIP_USAGES_OVERFLOW, // Too many usages
HID_RIP_COLLECTIONS_OVERFLOW, // Too many collections
HID_RIP_COLLECTIONS_UNDERFLOW // More collection ends than starts
} tuh_hid_rip_status_t;
typedef struct tuh_hid_rip_state {
const uint8_t* cursor;
uint16_t length;
int16_t item_length;
uint8_t stack_index;
uint8_t usage_count;
uint8_t collections_count;
const uint8_t* global_items[HID_REPORT_STACK_SIZE][16];
const uint8_t* local_items[16];
const uint8_t* collections[HID_REPORT_MAX_COLLECTION_DEPTH];
uint32_t usages[HID_REPORT_MAX_USAGES];
tuh_hid_rip_status_t status;
} tuh_hid_rip_state_t;
typedef struct
{
uint8_t report_id;
uint16_t usage;
uint16_t usage_page;
// TODO still use the endpoint size for now
// Note: these currently do not include the Report ID byte
uint16_t in_len; // length of IN report in bits
uint16_t out_len; // length of OUT report in bits
} tuh_hid_report_info_t;
// Initialise a report item descriptor parser
void tuh_hid_rip_init_state(tuh_hid_rip_state_t *state, const uint8_t *report, uint16_t length);
// Move to the next item in the report
//
// returns pointer to next item or null
//
const uint8_t* tuh_hid_rip_next_item(tuh_hid_rip_state_t *state);
// Move to the next short item in the report
//
// returns pointer to next item or null
//
const uint8_t* tuh_hid_rip_next_short_item(tuh_hid_rip_state_t *state);
// Accessor for the current value of a global item
//
// Returns a pointer to the start of the item of null
const uint8_t* tuh_hid_rip_global(tuh_hid_rip_state_t *state, uint8_t tag);
// Accessor for the current value of a local item
//
// Returns a pointer to the start of the item of null
const uint8_t* tuh_hid_rip_local(tuh_hid_rip_state_t *state, uint8_t tag);
// Returns a pointer to the start of the item or NULL for eof
const uint8_t* tuh_hid_rip_current_item(tuh_hid_rip_state_t *state);
// Return report_size * report_count
//
// Note: this currently does not include the Report ID byte
uint32_t tuh_hid_rip_report_total_size_bits(tuh_hid_rip_state_t *state);
//--------------------------------------------------------------------+
// Report Descriptor Parser
//
// Parse report descriptor into array of report_info struct and return number of reports.
// For complicated report, application should write its own parser.
//--------------------------------------------------------------------+
uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* reports_info_arr, uint8_t arr_count, uint8_t const* desc_report, uint16_t desc_len) TU_ATTR_UNUSED;
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_HID_RIP_H_ */

View File

@ -36,6 +36,7 @@
- -:test/support
:source:
- ../src/**
- ../examples/host/cdc_msc_hid/*
:support:
- test/support

View File

@ -0,0 +1,163 @@
/*
* The MIT License (MIT)
*
* 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 "unity.h"
#include "hid_ri.h"
TEST_FILE("hid_ri.c")
void setUp(void)
{
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
// Tests
//--------------------------------------------------------------------+
void test_short_item_length(void)
{
uint8_t tb[] = { 0x00, 0x01, 0x02, 0x03 };
TEST_ASSERT_EQUAL(0, tuh_hid_ri_short_data_length(&tb[0]));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_data_length(&tb[1]));
TEST_ASSERT_EQUAL(2, tuh_hid_ri_short_data_length(&tb[2]));
TEST_ASSERT_EQUAL(4, tuh_hid_ri_short_data_length(&tb[3]));
}
void test_short_item_size_check(void)
{
uint8_t tb[] = { 0x46, 0x3B, 0x01 }; /* Physical Maximum (315) */
TEST_ASSERT_EQUAL( 3, tuh_hid_ri_size(tb, 3));
TEST_ASSERT_EQUAL(-1, tuh_hid_ri_size(tb, 2));
TEST_ASSERT_EQUAL(-1, tuh_hid_ri_size(tb, 1));
TEST_ASSERT_EQUAL( 0, tuh_hid_ri_size(tb, 0));
}
void test_long_item(void)
{
uint8_t tb[] = { 0xfe, 0xff, 0x81, 0x01 };
TEST_ASSERT_EQUAL(true, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(2, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(255, tuh_hid_ri_long_data_length(tb));
TEST_ASSERT_EQUAL(0x81, tuh_hid_ri_long_tag(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_long_item_data(tb)[0]);
TEST_ASSERT_EQUAL(3 + 255, tuh_hid_ri_size(tb, 3 + 255));
}
void test_long_item_size_check(void)
{
uint8_t tb[] = { 0xfe, 0xff, 0x81, 0x01 };
TEST_ASSERT_EQUAL(3 + 255, tuh_hid_ri_size(tb, 3 + 255));
TEST_ASSERT_EQUAL(-2, tuh_hid_ri_size(tb, 3 + 254));
TEST_ASSERT_EQUAL(-2, tuh_hid_ri_size(tb, 3));
TEST_ASSERT_EQUAL(-1, tuh_hid_ri_size(tb, 2));
TEST_ASSERT_EQUAL(-1, tuh_hid_ri_size(tb, 1));
TEST_ASSERT_EQUAL( 0, tuh_hid_ri_size(tb, 0));
}
void test_physical_max_315(void)
{
uint8_t tb[] = { 0x46, 0x3B, 0x01 }; /* Physical Maximum (315) */
TEST_ASSERT_EQUAL(2, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_type(tb));
TEST_ASSERT_EQUAL(4, tuh_hid_ri_short_tag(tb));
TEST_ASSERT_EQUAL(HID_RI_TYPE_AND_TAG(1, 4), tuh_hid_ri_short_type_and_tag(tb));
TEST_ASSERT_EQUAL(false, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(315, tuh_hid_ri_short_udata32(tb));
TEST_ASSERT_EQUAL(315, tuh_hid_ri_short_data32(tb));
TEST_ASSERT_EQUAL(3, tuh_hid_ri_size(tb, 3));
}
void test_physical_max_123(void) {
uint8_t tb[] = { 0x46, 0x7B, 0x00 }; /* Physical Maximum (123) */
TEST_ASSERT_EQUAL(2, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_type(tb));
TEST_ASSERT_EQUAL(4, tuh_hid_ri_short_tag(tb));
TEST_ASSERT_EQUAL(false, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(123, tuh_hid_ri_short_udata32(tb));
TEST_ASSERT_EQUAL(123, tuh_hid_ri_short_udata8(tb));
TEST_ASSERT_EQUAL(123, tuh_hid_ri_short_data32(tb));
TEST_ASSERT_EQUAL(3, tuh_hid_ri_size(tb, 3));
}
void test_logical_min_neg_127(void)
{
uint8_t tb[] = { 0x15, 0x81 }; /* LOGICAL_MINIMUM (-127) */
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_type(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_tag(tb));
TEST_ASSERT_EQUAL(HID_RI_TYPE_AND_TAG(1, 1), tuh_hid_ri_short_type_and_tag(tb));
TEST_ASSERT_EQUAL(false, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(0x81, tuh_hid_ri_short_udata32(tb));
TEST_ASSERT_EQUAL(-127, tuh_hid_ri_short_data32(tb));
TEST_ASSERT_EQUAL(2, tuh_hid_ri_size(tb, 2));
}
void test_logical_min_neg_32768(void)
{
uint8_t tb[] = { 0x16, 0x00, 0x80 }; // Logical Minimum (-32768)
TEST_ASSERT_EQUAL(2, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_type(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_tag(tb));
TEST_ASSERT_EQUAL(false, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(-32768, tuh_hid_ri_short_data32(tb));
TEST_ASSERT_EQUAL(3, tuh_hid_ri_size(tb, 3));
}
// https://eleccelerator.com/usbdescreqparser/ says this should be -2147483648,
// which I am pretty sure is wrong.. but worth noting in case problems arise later.
void test_logical_min_neg_2147483647(void)
{
uint8_t tb[] = { 0x17, 0x01, 0x00, 0x00, 0x80 }; // Logical Minimum (-2147483647)
TEST_ASSERT_EQUAL(4, tuh_hid_ri_short_data_length(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_type(tb));
TEST_ASSERT_EQUAL(1, tuh_hid_ri_short_tag(tb));
TEST_ASSERT_EQUAL(false, tuh_hid_ri_is_long(tb));
TEST_ASSERT_EQUAL(-2147483647, tuh_hid_ri_short_data32(tb));
TEST_ASSERT_EQUAL(5, tuh_hid_ri_size(tb, 5));
}
void test_split_usage(void) {
uint16_t usage;
uint16_t usage_page;
tuh_hid_ri_split_usage(0xFA23832B, &usage, &usage_page);
TEST_ASSERT_EQUAL(0x832B, usage);
TEST_ASSERT_EQUAL(0xFA23, usage_page);
}

View File

@ -0,0 +1,708 @@
/*
* The MIT License (MIT)
*
* 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 "unity.h"
// Files to test
#include "hid_rip.h"
TEST_FILE("hid_ri.c")
TEST_FILE("hid_rip.c")
void setUp(void)
{
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
// Tests
//--------------------------------------------------------------------+
void test_next_simple(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(2, pstate.item_length);
TEST_ASSERT_EQUAL(0x05, *pstate.cursor);
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(2, pstate.item_length);
TEST_ASSERT_EQUAL(0x09, *pstate.cursor);
TEST_ASSERT_EQUAL(&tb[4], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(2, pstate.item_length);
TEST_ASSERT_EQUAL(0xA1, *pstate.cursor);
TEST_ASSERT_EQUAL(NULL, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.item_length);
}
void test_usage_with_page(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0x01, tuh_hid_ri_short_udata32(pstate.cursor));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(1, pstate.usage_count);
TEST_ASSERT_EQUAL(0x02, tuh_hid_ri_short_udata32(pstate.cursor));
TEST_ASSERT_EQUAL(0x00010002, pstate.usages[0]);
}
void test_globals_recorded(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x15, 0x55, // Logical Minimum 55
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.stack_index);
TEST_ASSERT_EQUAL(0x01, tuh_hid_ri_short_udata32(pstate.global_items[pstate.stack_index][0]));
TEST_ASSERT_EQUAL(0x55, tuh_hid_ri_short_data32(pstate.global_items[pstate.stack_index][1]));
}
void test_globals_overwritten(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x05, 0x09, // USAGE_PAGE (Button)
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.stack_index);
TEST_ASSERT_EQUAL(0x09, tuh_hid_ri_short_udata32(tuh_hid_rip_global(&pstate, 0)));
}
void test_push_pop(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0xA4, // Push
0x05, 0x09, // USAGE_PAGE (Button)
0xB4, // Pop
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[3], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[5], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.stack_index);
TEST_ASSERT_EQUAL(0x01, tuh_hid_ri_short_udata32(tuh_hid_rip_global(&pstate, 0)));
}
void test_stack_overflow(void)
{
uint8_t tb[HID_REPORT_STACK_SIZE];
memset(tb, 0xA4, sizeof(tb)); // Push
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_INIT);
for (int i = 0; i < HID_REPORT_STACK_SIZE - 1; ++i) {
TEST_ASSERT_EQUAL(&tb[i], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
}
TEST_ASSERT_EQUAL(NULL, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_STACK_OVERFLOW);
}
void test_stack_underflow(void)
{
uint8_t tb[] = {
0xA4, // Push
0xB4, // Pop
0xB4, // Pop
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_INIT);
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
TEST_ASSERT_EQUAL(&tb[1], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
TEST_ASSERT_EQUAL(0, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_STACK_UNDERFLOW);
}
void test_usage_overflow(void)
{
uint8_t tb[HID_REPORT_MAX_USAGES + 1];
memset(tb, 0x08, sizeof(tb)); // Usage
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_INIT);
for (int i = 0; i < HID_REPORT_MAX_USAGES; ++i) {
TEST_ASSERT_EQUAL(&tb[i], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
}
TEST_ASSERT_EQUAL(NULL, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_USAGES_OVERFLOW);
}
void test_collection_overflow(void)
{
uint8_t tb[HID_REPORT_MAX_COLLECTION_DEPTH + 1];
memset(tb, 0xA0, sizeof(tb)); // Collection start
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_INIT);
for (int i = 0; i < HID_REPORT_MAX_COLLECTION_DEPTH; ++i) {
TEST_ASSERT_EQUAL(&tb[i], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
}
TEST_ASSERT_EQUAL(NULL, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_COLLECTIONS_OVERFLOW);
}
void test_collection_underflow(void)
{
uint8_t tb[] = {
0xA0, // Collection start
0xC0, // Collection end
0xC0, // Collection end
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_INIT);
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
TEST_ASSERT_EQUAL(&tb[1], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_TTEM_OK);
TEST_ASSERT_EQUAL(0, tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(pstate.status, HID_RIP_COLLECTIONS_UNDERFLOW);
}
void test_main_clears_local(void)
{
uint8_t tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0x39, 0x04, // Designator Index 4
0xA1, 0x01, // Collection (Application)
0x75, 0x05, // REPORT_SIZE (5)
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[4], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[6], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(1, pstate.usage_count);
TEST_ASSERT_EQUAL(&tb[4], pstate.local_items[3]);
TEST_ASSERT_EQUAL(4, tuh_hid_ri_short_data32(pstate.local_items[3]));
TEST_ASSERT_EQUAL(&tb[8], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.global_items[pstate.stack_index][3]);
TEST_ASSERT_EQUAL(0, pstate.usage_count);
}
void test_collections(void)
{
uint8_t tb[] = {
0xA1, 0x01, // Collection (Application)
0xA1, 0x00, // Collection (Physical)
0xC0, // End Collection
0xC0, // End Collection
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(1, pstate.collections_count);
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(2, pstate.collections_count);
TEST_ASSERT_EQUAL(&tb[0], pstate.collections[0]);
TEST_ASSERT_EQUAL(&tb[2], pstate.collections[1]);
TEST_ASSERT_EQUAL(&tb[4], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(1, pstate.collections_count);
TEST_ASSERT_EQUAL(&tb[5], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(0, pstate.collections_count);
}
void test_total_size_bits(void)
{
uint8_t tb[] = {
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
};
tuh_hid_rip_state_t pstate;
tuh_hid_rip_init_state(&pstate, (uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(&tb[0], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(&tb[2], tuh_hid_rip_next_item(&pstate));
TEST_ASSERT_EQUAL(16, tuh_hid_rip_report_total_size_bits(&pstate));
}
void test_hid_parse_report_descriptor_single_mouse_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x02, // USAGE (Mouse)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x05, // REPORT_SIZE (5)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x06, // INPUT (Data,Var,Rel)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
TEST_ASSERT_EQUAL(1, report_info[0].usage_page);
TEST_ASSERT_EQUAL(2, report_info[0].usage);
TEST_ASSERT_EQUAL(0, report_info[0].report_id);
TEST_ASSERT_EQUAL(3*1 + 1*5 + 8*2, report_info[0].in_len);
TEST_ASSERT_EQUAL(0, report_info[0].out_len);
}
void test_hid_parse_report_descriptor_single_gamepad_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
// ReportID - 8 bits
0x85, 0x01, // REPORT_ID (1)
// X & Y - 2x8 = 16 bits
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x15, 0x81, // LOGICAL_MINIMUM (-127)
0x25, 0x7f, // LOGICAL_MAXIMUM (127)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x81, 0x02, // INPUT (Data,Var,Abs)
// Buttons - 8 bits
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x08, // USAGE_MAXIMUM (Button 8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
TEST_ASSERT_EQUAL(1, report_info[0].usage_page);
TEST_ASSERT_EQUAL(5, report_info[0].usage);
TEST_ASSERT_EQUAL(1, report_info[0].report_id);
TEST_ASSERT_EQUAL(8*2 + 8*1, report_info[0].in_len);
TEST_ASSERT_EQUAL(0, report_info[0].out_len);
}
void test_hid_parse_report_descriptor_dual_report(void) {
const uint8_t const tb[] = {
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID (1)
0x05, 0x0C, // Usage Page (Consumer)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x01, // Report Count (1)
0x09, 0xE2, // Usage (Mute)
0x81, 0x62, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,Null State)
0x85, 0x02, // Report ID (2)
0x05, 0x0C, // Usage Page (Consumer)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x09, 0xE2, // Usage (Mute)
0x09, 0xE2, // Usage (Mute)
0x81, 0x62, // Input (Data,Var,Abs,No Wrap,Linear,No Preferred State,Null State)
0xC0, // End Collection
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(2, report_count);
TEST_ASSERT_EQUAL(0, report_info[0].usage_page);
TEST_ASSERT_EQUAL(1, report_info[0].usage);
TEST_ASSERT_EQUAL(1, report_info[0].report_id);
TEST_ASSERT_EQUAL(1*1, report_info[0].in_len);
TEST_ASSERT_EQUAL(0, report_info[0].out_len);
TEST_ASSERT_EQUAL(0, report_info[1].usage_page);
TEST_ASSERT_EQUAL(1, report_info[1].usage);
TEST_ASSERT_EQUAL(2, report_info[1].report_id);
TEST_ASSERT_EQUAL(1*2, report_info[1].in_len);
TEST_ASSERT_EQUAL(0, report_info[1].out_len);
}
void test_hid_parse_report_descriptor_joystick_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x03, // Physical Maximum (1023)
0x65, 0x00, // Unit (None)
0x75, 0x0A, // Report Size (10)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x35, // Usage (Rz)
0x09, 0x32, // Usage (Z)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x01, // Logical Maximum (511)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x01, // Physical Maximum (511)
0x65, 0x00, // Unit (None)
0x75, 0x09, // Report Size (9)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x08, // Logical Maximum (8)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
TEST_ASSERT_EQUAL(1, report_info[0].usage_page);
TEST_ASSERT_EQUAL(4, report_info[0].usage);
TEST_ASSERT_EQUAL(0, report_info[0].report_id);
TEST_ASSERT_EQUAL(64, report_info[0].in_len);
TEST_ASSERT_EQUAL(0, report_info[0].out_len);
}
void test_hid_parse_greenasia_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x00, // Usage (Undefined)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x09, 0x01, // Usage (0x01)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x02, // Usage (0x02)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
}
void test_hid_parse_speedlink_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x09, 0x01, // Usage (0x01)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x07, // Report Count (7)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x02, // Usage (0x02)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
tuh_hid_report_info_t report_info[3];
uint8_t report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
}
void test_hid_parse_keyboard_and_trackpad_report(void) {
const uint8_t const tb[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, // Report ID (2)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x10, // Usage Maximum (0x10)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x95, 0x10, // Report Count (16)
0x75, 0x01, // Report Size (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x16, 0x01, 0xF8, // Logical Minimum (-2047)
0x26, 0xFF, 0x07, // Logical Maximum (2047)
0x75, 0x0C, // Report Size (12)
0x95, 0x02, // Report Count (2)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x15, 0x81, // Logical Minimum (-127)
0x25, 0x7F, // Logical Maximum (127)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x09, 0x38, // Usage (Wheel)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x0C, // Usage Page (Consumer)
0x0A, 0x38, 0x02, // Usage (AC Pan)
0x95, 0x01, // Report Count (1)
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
0x05, 0x0C, // Usage Page (Consumer)
0x09, 0x01, // Usage (Consumer Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x03, // Report ID (3)
0x75, 0x10, // Report Size (16)
0x95, 0x02, // Report Count (2)
0x15, 0x01, // Logical Minimum (1)
0x26, 0x8C, 0x02, // Logical Maximum (652)
0x19, 0x01, // Usage Minimum (Consumer Control)
0x2A, 0x8C, 0x02, // Usage Maximum (AC Send)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x80, // Usage (Sys Control)
0xA1, 0x01, // Collection (Application)
0x85, 0x04, // Report ID (4)
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x03, // Logical Maximum (3)
0x09, 0x82, // Usage (Sys Sleep)
0x09, 0x81, // Usage (Sys Power Down)
0x09, 0x83, // Usage (Sys Wake Up)
0x81, 0x60, // Input (Data,Array,Abs,No Wrap,Linear,No Preferred State,Null State)
0x75, 0x06, // Report Size (6)
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0x06, 0xBC, 0xFF, // Usage Page (Vendor Defined 0xFFBC)
0x09, 0x88, // Usage (0x88)
0xA1, 0x01, // Collection (Application)
0x85, 0x08, // Report ID (8)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0xFF, // Usage Maximum (0xFF)
0x15, 0x01, // Logical Minimum (1)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
};
tuh_hid_report_info_t report_info[5];
uint8_t report_count;
report_count = tuh_hid_parse_report_descriptor(report_info, 1, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(1, report_count);
report_count = tuh_hid_parse_report_descriptor(report_info, 2, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(2, report_count);
report_count = tuh_hid_parse_report_descriptor(report_info, 3, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(3, report_count);
report_count = tuh_hid_parse_report_descriptor(report_info, 4, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(4, report_count);
report_count = tuh_hid_parse_report_descriptor(report_info, 5, (const uint8_t*)&tb, sizeof(tb));
TEST_ASSERT_EQUAL(4, report_count);
TEST_ASSERT_EQUAL(1, report_info[0].usage_page);
TEST_ASSERT_EQUAL(2, report_info[0].usage);
TEST_ASSERT_EQUAL(2, report_info[0].report_id);
TEST_ASSERT_EQUAL(56, report_info[0].in_len);
TEST_ASSERT_EQUAL(0, report_info[0].out_len);
TEST_ASSERT_EQUAL(0xC, report_info[1].usage_page);
TEST_ASSERT_EQUAL(0x1, report_info[1].usage);
TEST_ASSERT_EQUAL(3, report_info[1].report_id);
TEST_ASSERT_EQUAL(32, report_info[1].in_len);
TEST_ASSERT_EQUAL(0, report_info[1].out_len);
TEST_ASSERT_EQUAL(1, report_info[2].usage_page);
TEST_ASSERT_EQUAL(0x80, report_info[2].usage);
TEST_ASSERT_EQUAL(4, report_info[2].report_id);
TEST_ASSERT_EQUAL(8, report_info[2].in_len);
TEST_ASSERT_EQUAL(0, report_info[2].out_len);
TEST_ASSERT_EQUAL(0xFFBC, report_info[3].usage_page);
TEST_ASSERT_EQUAL(0x88, report_info[3].usage);
TEST_ASSERT_EQUAL(8, report_info[3].report_id);
TEST_ASSERT_EQUAL(8, report_info[3].in_len);
TEST_ASSERT_EQUAL(0, report_info[3].out_len);
}

View File

@ -0,0 +1,520 @@
/*
* The MIT License (MIT)
*
* 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 "unity.h"
// Files to test
#include "hid_rip.h"
#include "hid_host_joy.h"
TEST_FILE("hid_ri.c")
TEST_FILE("hid_rip.c")
TEST_FILE("hid_host_utils.c")
TEST_FILE("hid_host_joy.c")
static const uint8_t const tb_greenasia[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x00, // Usage (Undefined)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x09, 0x01, // Usage (0x01)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x04, // Report Count (4)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x02, // Usage (0x02)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
static const uint8_t const tb_speedlink[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x05, // Report Count (5)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x09, 0x32, // Usage (Z)
0x09, 0x32, // Usage (Z)
0x09, 0x35, // Usage (Rz)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x25, 0x07, // Logical Maximum (7)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
0x65, 0x00, // Unit (None)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
0x75, 0x01, // Report Size (1)
0x95, 0x08, // Report Count (8)
0x25, 0x01, // Logical Maximum (1)
0x45, 0x01, // Physical Maximum (1)
0x09, 0x01, // Usage (0x01)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xA1, 0x02, // Collection (Logical)
0x75, 0x08, // Report Size (8)
0x95, 0x07, // Report Count (7)
0x46, 0xFF, 0x00, // Physical Maximum (255)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x09, 0x02, // Usage (0x02)
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
0xC0, // End Collection
0xC0, // End Collection
};
static const uint8_t const tb_apple[] = {
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x04, // Usage (Joystick)
0xA1, 0x01, // Collection (Application)
0x09, 0x01, // Usage (Pointer)
0xA1, 0x00, // Collection (Physical)
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x30, // Usage (X)
0x09, 0x31, // Usage (Y)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x03, // Logical Maximum (1023)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x03, // Physical Maximum (1023)
0x65, 0x00, // Unit (None)
0x75, 0x0A, // Report Size (10)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x35, // Usage (Rz)
0x09, 0x32, // Usage (Z)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x01, // Logical Maximum (511)
0x35, 0x00, // Physical Minimum (0)
0x46, 0xFF, 0x01, // Physical Maximum (511)
0x65, 0x00, // Unit (None)
0x75, 0x09, // Report Size (9)
0x95, 0x02, // Report Count (2)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x02, // Report Count (2)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x09, 0x39, // Usage (Hat switch)
0x15, 0x01, // Logical Minimum (1)
0x25, 0x08, // Logical Maximum (8)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
0x75, 0x08, // Report Size (8)
0x95, 0x01, // Report Count (1)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (0x01)
0x29, 0x0C, // Usage Maximum (0x0C)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x35, 0x00, // Physical Minimum (0)
0x45, 0x01, // Physical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x0C, // Report Count (12)
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x75, 0x01, // Report Size (1)
0x95, 0x04, // Report Count (4)
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0, // End Collection
0xC0, // End Collection
};
void setUp(void)
{
tuh_hid_free_simple_joysticks();
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
// Tests
//--------------------------------------------------------------------+
void test_simple_joystick_allocator(void) {
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(5, 1, 2));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(5, 1, 3));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(4, 2, 3));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(4, 5, 2));
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(5, 1, 2));
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(5, 1, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(4, 2, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(4, 5, 2));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(5, 1, 2));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(5, 1, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(4, 2, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(4, 5, 2));
tuh_hid_free_simple_joysticks_for_instance(5, 1);
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(5, 1, 2));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(5, 1, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(4, 2, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(4, 5, 2));
tuh_hid_free_simple_joysticks_for_instance(4, 5);
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(4, 5, 2));
TEST_ASSERT_NOT_NULL(tuh_hid_get_simple_joystick(4, 2, 3));
}
void test_simple_joystick_allocate_too_many(void) {
for (int i =0; i < HID_MAX_JOYSTICKS; ++i) {
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(1, i + 1, 4));
}
TEST_ASSERT_NULL(tuh_hid_allocate_simple_joystick(2, 1, 0));
}
void test_simple_joystick_free_all(void) {
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(7, 1, 3));
TEST_ASSERT_NOT_NULL(tuh_hid_allocate_simple_joystick(0, 2, 3));
tuh_hid_free_simple_joysticks();
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(7, 1, 3));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(0, 2, 3));
}
void test_simple_joystick_obtain(void) {
tusb_hid_simple_joysick_t* j1 = tuh_hid_allocate_simple_joystick(7, 1, 3);
TEST_ASSERT_NOT_NULL(j1);
TEST_ASSERT_EQUAL(j1, tuh_hid_obtain_simple_joystick(7, 1, 3));
TEST_ASSERT_NULL(tuh_hid_get_simple_joystick(7, 2, 3));
tusb_hid_simple_joysick_t* j2 = tuh_hid_allocate_simple_joystick(7, 2, 3);
TEST_ASSERT_NOT_NULL(j2);
TEST_ASSERT_NOT_EQUAL(j1,j2);
}
void test_tuh_hid_joystick_get_data(void) {
tuh_hid_joystick_data_t joystick_data;
tuh_hid_rip_state_t pstate;
tusb_hid_simple_joysick_t* simple_joystick;
tuh_hid_rip_init_state(&pstate, tb_speedlink, sizeof(tb_speedlink));
const uint8_t *ri;
while((ri = tuh_hid_rip_next_item(&pstate)) != NULL ) if (ri >= &tb_speedlink[32]) break;
TEST_ASSERT_EQUAL(&tb_speedlink[32], ri); // Move to the first input in the speedlink description
TEST_ASSERT_EQUAL(true, tuh_hid_joystick_get_data(&pstate, ri, &joystick_data));
TEST_ASSERT_EQUAL(8, joystick_data.report_size);
TEST_ASSERT_EQUAL(5, joystick_data.report_count);
TEST_ASSERT_EQUAL(0, joystick_data.report_id);
TEST_ASSERT_EQUAL(0, joystick_data.logical_min);
TEST_ASSERT_EQUAL(255, joystick_data.logical_max);
TEST_ASSERT_EQUAL(false, joystick_data.usage_is_range);
// Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.data_const);
TEST_ASSERT_EQUAL(true, joystick_data.input_flags.array_variable);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.absolute_relative);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.nowrap_wrap);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.linear_nonlinear);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.prefered_noprefered);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.nonull_null);
tuh_hid_joystick_process_usages(&pstate, &joystick_data, 0, 5, 9);
simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
// x1
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x1.flags.is_signed);
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x1.logical_min);
TEST_ASSERT_EQUAL(255, simple_joystick->axis_x1.logical_max);
// y1
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y1.flags.is_signed);
TEST_ASSERT_EQUAL(0, simple_joystick->axis_y1.logical_min);
TEST_ASSERT_EQUAL(255, simple_joystick->axis_y1.logical_max);
// x2
TEST_ASSERT_EQUAL(24, simple_joystick->axis_x2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x2.flags.is_signed);
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x2.logical_min);
TEST_ASSERT_EQUAL(255, simple_joystick->axis_x2.logical_max);
// y2
TEST_ASSERT_EQUAL(32, simple_joystick->axis_y2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y2.flags.is_signed);
TEST_ASSERT_EQUAL(0, simple_joystick->axis_y2.logical_min);
TEST_ASSERT_EQUAL(255, simple_joystick->axis_y2.logical_max);
while((ri = tuh_hid_rip_next_item(&pstate)) != NULL ) if (ri >= &tb_speedlink[47]) break;
TEST_ASSERT_EQUAL(&tb_speedlink[47], ri); // Move to the second input in the speedlink description
TEST_ASSERT_EQUAL(true, tuh_hid_joystick_get_data(&pstate, ri, &joystick_data));
TEST_ASSERT_EQUAL(4, joystick_data.report_size);
TEST_ASSERT_EQUAL(1, joystick_data.report_count);
TEST_ASSERT_EQUAL(0, joystick_data.report_id);
TEST_ASSERT_EQUAL(0, joystick_data.logical_min);
TEST_ASSERT_EQUAL(7, joystick_data.logical_max);
TEST_ASSERT_EQUAL(false, joystick_data.usage_is_range);
// Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.data_const);
TEST_ASSERT_EQUAL(true, joystick_data.input_flags.array_variable);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.absolute_relative);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.nowrap_wrap);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.linear_nonlinear);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.prefered_noprefered);
TEST_ASSERT_EQUAL(true, joystick_data.input_flags.nonull_null);
tuh_hid_joystick_process_usages(&pstate, &joystick_data, 40, 5, 9);
simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
TEST_ASSERT_EQUAL(40, simple_joystick->hat.start);
TEST_ASSERT_EQUAL(4, simple_joystick->hat.length);
while((ri = tuh_hid_rip_next_item(&pstate)) != NULL ) if (ri >= &tb_speedlink[65]) break;
TEST_ASSERT_EQUAL(&tb_speedlink[65], ri); // Move to the second input in the speedlink description
TEST_ASSERT_EQUAL(true, tuh_hid_joystick_get_data(&pstate, ri, &joystick_data));
TEST_ASSERT_EQUAL(1, joystick_data.report_size);
TEST_ASSERT_EQUAL(12, joystick_data.report_count);
TEST_ASSERT_EQUAL(0, joystick_data.report_id);
TEST_ASSERT_EQUAL(0, joystick_data.logical_min);
TEST_ASSERT_EQUAL(1, joystick_data.logical_max);
TEST_ASSERT_EQUAL(true, joystick_data.usage_is_range);
TEST_ASSERT_EQUAL(1, joystick_data.usage_min);
TEST_ASSERT_EQUAL(12, joystick_data.usage_max);
// Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.data_const);
TEST_ASSERT_EQUAL(true, joystick_data.input_flags.array_variable);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.absolute_relative);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.nowrap_wrap);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.linear_nonlinear);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.prefered_noprefered);
TEST_ASSERT_EQUAL(false, joystick_data.input_flags.nonull_null);
tuh_hid_joystick_process_usages(&pstate, &joystick_data, 44, 5, 9); // 56
simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
TEST_ASSERT_EQUAL(44, simple_joystick->buttons.start);
TEST_ASSERT_EQUAL(12, simple_joystick->buttons.length);
TEST_ASSERT_EQUAL(7, simple_joystick->report_length);
}
void test_hid_parse_greenasia_report(void) {
tuh_hid_joystick_parse_report_descriptor(tb_speedlink, sizeof(tb_speedlink), 5, 9);
tusb_hid_simple_joysick_t* simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
// x1
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x1.flags.is_signed);
// y1
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y1.flags.is_signed);
// x2
TEST_ASSERT_EQUAL(24, simple_joystick->axis_x2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x2.flags.is_signed);
// y2
TEST_ASSERT_EQUAL(32, simple_joystick->axis_y2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y2.flags.is_signed);
TEST_ASSERT_EQUAL(40, simple_joystick->hat.start);
TEST_ASSERT_EQUAL(4, simple_joystick->hat.length);
TEST_ASSERT_EQUAL(44, simple_joystick->buttons.start);
TEST_ASSERT_EQUAL(12, simple_joystick->buttons.length);
TEST_ASSERT_EQUAL(8, simple_joystick->report_length);
}
void test_hid_parse_speedlink_report(void) {
tuh_hid_joystick_parse_report_descriptor(tb_greenasia, sizeof(tb_greenasia), 5, 9);
tusb_hid_simple_joysick_t* simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
// x1
TEST_ASSERT_EQUAL(16, simple_joystick->axis_x1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x1.flags.is_signed);
// y1
TEST_ASSERT_EQUAL(24, simple_joystick->axis_y1.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y1.flags.is_signed);
// x2
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_x2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x2.flags.is_signed);
// y2
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y2.start);
TEST_ASSERT_EQUAL(8, simple_joystick->axis_y2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y2.flags.is_signed);
TEST_ASSERT_EQUAL(40, simple_joystick->hat.start);
TEST_ASSERT_EQUAL(4, simple_joystick->hat.length);
TEST_ASSERT_EQUAL(44, simple_joystick->buttons.start);
TEST_ASSERT_EQUAL(12, simple_joystick->buttons.length);
TEST_ASSERT_EQUAL(8, simple_joystick->report_length);
}
void test_apple_joystick(void) {
// Thanks to https://jenswilly.dk/2012/10/parsing-usb-joystick-hid-data/
// 10 bits of X-axis data (for values from 0 to 1023)
// 10 bits of Y-axis data (for values from 0 to 1023)
// 9 bits of Rz-axis (yaw or rotation) data (for values from 0-511)
// 9 bits of Z-axis (throttle) data (for values from 0-511)
// 2 "constant" bits i.e. unused padding bits
// 8 bits of hat switch data (though values are only from 1-8)
// 12 bits of button states (12 buttons of 0 or 1 values)
// 4 bits of padding
tuh_hid_joystick_parse_report_descriptor(tb_apple, sizeof(tb_apple), 5, 9);
tusb_hid_simple_joysick_t* simple_joystick = tuh_hid_get_simple_joystick(5, 9, 0);
TEST_ASSERT_NOT_NULL(simple_joystick);
// x1
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x1.start);
TEST_ASSERT_EQUAL(10, simple_joystick->axis_x1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x1.flags.is_signed);
TEST_ASSERT_EQUAL(0, simple_joystick->axis_x1.logical_min);
TEST_ASSERT_EQUAL(1023, simple_joystick->axis_x1.logical_max);
// y1
TEST_ASSERT_EQUAL(10, simple_joystick->axis_y1.start);
TEST_ASSERT_EQUAL(10, simple_joystick->axis_y1.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y1.flags.is_signed);
// x2
TEST_ASSERT_EQUAL(29, simple_joystick->axis_x2.start);
TEST_ASSERT_EQUAL(9, simple_joystick->axis_x2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_x2.flags.is_signed);
// y2
TEST_ASSERT_EQUAL(20, simple_joystick->axis_y2.start);
TEST_ASSERT_EQUAL(9, simple_joystick->axis_y2.length);
TEST_ASSERT_EQUAL(false, simple_joystick->axis_y2.flags.is_signed);
TEST_ASSERT_EQUAL(40, simple_joystick->hat.start);
TEST_ASSERT_EQUAL(8, simple_joystick->hat.length);
TEST_ASSERT_EQUAL(48, simple_joystick->buttons.start);
TEST_ASSERT_EQUAL(12, simple_joystick->buttons.length);
// Raw hex: e9 31 e8 ef 3f 00 00 00
//
// Binary 11101001 00110001 11101000 11101111 00111111 00000000 00000000 00000000
// field XXXXXXXX YYYYYYXX RRRRYYYY ZZZRRRRR --ZZZZZZ HHHHHHHH BBBBBBBB ----BBBB
// bit no. 76543210 54321098 32109876 21087654 --876543 76543210 76543210 ----BA98
//
// X = X-axis: b0111101001 = 489
// Y = Y-axis: b1000001100 = 524
// R = Rz-axis: b011111110 = 254
// Z = Z-axis: b111111111 = 511
// H = hat: b00000000 = 0 (centered)
// B = buttons b000000000000 = 0 (no buttons pressed)
// - = padding
uint8_t report[] = {0xe9, 0x31, 0xe8, 0xef, 0x3f, 0x00, 0x00, 0x00};
tusb_hid_simple_joysick_process_report(simple_joystick, report, sizeof(report));
TEST_ASSERT_EQUAL(true, simple_joystick->has_values);
TEST_ASSERT_EQUAL(489, simple_joystick->values.x1);
TEST_ASSERT_EQUAL(524, simple_joystick->values.y1);
TEST_ASSERT_EQUAL(511, simple_joystick->values.x2);
TEST_ASSERT_EQUAL(254, simple_joystick->values.y2);
TEST_ASSERT_EQUAL(0, simple_joystick->values.hat);
TEST_ASSERT_EQUAL(0, simple_joystick->values.buttons);
TEST_ASSERT_EQUAL(8, simple_joystick->report_length);
}
void test_get_simple_joysticks(void) {
static tusb_hid_simple_joysick_t* hid_simple_joysicks[4];
uint8_t jcount;
jcount = tuh_hid_get_simple_joysticks(hid_simple_joysicks, 4);
TEST_ASSERT_EQUAL(0, jcount);
tuh_hid_joystick_parse_report_descriptor(tb_speedlink, sizeof(tb_speedlink), 5, 9);
jcount = tuh_hid_get_simple_joysticks(hid_simple_joysicks, 4);
TEST_ASSERT_EQUAL(1, jcount);
TEST_ASSERT_EQUAL(tuh_hid_get_simple_joystick(5, 9, 0), hid_simple_joysicks[0]);
tuh_hid_joystick_parse_report_descriptor(tb_apple, sizeof(tb_apple), 1, 3);
jcount = tuh_hid_get_simple_joysticks(hid_simple_joysicks, 1);
TEST_ASSERT_EQUAL(1, jcount);
jcount = tuh_hid_get_simple_joysticks(hid_simple_joysicks, 4);
TEST_ASSERT_EQUAL(2, jcount);
TEST_ASSERT_EQUAL(tuh_hid_get_simple_joystick(5, 9, 0), hid_simple_joysicks[0]);
TEST_ASSERT_EQUAL(tuh_hid_get_simple_joystick(1, 3, 0), hid_simple_joysicks[1]);
tuh_hid_free_simple_joysticks_for_instance(5, 9);
jcount = tuh_hid_get_simple_joysticks(hid_simple_joysicks, 4);
TEST_ASSERT_EQUAL(1, jcount);
TEST_ASSERT_EQUAL(tuh_hid_get_simple_joystick(1, 3, 0), hid_simple_joysicks[0]);
}
// TODO Test with report ID
// anyone got a joystick that reports with an ID?

View File

@ -0,0 +1,97 @@
/*
* The MIT License (MIT)
*
* 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 "unity.h"
// Files to test
#include "hid_host_utils.h"
TEST_FILE("hid_host_utils.c")
void setUp(void)
{
}
void tearDown(void)
{
}
//--------------------------------------------------------------------+
// Tests
//--------------------------------------------------------------------+
void test_tuh_hid_report_bits_u32(void) {
const uint8_t const tb[] = {
0x50, 0x05, 0x8f, 0xff
};
TEST_ASSERT_EQUAL(0x55, tuh_hid_report_bits_u32(tb, 4, 8));
TEST_ASSERT_EQUAL(0x8f, tuh_hid_report_bits_u32(tb, 16, 8));
TEST_ASSERT_EQUAL((uint32_t)0xff8f0550UL, tuh_hid_report_bits_u32(tb, 0, 32));
}
void test_tuh_hid_report_bits_i32(void) {
const uint8_t const tb[] = {
0x50, 0x05, 0x8f, 0xff
};
TEST_ASSERT_EQUAL(0x55, tuh_hid_report_bits_i32(tb, 4, 8));
TEST_ASSERT_EQUAL(-113, tuh_hid_report_bits_i32(tb, 16, 8)); // int32_t 0xffffff8f == -113
TEST_ASSERT_EQUAL(-7404208, tuh_hid_report_bits_i32(tb, 0, 32)); // int32_t 0xff8f0550 == -7404208
}
void test_tuh_hid_report_bytes_u32(void) {
const uint8_t const tb[] = {
0x50, 0x05, 0x8f, 0xff
};
TEST_ASSERT_EQUAL(0x0550, tuh_hid_report_bytes_u32(tb, 0, 2));
TEST_ASSERT_EQUAL(0x8f05, tuh_hid_report_bytes_u32(tb, 1, 2));
TEST_ASSERT_EQUAL(0xff8f05, tuh_hid_report_bytes_u32(tb, 1, 3));
TEST_ASSERT_EQUAL(0xff8f0550, tuh_hid_report_bytes_u32(tb, 0, 4));
TEST_ASSERT_EQUAL(0x8f, tuh_hid_report_bytes_u32(tb, 2, 1));
}
void test_tuh_hid_report_bytes_i32(void) {
const uint8_t const tb[] = {
0x50, 0x05, 0x8f, 0xff
};
TEST_ASSERT_EQUAL(0x0550, tuh_hid_report_bytes_i32(tb, 0, 2));
TEST_ASSERT_EQUAL(-28923, tuh_hid_report_bytes_i32(tb, 1, 2)); // int32_t 0xffff8f05 == -28923
TEST_ASSERT_EQUAL(-28923, tuh_hid_report_bytes_i32(tb, 1, 3)); // int32_t 0xffff8f05 == -28923
TEST_ASSERT_EQUAL(-7404208, tuh_hid_report_bytes_i32(tb, 0, 4)); // int32_t 0xff8f0550 == -7404208
TEST_ASSERT_EQUAL(-113, tuh_hid_report_bytes_i32(tb, 2, 1)); // int32_t 0xffffff8f == -113
}
void test_tuh_hid_report_i32(void) {
const uint8_t const tb[] = {
0x50, 0x05, 0x8f, 0xff
};
TEST_ASSERT_EQUAL(0x55, tuh_hid_report_i32(tb, 4, 8, false));
TEST_ASSERT_EQUAL(0x8f, tuh_hid_report_i32(tb, 16, 8, false));
TEST_ASSERT_EQUAL(-7404208, tuh_hid_report_i32(tb, 0, 32, false)); // int32_t 0xff8f0550 == -7404208
TEST_ASSERT_EQUAL(0x55, tuh_hid_report_i32(tb, 4, 8, true));
TEST_ASSERT_EQUAL(-113, tuh_hid_report_i32(tb, 16, 8, true)); // int32_t 0xffffff8f == -113
TEST_ASSERT_EQUAL(-7404208, tuh_hid_report_i32(tb, 0, 32, true)); // int32_t 0xff8f0550 == -7404208
}

View File

@ -31,6 +31,8 @@
</group>
<group name="src/class/hid">
<path>$TUSB_DIR$/src/class/hid/hid_device.c</path>
<path>$TUSB_DIR$/src/class/hid/hid_ri.c</path>
<path>$TUSB_DIR$/src/class/hid/hid_rip.c</path>
<path>$TUSB_DIR$/src/class/hid/hid_host.c</path>
</group>
<group name="src/class/midi">