154 lines
4.2 KiB
Bash
Executable File
154 lines
4.2 KiB
Bash
Executable File
#!/bin/sh -e
|
|
# upscat-gen-usb.sh: convert tables from the USB HID specification to C code
|
|
#
|
|
# Copyright (c) 2026, Přemysl Eric Janouch <p@janouch.name>
|
|
# SPDX-License-Identifier: 0BSD
|
|
|
|
if [ ! -e hut1_7.pdf ]
|
|
then curl -LO https://www.usb.org/sites/default/files/hut1_7.pdf
|
|
fi
|
|
|
|
# Our program doesn't need all of them, so we're filtering them here.
|
|
process() (qpdf --show-attachment=HidUsageTables.json hut1_7.pdf \
|
|
| jq -r '.UsagePages[] | select(.Name | IN("Battery System", "Power")) |
|
|
'"$1"' | @tsv' | sort -n -k 1,1 -k 2,2 | awk -F'\t' '
|
|
function ident(s) { gsub(/[^[:alnum:]]/, "_", s); return s } '"{ $2 }")
|
|
|
|
pages='[.Id, .Name]'
|
|
usages='. as $page | $page.UsageIds[] |
|
|
[$page.Id, .Id, $page.Name, (.Name | gsub("\\\\_"; "_")),
|
|
(.Kinds | join("/"))]'
|
|
generated='. as $page | .UsageIdGenerator | select(.) |
|
|
[$page.Id, .StartUsageId, .EndUsageId, $page.Name, .NamePrefix,
|
|
(.Kinds | join("/"))]'
|
|
|
|
cat <<EOF >upscat-usb.h
|
|
// Code generated by $0. DO NOT EDIT.
|
|
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
enum {
|
|
USB_HID_COLLECTION_PHYSICAL = 0x00,
|
|
USB_HID_COLLECTION_APPLICATION = 0x01,
|
|
USB_HID_COLLECTION_LOGICAL = 0x02,
|
|
USB_HID_COLLECTION_REPORT = 0x03,
|
|
USB_HID_COLLECTION_NAMED_ARRAY = 0x04,
|
|
USB_HID_COLLECTION_USAGE_SWITCH = 0x05,
|
|
USB_HID_COLLECTION_USAGE_MODIFIER = 0x06,
|
|
};
|
|
|
|
enum {
|
|
$(process "$pages" '
|
|
printf("\t/// %s\n", $2)
|
|
printf("\tUSB_HID_USAGE_PAGE__%s = 0x%04x,\n", toupper(ident($2)), $1)
|
|
')
|
|
};
|
|
|
|
static const struct usb_hid_usage_page_info {
|
|
uint16_t usage_page;
|
|
const char *name;
|
|
} usb_hid_usage_pages[] = {
|
|
$(process "$pages" 'printf("\t{0x%04x, \"%s\"},\n", $1, $2)')
|
|
};
|
|
|
|
$(process "$usages" '
|
|
printf("/// %s: %s (%s)\n", $3, $4, $5)
|
|
printf("#define USB_HID_USAGE__%s__%s 0x%04x%04x\n",
|
|
toupper(ident($3)), toupper(ident($4)), $1, $2)
|
|
')
|
|
|
|
static const struct usb_hid_usage_info {
|
|
uint32_t usage;
|
|
const char *name;
|
|
} usb_hid_usages[] = {
|
|
$(process "$usages" 'printf("\t{0x%04x%04x, \"%s\"},\n", $1, $2, $4)')
|
|
};
|
|
|
|
static const struct usb_hid_usage_range_info {
|
|
uint32_t usage_start;
|
|
uint32_t usage_end;
|
|
const char *name_prefix;
|
|
} usb_hid_usages_ranged[] = {
|
|
$(process "$generated" '
|
|
printf("\t// %s (%s)\n", $4, $6)
|
|
printf("\t{0x%04x%04x, 0x%04x%04x, \"%s\"},\n", $1, $2, $1, $3, $5)
|
|
')
|
|
};
|
|
|
|
static int
|
|
usb_hid_usage_pages_cmp(const void *a, const void *b)
|
|
{
|
|
const uint16_t *key = a;
|
|
const struct usb_hid_usage_page_info *entry = b;
|
|
return (*key > entry->usage_page) - (*key < entry->usage_page);
|
|
}
|
|
|
|
static int
|
|
usb_hid_usages_cmp(const void *a, const void *b)
|
|
{
|
|
const uint32_t *key = a;
|
|
const struct usb_hid_usage_info *entry = b;
|
|
return (*key > entry->usage) - (*key < entry->usage);
|
|
}
|
|
|
|
static int
|
|
usb_hid_usages_ranged_cmp(const void *a, const void *b)
|
|
{
|
|
const uint32_t *key = a;
|
|
const struct usb_hid_usage_range_info *entry = b;
|
|
return (*key > entry->usage_end) - (*key < entry->usage_start);
|
|
}
|
|
|
|
static const char *
|
|
usb_hid_usage_page_to_string(uint16_t usage_page)
|
|
{
|
|
const struct usb_hid_usage_page_info *entry = bsearch(
|
|
&usage_page, usb_hid_usage_pages,
|
|
sizeof usb_hid_usage_pages / sizeof usb_hid_usage_pages[0],
|
|
sizeof usb_hid_usage_pages[0], usb_hid_usage_pages_cmp);
|
|
if (entry)
|
|
return entry->name;
|
|
|
|
static char buffer[32];
|
|
snprintf(buffer, sizeof buffer, "%04x", usage_page);
|
|
return buffer;
|
|
}
|
|
|
|
static const char *
|
|
usb_hid_usage_to_string(uint32_t usage)
|
|
{
|
|
const struct usb_hid_usage_info *info = bsearch(
|
|
&usage, usb_hid_usages,
|
|
sizeof usb_hid_usages / sizeof usb_hid_usages[0],
|
|
sizeof usb_hid_usages[0], usb_hid_usages_cmp);
|
|
if (info)
|
|
return info->name;
|
|
|
|
static char buffer[64];
|
|
const struct usb_hid_usage_range_info *range = bsearch(
|
|
&usage, usb_hid_usages_ranged,
|
|
sizeof usb_hid_usages_ranged / sizeof usb_hid_usages_ranged[0],
|
|
sizeof usb_hid_usages_ranged[0], usb_hid_usages_ranged_cmp);
|
|
if (range) {
|
|
snprintf(buffer, sizeof buffer,
|
|
"%s %u", range->name_prefix, (unsigned) (usage & 0xffff));
|
|
return buffer;
|
|
}
|
|
|
|
snprintf(buffer, sizeof buffer, "%04x", (unsigned) (usage & 0xffff));
|
|
return buffer;
|
|
}
|
|
|
|
static const char *
|
|
usb_hid_usage_to_string_full(uint32_t usage)
|
|
{
|
|
static char buffer[128];
|
|
snprintf(buffer, sizeof buffer, "%s: %s",
|
|
usb_hid_usage_page_to_string(usage >> 16),
|
|
usb_hid_usage_to_string(usage));
|
|
return buffer;
|
|
}
|
|
EOF
|