Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,410 @@
|
||||
#define LOG_MODULE "hid-generic"
|
||||
|
||||
#include <windows.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "geninput/hid-meta-in.h"
|
||||
#include "geninput/hid-report-in.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
static bool hid_meta_in_init_caps(struct hid_meta_in *meta,
|
||||
PHIDP_PREPARSED_DATA ppd);
|
||||
static void hid_meta_in_init_arrays(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_init_buttons(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_init_values(struct hid_meta_in *meta);
|
||||
static struct hid_report_in *hid_meta_in_lookup_report(
|
||||
struct hid_meta_in *meta, uint8_t report_id);
|
||||
static void hid_meta_in_fini_arrays(struct hid_meta_in *meta);
|
||||
static void hid_meta_in_fini_caps(struct hid_meta_in *meta);
|
||||
|
||||
bool hid_meta_in_init(struct hid_meta_in *meta, PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
if (!hid_meta_in_init_caps(meta, ppd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
hid_meta_in_init_arrays(meta);
|
||||
hid_meta_in_init_buttons(meta);
|
||||
hid_meta_in_init_values(meta);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool hid_meta_in_init_caps(struct hid_meta_in *meta,
|
||||
PHIDP_PREPARSED_DATA ppd)
|
||||
{
|
||||
uint16_t len;
|
||||
NTSTATUS status;
|
||||
|
||||
meta->ppd = ppd;
|
||||
|
||||
status = HidP_GetCaps(meta->ppd, &meta->caps_tlc);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting top-level collection caps");
|
||||
|
||||
goto caps_tlc_fail;
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberInputButtonCaps;
|
||||
meta->caps_btn = xmalloc(sizeof(*meta->caps_btn) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetButtonCaps(HidP_Input, meta->caps_btn, &len,meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting button caps");
|
||||
|
||||
goto caps_btn_fail;
|
||||
}
|
||||
}
|
||||
|
||||
len = meta->caps_tlc.NumberInputValueCaps;
|
||||
meta->caps_val = xmalloc(sizeof(*meta->caps_val) * len);
|
||||
|
||||
if (len > 0) {
|
||||
status = HidP_GetValueCaps(HidP_Input, meta->caps_val, &len, meta->ppd);
|
||||
|
||||
if (status != HIDP_STATUS_SUCCESS) {
|
||||
log_warning("Error getting value caps");
|
||||
|
||||
goto caps_val_fail;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
caps_val_fail:
|
||||
free(meta->caps_val);
|
||||
|
||||
caps_btn_fail:
|
||||
free(meta->caps_btn);
|
||||
|
||||
caps_tlc_fail:
|
||||
return false;
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_arrays(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
uint16_t *report_btns;
|
||||
bool *report_presence;
|
||||
uint16_t count;
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
void *report_buf;
|
||||
|
||||
/* Init counters */
|
||||
|
||||
meta->ncontrols = 0;
|
||||
meta->nbuttons = 0;
|
||||
meta->nreports = 0;
|
||||
|
||||
meta->reports_muxed = false;
|
||||
|
||||
/* We have extremely limited stack headroom when called from IIDX for some
|
||||
reason, gotta do this on the heap... */
|
||||
|
||||
report_btns = xcalloc(sizeof(*report_btns) * 0x100);
|
||||
report_presence = xcalloc(sizeof(*report_presence) * 0x100);
|
||||
|
||||
/* Count up buttons, globally and per-report */
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
count = bc->Range.UsageMax - bc->Range.UsageMin + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
if (bc->ReportID != 0) {
|
||||
meta->reports_muxed = true;
|
||||
}
|
||||
|
||||
report_btns[bc->ReportID] += count;
|
||||
|
||||
meta->ncontrols += count;
|
||||
meta->nbuttons += count;
|
||||
}
|
||||
|
||||
/* Count up values, note all reports that have values */
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
count = vc->Range.UsageMax - vc->Range.UsageMin + 1;
|
||||
} else {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
if (vc->ReportID != 0) {
|
||||
meta->reports_muxed = true;
|
||||
}
|
||||
|
||||
report_presence[vc->ReportID] = true;
|
||||
|
||||
meta->ncontrols += count;
|
||||
}
|
||||
|
||||
/* Count up total number of reports and initialize them */
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_btns[i] || report_presence[i]) {
|
||||
meta->nreports++;
|
||||
}
|
||||
}
|
||||
|
||||
j = 0;
|
||||
meta->reports = xmalloc(sizeof(*meta->reports) * meta->nreports);
|
||||
|
||||
for (i = 0 ; i < 0x100 ; i++) {
|
||||
if (report_btns[i] || report_presence[i]) {
|
||||
report_buf = xmalloc(meta->caps_tlc.InputReportByteLength);
|
||||
|
||||
hid_report_in_init(&meta->reports[j++], (uint8_t) i,
|
||||
report_btns[i], report_buf,
|
||||
meta->caps_tlc.InputReportByteLength, meta->ppd);
|
||||
}
|
||||
}
|
||||
|
||||
/* Init control arrays */
|
||||
|
||||
meta->controls = xmalloc(sizeof(*meta->controls) * meta->ncontrols);
|
||||
meta->priv_controls = xmalloc(sizeof(*meta->priv_controls)
|
||||
* meta->ncontrols);
|
||||
|
||||
/* Clean up our scratch space */
|
||||
|
||||
free(report_presence);
|
||||
free(report_btns);
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_buttons(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_BUTTON_CAPS *bc;
|
||||
struct hid_control_in *priv;
|
||||
struct hid_control *ctl;
|
||||
unsigned int pos;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
pos = 0;
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputButtonCaps ; i++) {
|
||||
bc = &meta->caps_btn[i];
|
||||
|
||||
if (bc->IsRange) {
|
||||
for (j = 0 ; j <= bc->Range.UsageMax - bc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (bc->UsagePage << 16) | (bc->Range.UsageMin + j);
|
||||
ctl->value_min = 0;
|
||||
ctl->value_max = 1;
|
||||
ctl->flags = 0;
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, bc->ReportID);
|
||||
priv->collection_id = bc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (bc->UsagePage << 16) | bc->NotRange.Usage;
|
||||
ctl->value_min = 0;
|
||||
ctl->value_max = 1;
|
||||
ctl->flags = 0;
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void hid_meta_in_init_values(struct hid_meta_in *meta)
|
||||
{
|
||||
const HIDP_VALUE_CAPS *vc;
|
||||
struct hid_control_in *priv;
|
||||
struct hid_control *ctl;
|
||||
unsigned int pos;
|
||||
unsigned int i;
|
||||
int j;
|
||||
|
||||
pos = meta->nbuttons;
|
||||
|
||||
for (i = 0 ; i < meta->caps_tlc.NumberInputValueCaps ; i++) {
|
||||
vc = &meta->caps_val[i];
|
||||
|
||||
if (vc->IsRange) {
|
||||
for (j = 0 ; j <= vc->Range.UsageMax - vc->Range.UsageMin ; j++) {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (vc->UsagePage << 16) | (vc->Range.UsageMin + i);
|
||||
ctl->value_min = vc->LogicalMin;
|
||||
ctl->value_max = vc->LogicalMax;
|
||||
ctl->flags = 0;
|
||||
|
||||
if (vc->HasNull) {
|
||||
ctl->flags |= HID_FLAG_NULLABLE;
|
||||
}
|
||||
|
||||
if (!vc->IsAbsolute) {
|
||||
ctl->flags |= HID_FLAG_RELATIVE;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
} else {
|
||||
priv = &meta->priv_controls[pos];
|
||||
|
||||
priv->report = hid_meta_in_lookup_report(meta, vc->ReportID);
|
||||
priv->collection_id = vc->LinkCollection;
|
||||
|
||||
ctl = &meta->controls[pos];
|
||||
|
||||
ctl->usage = (vc->UsagePage << 16) | vc->NotRange.Usage;
|
||||
ctl->value_min = vc->LogicalMin;
|
||||
ctl->value_max = vc->LogicalMax;
|
||||
ctl->flags = 0;
|
||||
|
||||
if (vc->HasNull) {
|
||||
ctl->flags |= HID_FLAG_NULLABLE;
|
||||
}
|
||||
|
||||
if (!vc->IsAbsolute) {
|
||||
ctl->flags |= HID_FLAG_RELATIVE;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct hid_report_in *hid_meta_in_lookup_report(
|
||||
struct hid_meta_in *meta, uint8_t report_id)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
if (meta->reports[i].id == report_id) {
|
||||
return &meta->reports[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool hid_meta_in_dispatch(struct hid_meta_in *meta, uint8_t **bytes,
|
||||
size_t nbytes)
|
||||
{
|
||||
struct hid_report_in *r;
|
||||
uint8_t report_id;
|
||||
|
||||
if (meta->reports_muxed) {
|
||||
report_id = **bytes;
|
||||
} else {
|
||||
report_id = 0;
|
||||
}
|
||||
|
||||
r = hid_meta_in_lookup_report(meta, report_id);
|
||||
|
||||
if (r == NULL) {
|
||||
log_warning("Got spurious report with ID %02x", report_id);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return hid_report_in_exchange(r, bytes, nbytes);
|
||||
}
|
||||
|
||||
size_t hid_meta_in_get_buffer_size(const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->caps_tlc.InputReportByteLength;
|
||||
}
|
||||
|
||||
const struct hid_control *hid_meta_in_get_controls(
|
||||
const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->controls;
|
||||
}
|
||||
|
||||
size_t hid_meta_in_get_ncontrols(const struct hid_meta_in *meta)
|
||||
{
|
||||
return meta->ncontrols;
|
||||
}
|
||||
|
||||
uint32_t hid_meta_in_get_tlc_usage(const struct hid_meta_in *meta)
|
||||
{
|
||||
return (meta->caps_tlc.UsagePage << 16) | meta->caps_tlc.Usage;
|
||||
}
|
||||
|
||||
bool hid_meta_in_get_value(struct hid_meta_in *meta,
|
||||
size_t control_no, int32_t *value)
|
||||
{
|
||||
struct hid_control_in *priv;
|
||||
uint32_t usage;
|
||||
|
||||
if (control_no >= meta->ncontrols) {
|
||||
return false;
|
||||
}
|
||||
|
||||
priv = &meta->priv_controls[control_no];
|
||||
usage = meta->controls[control_no].usage;
|
||||
|
||||
if (control_no < meta->nbuttons) {
|
||||
return hid_report_in_get_bit(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, value);
|
||||
} else {
|
||||
return hid_report_in_get_value(priv->report, meta->ppd,
|
||||
priv->collection_id, usage, value);
|
||||
}
|
||||
}
|
||||
|
||||
void hid_meta_in_fini(struct hid_meta_in *meta)
|
||||
{
|
||||
hid_meta_in_fini_arrays(meta);
|
||||
hid_meta_in_fini_caps(meta);
|
||||
}
|
||||
|
||||
static void hid_meta_in_fini_arrays(struct hid_meta_in *meta)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
free(meta->priv_controls);
|
||||
free(meta->controls);
|
||||
|
||||
for (i = 0 ; i < meta->nreports ; i++) {
|
||||
hid_report_in_fini(&meta->reports[i]);
|
||||
}
|
||||
|
||||
free(meta->reports);
|
||||
}
|
||||
|
||||
static void hid_meta_in_fini_caps(struct hid_meta_in *meta)
|
||||
{
|
||||
free(meta->caps_val);
|
||||
free(meta->caps_btn);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user