Merge 48d86297df
into fd5bb6e5db
This commit is contained in:
commit
c10d01c5fb
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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_ */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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_ */
|
||||
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
@ -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
|
||||
//--------------------------------------------------------------------+
|
||||
|
|
|
@ -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
|
|
@ -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_ */
|
|
@ -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
|
||||
|
||||
|
|
@ -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_ */
|
|
@ -36,6 +36,7 @@
|
|||
- -:test/support
|
||||
:source:
|
||||
- ../src/**
|
||||
- ../examples/host/cdc_msc_hid/*
|
||||
:support:
|
||||
- test/support
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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?
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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">
|
||||
|
|
Loading…
Reference in New Issue