btstack_hid_parser: extract btstack_hid_usage_iterator_t

This commit is contained in:
Matthias Ringwald 2024-08-13 16:20:46 +02:00
parent cc770b389f
commit c4241e61df
3 changed files with 125 additions and 169 deletions

View File

@ -118,7 +118,7 @@ static const char * local_tags[] = {
};
#endif
static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
static void hid_pretty_print_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
#ifdef HID_PARSER_PRETTY_PRINT
const char ** item_tag_table;
switch ((TagType)item->item_type){
@ -139,9 +139,9 @@ static void hid_pretty_print_item(btstack_hid_parser_t * parser, hid_descriptor_
if (item_tag_table){
item_tag_name = item_tag_table[item->item_tag];
}
log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], parser->descriptor[parser->descriptor_pos], item->item_value);
log_info("%-15s (%-6s) // %02x 0x%0008x", item_tag_name, type_names[item->item_type], iterator->descriptor[iterator->descriptor_pos], item->item_value);
#else
UNUSED(parser);
UNUSED(iterator);
UNUSED(item);
#endif
}
@ -200,25 +200,25 @@ static bool btstack_hid_main_item_tag_matches_report_type(MainItemTag tag, hid_r
}
}
static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
static void btstack_hid_handle_global_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
switch((GlobalItemTag)item->item_tag){
case UsagePage:
parser->global_usage_page = item->item_value;
iterator->global_usage_page = item->item_value;
break;
case LogicalMinimum:
parser->global_logical_minimum = item->item_value;
iterator->global_logical_minimum = item->item_value;
break;
case LogicalMaximum:
parser->global_logical_maximum = item->item_value;
iterator->global_logical_maximum = item->item_value;
break;
case ReportSize:
parser->global_report_size = item->item_value;
iterator->global_report_size = item->item_value;
break;
case ReportID:
parser->global_report_id = item->item_value;
iterator->global_report_id = item->item_value;
break;
case ReportCount:
parser->global_report_count = item->item_value;
iterator->global_report_count = item->item_value;
break;
// TODO handle tags
@ -236,97 +236,47 @@ static void btstack_hid_handle_global_item(btstack_hid_parser_t * parser, hid_de
}
}
static void hid_find_next_usage(btstack_hid_parser_t * parser){
static void hid_find_next_usage(btstack_hid_usage_iterator_t * main_iterator){
bool have_usage_min = false;
bool have_usage_max = false;
parser->usage_range = false;
main_iterator->usage_range = false;
btstack_hid_descriptor_iterator_t iterator;
btstack_hid_descriptor_iterator_init(&iterator, &parser->descriptor[parser->usage_pos], parser->descriptor_len - parser->usage_pos);
while ((parser->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
btstack_hid_descriptor_iterator_init(&iterator, &main_iterator->descriptor[main_iterator->usage_pos], main_iterator->descriptor_len - main_iterator->usage_pos);
while ((main_iterator->available_usages == 0u) && btstack_hid_descriptor_iterator_has_more(&iterator) ){
hid_descriptor_item_t usage_item = *btstack_hid_descriptor_iterator_get_item(&iterator);
if ((usage_item.item_type == Global) && (usage_item.item_tag == UsagePage)){
parser->usage_page = usage_item.item_value;
main_iterator->usage_page = usage_item.item_value;
}
if (usage_item.item_type == Local){
uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((parser->usage_page << 16u) | usage_item.item_value);
uint32_t usage_value = (usage_item.data_size > 2u) ? usage_item.item_value : ((main_iterator->usage_page << 16u) | usage_item.item_value);
switch (usage_item.item_tag){
case Usage:
parser->available_usages = 1;
parser->usage_minimum = usage_value;
main_iterator->available_usages = 1;
main_iterator->usage_minimum = usage_value;
break;
case UsageMinimum:
parser->usage_minimum = usage_value;
main_iterator->usage_minimum = usage_value;
have_usage_min = true;
break;
case UsageMaximum:
parser->usage_maximum = usage_value;
main_iterator->usage_maximum = usage_value;
have_usage_max = true;
break;
default:
break;
}
if (have_usage_min && have_usage_max){
parser->available_usages = parser->usage_maximum - parser->usage_minimum + 1u;
parser->usage_range = true;
if (parser->available_usages < parser->required_usages){
log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", parser->usage_minimum & 0xffff, parser->usage_maximum & 0xffff, parser->required_usages);
main_iterator->available_usages = main_iterator->usage_maximum - main_iterator->usage_minimum + 1u;
main_iterator->usage_range = true;
if (main_iterator->available_usages < main_iterator->required_usages){
log_debug("Usage Min - Usage Max [%04"PRIx32"..%04"PRIx32"] < Report Count %u", main_iterator->usage_minimum & 0xffff, main_iterator->usage_maximum & 0xffff, main_iterator->required_usages);
}
}
}
}
parser->usage_pos += iterator.descriptor_pos;
main_iterator->usage_pos += iterator.descriptor_pos;
}
static void hid_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
hid_pretty_print_item(parser, item);
int valid_field = 0;
uint16_t report_id_before;
switch ((TagType)item->item_type){
case Main:
valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
parser->report_type);
break;
case Global:
report_id_before = parser->global_report_id;
btstack_hid_handle_global_item(parser, item);
// track record id for report handling
if ((GlobalItemTag)item->item_tag == ReportID){
if (parser->active_record && (report_id_before != item->item_value)){
parser->active_record = 0;
}
}
break;
case Local:
case Reserved:
break;
default:
btstack_assert(false);
break;
}
if (!valid_field) return;
// verify record id
if (parser->global_report_id && !parser->active_record){
if (parser->report[0] != parser->global_report_id){
return;
}
parser->report_pos_in_bit += 8u;
}
parser->active_record = 1;
// handle constant fields used for padding
if (item->item_value & 1){
int item_bits = parser->global_report_size * parser->global_report_count;
#ifdef HID_PARSER_PRETTY_PRINT
log_info("- Skip %u constant bits", item_bits);
#endif
parser->report_pos_in_bit += item_bits;
return;
}
// Empty Item
if (parser->global_report_count == 0u) return;
// let's start
parser->required_usages = parser->global_report_count;
}
// PUBLIC API
@ -487,21 +437,21 @@ bool btstack_hid_report_id_declared(const uint8_t *hid_descriptor, uint16_t hid_
// HID Descriptor Usage Iterator
static void btstack_parser_usage_iterator_process_item(btstack_hid_parser_t * parser, hid_descriptor_item_t * item){
hid_pretty_print_item(parser, item);
static void btstack_parser_usage_iterator_process_item(btstack_hid_usage_iterator_t * iterator, hid_descriptor_item_t * item){
hid_pretty_print_item(iterator, item);
int valid_field = 0;
uint16_t report_id_before;
switch ((TagType)item->item_type){
case Main:
valid_field = btstack_hid_main_item_tag_matches_report_type((MainItemTag) item->item_tag,
parser->report_type);
iterator->report_type);
break;
case Global:
report_id_before = parser->global_report_id;
btstack_hid_handle_global_item(parser, item);
report_id_before = iterator->global_report_id;
btstack_hid_handle_global_item(iterator, item);
// track record id for report handling, reset report position
if (report_id_before != parser->global_report_id){
parser->report_pos_in_bit = 8u;
if (report_id_before != iterator->global_report_id){
iterator->report_pos_in_bit = 8u;
}
break;
case Local:
@ -515,109 +465,109 @@ static void btstack_parser_usage_iterator_process_item(btstack_hid_parser_t * pa
// handle constant fields used for padding
if (item->item_value & 1){
int item_bits = parser->global_report_size * parser->global_report_count;
int item_bits = iterator->global_report_size * iterator->global_report_count;
#ifdef HID_PARSER_PRETTY_PRINT
log_info("- Skip %u constant bits", item_bits);
#endif
parser->report_pos_in_bit += item_bits;
iterator->report_pos_in_bit += item_bits;
return;
}
// Empty Item
if (parser->global_report_count == 0u) return;
if (iterator->global_report_count == 0u) return;
// let's start
parser->required_usages = parser->global_report_count;
iterator->required_usages = iterator->global_report_count;
}
static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_parser_t * parser) {
while (btstack_hid_descriptor_iterator_has_more(&parser->descriptor_iterator)){
parser->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&parser->descriptor_iterator);
static void btstack_hid_usage_iterator_find_next_usage(btstack_hid_usage_iterator_t * iterator) {
while (btstack_hid_descriptor_iterator_has_more(&iterator->descriptor_iterator)){
iterator->descriptor_item = * btstack_hid_descriptor_iterator_get_item(&iterator->descriptor_iterator);
btstack_parser_usage_iterator_process_item(parser, &parser->descriptor_item);
btstack_parser_usage_iterator_process_item(iterator, &iterator->descriptor_item);
if (parser->required_usages){
hid_find_next_usage(parser);
if (parser->available_usages) {
parser->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
if (iterator->required_usages){
hid_find_next_usage(iterator);
if (iterator->available_usages) {
iterator->state = BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
return;
} else {
log_debug("no usages found");
parser->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
return;
}
} else {
if ((TagType) (&parser->descriptor_item)->item_type == Main) {
if ((TagType) (&iterator->descriptor_item)->item_type == Main) {
// reset usage
parser->usage_pos = parser->descriptor_iterator.descriptor_pos;
parser->usage_page = parser->global_usage_page;
iterator->usage_pos = iterator->descriptor_iterator.descriptor_pos;
iterator->usage_page = iterator->global_usage_page;
}
}
}
// end of descriptor
parser->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
}
void btstack_hid_usage_iterator_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){
memset(parser, 0, sizeof(btstack_hid_parser_t));
void btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type){
memset(iterator, 0, sizeof(btstack_hid_usage_iterator_t));
parser->descriptor = hid_descriptor;
parser->descriptor_len = hid_descriptor_len;
parser->report_type = hid_report_type;
parser->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
parser->global_report_id = HID_REPORT_ID_UNDEFINED;
btstack_hid_descriptor_iterator_init(&parser->descriptor_iterator, hid_descriptor, hid_descriptor_len);
iterator->descriptor = hid_descriptor;
iterator->descriptor_len = hid_descriptor_len;
iterator->report_type = hid_report_type;
iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
iterator->global_report_id = HID_REPORT_ID_UNDEFINED;
btstack_hid_descriptor_iterator_init(&iterator->descriptor_iterator, hid_descriptor, hid_descriptor_len);
}
bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser){
while (parser->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){
btstack_hid_usage_iterator_find_next_usage(parser);
bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator){
while (iterator->state == BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM){
btstack_hid_usage_iterator_find_next_usage(iterator);
}
return parser->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
return iterator->state == BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE;
}
void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item){
void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item){
// cache current values
memset(item, 0, sizeof(btstack_hid_usage_item_t));
item->size = parser->global_report_size;
item->report_id = parser->global_report_id;
item->usage_page = parser->usage_minimum >> 16;
item->bit_pos = parser->report_pos_in_bit;
item->size = iterator->global_report_size;
item->report_id = iterator->global_report_id;
item->usage_page = iterator->usage_minimum >> 16;
item->bit_pos = iterator->report_pos_in_bit;
bool is_variable = (parser->descriptor_item.item_value & 2) != 0;
bool is_variable = (iterator->descriptor_item.item_value & 2) != 0;
if (is_variable){
item->usage = parser->usage_minimum & 0xffffu;
item->usage = iterator->usage_minimum & 0xffffu;
}
parser->required_usages--;
parser->report_pos_in_bit += parser->global_report_size;
iterator->required_usages--;
iterator->report_pos_in_bit += iterator->global_report_size;
// cache descriptor item and
item->descriptor_item = parser->descriptor_item;
item->global_logical_minimum = parser->global_logical_minimum;
item->descriptor_item = iterator->descriptor_item;
item->global_logical_minimum = iterator->global_logical_minimum;
// next usage
if (is_variable){
parser->usage_minimum++;
parser->available_usages--;
if (parser->usage_range && (parser->usage_minimum > parser->usage_maximum)){
iterator->usage_minimum++;
iterator->available_usages--;
if (iterator->usage_range && (iterator->usage_minimum > iterator->usage_maximum)){
// usage min - max range smaller than report count, ignore remaining bit in report
log_debug("Ignoring %u items without Usage", parser->required_usages);
parser->report_pos_in_bit += parser->global_report_size * parser->required_usages;
parser->required_usages = 0;
iterator->report_pos_in_bit += iterator->global_report_size * iterator->required_usages;
iterator->required_usages = 0;
}
} else {
if (parser->required_usages == 0u){
parser->available_usages = 0;
if (iterator->required_usages == 0u){
iterator->available_usages = 0;
}
}
if (parser->available_usages) {
if (iterator->available_usages) {
return;
}
if (parser->required_usages == 0u){
parser->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
if (iterator->required_usages == 0u){
iterator->state = BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM;
} else {
hid_find_next_usage(parser);
if (parser->available_usages == 0u) {
parser->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
hid_find_next_usage(iterator);
if (iterator->available_usages == 0u) {
iterator->state = BTSTACK_HID_USAGE_ITERATOR_PARSER_COMPLETE;
}
}
}
@ -626,7 +576,7 @@ void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_
// HID Report Parser
void btstack_hid_parser_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type, const uint8_t * hid_report, uint16_t hid_report_len){
btstack_hid_usage_iterator_init(parser, hid_descriptor, hid_descriptor_len, hid_report_type);
btstack_hid_usage_iterator_init(&parser->usage_iterator, hid_descriptor, hid_descriptor_len, hid_report_type);
parser->report = hid_report;
parser->report_len = hid_report_len;
parser->have_report_usage_ready = false;
@ -637,8 +587,8 @@ void btstack_hid_parser_init(btstack_hid_parser_t * parser, const uint8_t * hid_
* @param parser
*/
bool btstack_hid_parser_has_more(btstack_hid_parser_t * parser){
while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(parser)){
btstack_hid_usage_iterator_get_item(parser, &parser->descriptor__usage_item);
while ((parser->have_report_usage_ready == false) && btstack_hid_usage_iterator_has_more(&parser->usage_iterator)){
btstack_hid_usage_iterator_get_item(&parser->usage_iterator, &parser->descriptor__usage_item);
// ignore usages for other report ids
if (parser->descriptor__usage_item.report_id != HID_REPORT_ID_UNDEFINED){
if (parser->descriptor__usage_item.report_id != parser->report[0]){

View File

@ -115,6 +115,7 @@ typedef struct {
hid_descriptor_item_t descriptor_item;
} btstack_hid_descriptor_iterator_t;
typedef enum {
BTSTACK_HID_USAGE_ITERATOR_STATE_SCAN_FOR_REPORT_ITEM,
BTSTACK_HID_USAGE_ITERATOR_USAGES_AVAILABLE,
@ -122,33 +123,17 @@ typedef enum {
} btstack_hid_usage_iterator_state_t;
typedef struct {
uint16_t report_id; // 8-bit report ID or 0xffff if not set
uint16_t bit_pos; // position in bit
uint16_t usage_page;
uint16_t usage;
uint8_t size; // in bit
// cached values of relevant descriptor item (input,output,feature)
hid_descriptor_item_t descriptor_item;
int32_t global_logical_minimum;
} btstack_hid_usage_item_t;
typedef struct {
// Descriptor
const uint8_t * descriptor;
uint16_t descriptor_len;
btstack_hid_descriptor_iterator_t descriptor_iterator;
// Report
hid_report_type_t report_type;
const uint8_t * report;
uint16_t report_len;
// State
btstack_hid_usage_iterator_state_t state;
hid_descriptor_item_t descriptor_item;
hid_report_type_t report_type;
uint16_t report_pos_in_bit;
// usage pos and usage_page after last main item, used to find next usage
@ -158,7 +143,7 @@ typedef struct {
// usage generator
uint32_t usage_minimum;
uint32_t usage_maximum;
uint16_t available_usages;
uint16_t available_usages;
uint16_t required_usages;
bool usage_range;
uint8_t active_record;
@ -166,14 +151,35 @@ typedef struct {
// global
int32_t global_logical_minimum;
int32_t global_logical_maximum;
uint16_t global_usage_page;
uint16_t global_usage_page;
uint8_t global_report_size;
uint8_t global_report_count;
uint16_t global_report_id;
// for HID Parser
} btstack_hid_usage_iterator_t;
typedef struct {
uint16_t report_id; // 8-bit report ID or 0xffff if not set
uint16_t bit_pos; // position in bit
uint16_t usage_page;
uint16_t usage;
uint8_t size; // in bit
// cached values of relevant descriptor item (input,output,feature)
hid_descriptor_item_t descriptor_item;
int32_t global_logical_minimum;
} btstack_hid_usage_item_t;
typedef struct {
btstack_hid_usage_iterator_t usage_iterator;
const uint8_t * report;
uint16_t report_len;
bool have_report_usage_ready;
btstack_hid_usage_item_t descriptor__usage_item;
} btstack_hid_parser_t;
@ -220,20 +226,20 @@ bool btstack_hid_descriptor_iterator_valid(btstack_hid_descriptor_iterator_t * i
* @param hid_descriptor_len
* @param hid_report_type
*/
void btstack_hid_usage_iterator_init(btstack_hid_parser_t * parser, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type);
void btstack_hid_usage_iterator_init(btstack_hid_usage_iterator_t * iterator, const uint8_t * hid_descriptor, uint16_t hid_descriptor_len, hid_report_type_t hid_report_type);
/**
* @brief Checks if more usages are available
* @param parser
*/
bool btstack_hid_usage_iterator_has_more(btstack_hid_parser_t * parser);
bool btstack_hid_usage_iterator_has_more(btstack_hid_usage_iterator_t * iterator);
/**
* @brief Get current usage item
* @param parser
* @param item
*/
void btstack_hid_usage_iterator_get_item(btstack_hid_parser_t * parser, btstack_hid_usage_item_t * item);
void btstack_hid_usage_iterator_get_item(btstack_hid_usage_iterator_t * iterator, btstack_hid_usage_item_t * item);
// HID Report Iterator

View File

@ -584,11 +584,11 @@ TEST(HID, GetReportSize){
TEST(HID, UsageIteratorBootKeyboard){
const uint8_t * hid_descriptor = hid_descriptor_keyboard_boot_mode;
uint16_t hid_descriptor_len = sizeof(hid_descriptor_keyboard_boot_mode);
btstack_hid_parser_t parser;
btstack_hid_usage_iterator_init(&parser, hid_descriptor, hid_descriptor_len, HID_REPORT_TYPE_INPUT);
while (btstack_hid_usage_iterator_has_more(&parser)){
btstack_hid_usage_iterator_t iterator;
btstack_hid_usage_iterator_init(&iterator, hid_descriptor, hid_descriptor_len, HID_REPORT_TYPE_INPUT);
while (btstack_hid_usage_iterator_has_more(&iterator)){
btstack_hid_usage_item_t item;
btstack_hid_usage_iterator_get_item(&parser, &item);
btstack_hid_usage_iterator_get_item(&iterator, &item);
// printf("Report ID 0x%04x, bitpos %3u, usage page 0x%04x, usage 0x%04x, size %u\n", item.report_id, item.bit_pos, item.usage_page, item.usage, item.size);
}
}
@ -596,11 +596,11 @@ TEST(HID, UsageIteratorBootKeyboard){
TEST(HID, UsageIteratorCombo1){
const uint8_t * hid_descriptor = combo_descriptor_with_report_ids;
uint16_t hid_descriptor_len = sizeof(combo_descriptor_with_report_ids);
btstack_hid_parser_t parser;
btstack_hid_usage_iterator_init(&parser, hid_descriptor, hid_descriptor_len, HID_REPORT_TYPE_INPUT);
while (btstack_hid_usage_iterator_has_more(&parser)){
btstack_hid_usage_iterator_t iterator;
btstack_hid_usage_iterator_init(&iterator, hid_descriptor, hid_descriptor_len, HID_REPORT_TYPE_INPUT);
while (btstack_hid_usage_iterator_has_more(&iterator)){
btstack_hid_usage_item_t item;
btstack_hid_usage_iterator_get_item(&parser, &item);
btstack_hid_usage_iterator_get_item(&iterator, &item);
// printf("Report ID 0x%04x, bitpos %3u, usage page 0x%04x, usage 0x%04x, size %u\n", item.report_id, item.bit_pos, item.usage_page, item.usage, item.size);
}
}