Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+32
View File
@@ -0,0 +1,32 @@
dlls += geninput
ldflags_geninput := \
-lhid \
-lsetupapi \
libs_geninput := \
util \
src_geninput := \
dev-list.c \
guid.c \
hid.c \
hid-generic.c \
hid-generic-strings.c \
hid-meta-in.c \
hid-meta-out.c \
hid-mgr.c \
hid-report-in.c \
hid-report-out.c \
hotplug.c \
input.c \
io-thread.c \
kbd.c \
kbd-data.c \
mapper.c \
mapper-s11n.c \
mouse.c \
msg-thread.c \
pacdrive.c \
ri.c \
+173
View File
@@ -0,0 +1,173 @@
#include <windows.h>
#include <setupapi.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wchar.h>
#include "geninput/dev-list.h"
#include "util/log.h"
#include "util/mem.h"
void dev_list_init(struct dev_list *devs, const GUID *class_guid)
{
devs->class_guid = (GUID *) class_guid;
devs->infolist = SetupDiGetClassDevs(devs->class_guid, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (devs->infolist == NULL) {
log_fatal("SetupDiGetClassDevs failed: %08x",
(unsigned int) GetLastError());
}
devs->dev_no = 0;
devs->iface_no = 0;
devs->detail = NULL;
devs->name = NULL;
}
bool dev_list_next(struct dev_list *devs)
{
if (devs->detail != NULL) {
free(devs->detail);
devs->detail = NULL;
}
if (devs->name != NULL) {
free(devs->name);
devs->name = NULL;
}
for (;;) {
devs->dev.cbSize = sizeof(devs->dev);
if (!SetupDiEnumDeviceInfo(devs->infolist, devs->dev_no, &devs->dev)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
return false;
} else {
log_fatal("SetupDiEnumDeviceInfo failed: %08x",
(unsigned int) GetLastError());
}
}
devs->iface.cbSize = sizeof(devs->iface);
if (!SetupDiEnumDeviceInterfaces(devs->infolist, &devs->dev,
devs->class_guid, devs->iface_no, &devs->iface)) {
if (GetLastError() == ERROR_NO_MORE_ITEMS) {
devs->dev_no++;
devs->iface_no = 0;
continue;
} else {
log_fatal("SetupDiEnumDeviceInterfaces failed: %08x",
(unsigned int) GetLastError());
}
}
devs->iface_no++;
return true;
}
}
const char *dev_list_get_dev_node(struct dev_list *devs)
{
DWORD detail_size;
if (devs->detail != NULL) {
return devs->detail->DevicePath;
}
if (SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface, NULL, 0,
&detail_size, NULL)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
log_fatal("SetupDiGetDeviceInterfaceDetail sizing failed: %08x",
(unsigned int) GetLastError());
}
devs->detail = xmalloc(detail_size);
devs->detail->cbSize = sizeof(*devs->detail);
if (!SetupDiGetDeviceInterfaceDetail(devs->infolist, &devs->iface,
devs->detail, detail_size, NULL, NULL)) {
log_fatal("SetupDiGetDeviceInterfaceDetail content failed: %08x",
(unsigned int) GetLastError());
}
return devs->detail->DevicePath;
}
const wchar_t *dev_list_get_dev_name(struct dev_list *devs)
{
DWORD name_size;
const char* dev_path;
// largest possible product string according to WinAPI docs
wchar_t product_string[126];
DWORD product_size;
bool product_success;
if (devs->name != NULL) {
return devs->name;
}
if (SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
SPDRP_DEVICEDESC, NULL, NULL, 0, &name_size)
|| GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) sizing "
"failed: %08x", (unsigned int) GetLastError());
}
/* This function returns arbitrary data, not just strings. Therefore, we
are actually working in units of bytes and not wchar_t's here for once */
devs->name = xmalloc(name_size);
if (!SetupDiGetDeviceRegistryPropertyW(devs->infolist, &devs->dev,
SPDRP_DEVICEDESC, NULL, (void *) devs->name, name_size, 0)) {
log_fatal("SetupDiGetDeviceRegistryPropertyW(SPDRP_DEVICEDESC) content "
"failed: %08x", (unsigned int) GetLastError());
}
/* Also, try and get the product string - this is usually far more human
readable than the registry value, which is often "HID Compliant Thing".
We then append this in parentheses */
dev_path = dev_list_get_dev_node(devs);
HANDLE hid_hnd = CreateFile(dev_path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(hid_hnd) {
product_success = HidD_GetProductString(hid_hnd, product_string, sizeof(product_string));
CloseHandle(hid_hnd);
if(product_success && wcslen(product_string) > 0 && wcscmp(product_string, devs->name)) {
// len + null + 2*parentheses + space separator
product_size = (wcslen(product_string)+4) * sizeof(wchar_t);
devs->name = xrealloc(devs->name, name_size + product_size);
wcscat(devs->name, L" (");
wcscat(devs->name, product_string);
wcscat(devs->name, L")");
}
}
return devs->name;
}
void dev_list_fini(struct dev_list *devs)
{
if (devs->detail != NULL) {
free(devs->detail);
}
if (devs->name != NULL) {
free(devs->name);
}
if (!SetupDiDestroyDeviceInfoList(devs->infolist)) {
log_fatal("SetupDiDestroyDeviceList failed: %08x",
(unsigned int) GetLastError());
}
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef GENINPUT_DEV_LIST_H
#define GENINPUT_DEV_LIST_H
#include <windows.h>
#include <setupapi.h>
#include <stdbool.h>
#include <wchar.h>
struct dev_list {
GUID *class_guid;
HDEVINFO infolist;
SP_DEVINFO_DATA dev;
SP_DEVICE_INTERFACE_DATA iface;
SP_DEVICE_INTERFACE_DETAIL_DATA *detail;
wchar_t *name;
unsigned int dev_no;
unsigned int iface_no;
};
void dev_list_init(struct dev_list *devs, const GUID *class_guid);
bool dev_list_next(struct dev_list *devs);
const char *dev_list_get_dev_node(struct dev_list *devs);
const wchar_t *dev_list_get_dev_name(struct dev_list *devs);
void dev_list_fini(struct dev_list *devs);
#endif
+50
View File
@@ -0,0 +1,50 @@
LIBRARY geninput
EXPORTS
action_iter_get_mapping
action_iter_get_action
action_iter_get_page
action_iter_is_valid
action_iter_next
action_iter_free
hid_mgr_get_first_stub
hid_mgr_get_named_stub
hid_mgr_get_next_stub
hid_mgr_lock
hid_mgr_unlock
hid_stub_get_name
hid_stub_get_device_usage
hid_stub_get_controls
hid_stub_get_dev_node
hid_stub_get_lights
hid_stub_get_value
hid_stub_is_attached
hid_stub_set_light
input_fini
input_init
input_set_loggers
light_iter_get_mapping
light_iter_get_game_light
light_iter_is_valid
light_iter_next
light_iter_free
mapper_config_load
mapper_config_save
mapper_clear_action_map
mapper_clear_light_map
mapper_get_action_map
mapper_get_analog_map
mapper_get_analog_sensitivity
mapper_get_npages
mapper_is_analog_absolute
mapper_iterate_lights
mapper_iterate_actions
mapper_set_action_map
mapper_set_analog_map
mapper_set_analog_sensitivity
mapper_set_light_map
mapper_set_nanalogs
mapper_set_nlights
mapper_read_analog
mapper_update
mapper_write_light
+4
View File
@@ -0,0 +1,4 @@
#include <windows.h>
#include <initguid.h>
#include "geninput/hid.h"
+49
View File
@@ -0,0 +1,49 @@
#include <windows.h>
#include <hidsdi.h>
#include <stdlib.h>
#include "geninput/hid-generic-strings.h"
#include "util/defs.h"
#include "util/str.h"
void hid_generic_strings_init(struct hid_generic_strings *strings, HANDLE fd)
{
wchar_t wstr[128];
wstr_cpy(wstr, lengthof(wstr), L"???");
HidD_GetProductString(fd, wstr, sizeof(wstr));
strings->wstr_prod = wstr_dup(wstr);
if (!wstr_narrow(wstr, &strings->str_prod)) {
strings->str_prod = str_dup("???");
}
wstr_cpy(wstr, lengthof(wstr), L"???");
HidD_GetManufacturerString(fd, wstr, sizeof(wstr));
strings->wstr_manf = wstr_dup(wstr);
if (!wstr_narrow(wstr, &strings->str_manf)) {
strings->str_manf = str_dup("???");
}
}
const char *hid_generic_strings_get_manf(struct hid_generic_strings *strings)
{
return strings->str_manf;
}
const char *hid_generic_strings_get_prod(struct hid_generic_strings *strings)
{
return strings->str_prod;
}
void hid_generic_strings_fini(struct hid_generic_strings *strings)
{
free(strings->str_manf);
free(strings->wstr_manf);
free(strings->str_prod);
free(strings->wstr_prod);
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef GENINPUT_HID_GENERIC_STRINGS_H
#define GENINPUT_HID_GENERIC_STRINGS_H
struct hid_generic_strings {
wchar_t *wstr_prod;
wchar_t *wstr_manf;
char *str_prod;
char *str_manf;
};
void hid_generic_strings_init(struct hid_generic_strings *strings, HANDLE fd);
const char *hid_generic_strings_get_manf(struct hid_generic_strings *strings);
const char *hid_generic_strings_get_prod(struct hid_generic_strings *strings);
void hid_generic_strings_fini(struct hid_generic_strings *strings);
#endif
+408
View File
@@ -0,0 +1,408 @@
#define LOG_MODULE "hid-generic"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <windows.h>
#include <hidsdi.h>
#include "geninput/hid.h"
#include "geninput/hid-generic.h"
#include "geninput/hid-generic-strings.h"
#include "geninput/hid-meta-in.h"
#include "geninput/hid-meta-out.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
struct hid_generic {
struct hid_fd super;
char *dev_node;
HANDLE fd;
PHIDP_PREPARSED_DATA ppd;
OVERLAPPED in_ovl;
OVERLAPPED out_ovl;
struct hid_generic_strings strings;
struct hid_meta_in meta_in;
struct hid_meta_out meta_out;
uint8_t *in_buf;
size_t in_nbytes;
uint8_t *out_buf;
size_t out_nbytes;
bool out_started;
bool out_faulty;
};
static bool hid_generic_get_device_usage(const struct hid *hid,
uint32_t *usage);
static bool hid_generic_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars);
static bool hid_generic_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols);
static bool hid_generic_get_lights(const struct hid *hid,
struct hid_light *lights, size_t *nlights);
static bool hid_generic_get_value(struct hid *hid, size_t control_no,
int32_t *out_value);
static bool hid_generic_set_light(struct hid *hid, size_t light_no,
uint32_t intensity);
static bool hid_generic_handle_event(struct hid_fd *super, OVERLAPPED *ovl,
size_t nbytes);
static void hid_generic_close(struct hid *hid);
static struct hid_fd_vtbl hid_generic_vtbl = {{
/* .close = */ hid_generic_close,
/* .get_device_usage = */ hid_generic_get_device_usage,
/* .get_name = */ hid_generic_get_name,
/* .get_controls = */ hid_generic_get_controls,
/* .get_lights = */ hid_generic_get_lights,
/* .get_value = */ hid_generic_get_value,
/* .set_light = */ hid_generic_set_light },
/* .handle_event = */ hid_generic_handle_event
};
bool hid_generic_open(struct hid_fd **out, const char *dev_node, HANDLE iocp,
uintptr_t iocp_ctx)
{
struct hid_generic *hg;
uint32_t tlc_usage;
hg = xmalloc(sizeof(*hg));
hg->fd = CreateFile(dev_node, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hg->fd == INVALID_HANDLE_VALUE) {
/* Probably a keyboard that's locked by the OS */
goto open_fail;
}
if (!HidD_GetPreparsedData(hg->fd, &hg->ppd)) {
log_warning("HidD_GetPreparsedData failed on dev node %s", dev_node);
goto ppd_fail;
}
if (!hid_meta_in_init(&hg->meta_in, hg->ppd)) {
goto meta_in_fail;
}
tlc_usage = hid_meta_in_get_tlc_usage(&hg->meta_in);
if ((tlc_usage >> 24) == 0xFF) {
log_misc("Skipping vendor-specific HID on dev node %s", dev_node);
goto vsp_fail;
}
if (!hid_meta_out_init(&hg->meta_out, hg->ppd, hg->fd)) {
goto meta_out_fail;
}
hid_generic_strings_init(&hg->strings, hg->fd);
/* Dump diagnostics */
log_misc("Initializing generic HID on dev node %s:", dev_node);
log_misc("... Product: %s", hg->strings.str_prod);
log_misc("... Manufacturer: %s", hg->strings.str_manf);
log_misc("... Device usage: %08x",
hid_meta_in_get_tlc_usage(&hg->meta_in));
log_misc("... Number of controls: %u",
(unsigned int) hid_meta_in_get_ncontrols(&hg->meta_in));
log_misc("... Number of \"lights\": %u",
(unsigned int) hid_meta_out_get_nlights(&hg->meta_out));
/* Start up input double-buffer chain by reading a report.
We bind to the IO completion port here. */
CreateIoCompletionPort(hg->fd, iocp, iocp_ctx, 0);
hg->in_nbytes = hid_meta_in_get_buffer_size(&hg->meta_in);
hg->in_buf = xmalloc(hg->in_nbytes);
memset(&hg->in_ovl, 0, sizeof(hg->in_ovl));
if (!ReadFile(hg->fd, hg->in_buf, hg->in_nbytes, NULL,
&hg->in_ovl)) {
if (GetLastError() != ERROR_IO_PENDING) {
log_warning("Initial ReadFile failed: %08x: %s",
(unsigned int) GetLastError(), dev_node);
goto read_fail;
}
}
/* Allocate OUT report buffers, but don't start sending the reports until
the user tries to set a light. Some HIDs (e.g. Razer devices) freak out
if you send them HID reports that they aren't expecting.
These OUT reports are probably used for configuration settings or,
worse yet, firmware updates. Configuration changes are what feature
reports are for. As for firmware updates over HID... no. Just, no. */
hg->out_nbytes = hid_meta_out_get_buffer_size(&hg->meta_out);
hg->out_buf = xmalloc(hg->out_nbytes);
hg->out_started = false;
memset(&hg->out_ovl, 0, sizeof(hg->out_ovl));
/* (finish up and return success) */
hg->dev_node = str_dup(dev_node);
hg->super.vptr = &hid_generic_vtbl;
*out = &hg->super;
return true;
read_fail:
free(hg->in_buf);
hid_generic_strings_fini(&hg->strings);
hid_meta_out_fini(&hg->meta_out);
meta_out_fail:
vsp_fail:
hid_meta_in_fini(&hg->meta_in);
meta_in_fail:
HidD_FreePreparsedData(hg->ppd);
ppd_fail:
CloseHandle(hg->fd);
open_fail:
free(hg);
return false;
}
static bool hid_generic_get_device_usage(const struct hid *hid,
uint32_t *usage)
{
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
*usage = hid_meta_in_get_tlc_usage(&hg->meta_in);
return true;
}
static bool hid_generic_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars)
{
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
size_t len;
log_assert(nchars != NULL);
len = wcslen(hg->strings.wstr_prod) + 1;
if (chars == NULL) {
*nchars = len;
return true;
} else if (*nchars >= len) {
memcpy(chars, hg->strings.wstr_prod, len * sizeof(wchar_t));
return true;
} else {
return false;
}
}
static bool hid_generic_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols)
{
const struct hid_generic *hg = containerof(hid, struct hid_generic, super);
const struct hid_control *meta_controls;
size_t meta_ncontrols;
if (ncontrols == NULL) {
return false;
}
meta_controls = hid_meta_in_get_controls(&hg->meta_in);
meta_ncontrols = hid_meta_in_get_ncontrols(&hg->meta_in);
if (controls == NULL) {
*ncontrols = meta_ncontrols;
return true;
} else if (*ncontrols >= meta_ncontrols) {
*ncontrols = meta_ncontrols;
memcpy(controls, meta_controls, sizeof(*controls) * meta_ncontrols);
return true;
} else {
return false;
}
}
static bool hid_generic_get_lights(const struct hid *hid,
struct hid_light *lights, size_t *nlights)
{
const struct hid_generic *hg = containerof(hid, const struct hid_generic,
super);
const struct hid_light *meta_lights;
size_t meta_nlights;
log_assert(nlights != NULL);
meta_lights = hid_meta_out_get_lights(&hg->meta_out);
meta_nlights = hid_meta_out_get_nlights(&hg->meta_out);
if (lights == NULL) {
*nlights = meta_nlights;
return true;
} else if (*nlights >= meta_nlights) {
*nlights = meta_nlights;
memcpy(lights, meta_lights, sizeof(*lights) * meta_nlights);
return true;
} else {
return false;
}
}
static bool hid_generic_get_value(struct hid *hid, size_t control_no,
int32_t *out_value)
{
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
return hid_meta_in_get_value(&hg->meta_in, control_no, out_value);
}
static bool hid_generic_set_light(struct hid *hid, size_t light_no,
uint32_t intensity)
{
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
size_t nlights;
nlights = hid_meta_out_get_nlights(&hg->meta_out);
if (light_no >= nlights) {
return false;
}
/* Lazily start the lighting double-buffer chain (see hid_generic_init) */
if (!hg->out_started && !hg->out_faulty) {
log_misc("Starting light output to %s", hg->dev_node);
hid_meta_out_get_next_report(&hg->meta_out, hg->out_buf);
if (!WriteFile(hg->fd, hg->out_buf, hg->out_nbytes, NULL,
&hg->out_ovl)) {
if (GetLastError() != ERROR_IO_PENDING) {
log_warning("Initial WriteFile failed: %08x: %s",
(unsigned int) GetLastError(), hg->dev_node);
hg->out_faulty = true;
return false;
}
}
hg->out_started = true;
}
return hid_meta_out_set_light(&hg->meta_out, light_no, intensity);
}
static bool hid_generic_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
size_t nbytes)
{
struct hid_generic *hg = containerof(hid_fd, struct hid_generic, super);
if (ovl == &hg->out_ovl) {
/* OUT transmit ok, prepare and transmit another */
hid_meta_out_get_next_report(&hg->meta_out, hg->out_buf);
memset(&hg->out_ovl, 0, sizeof(hg->out_ovl));
if (!WriteFile(hg->fd, hg->out_buf, hg->out_nbytes, NULL,
&hg->out_ovl) && GetLastError() != ERROR_IO_PENDING) {
log_warning("Write error %08x from \"%s\" device on dev node %s",
(unsigned int) GetLastError(), hg->strings.str_prod,
hg->dev_node);
return false;
}
} else if (ovl == &hg->in_ovl) {
/* Received an IN report, dispatch it */
/* The dispatcher will swap buffers with us, i.e. it will take
ownership of whatever hg->current_in points to, and replace it
with a pointer to a fresh buffer we can use for subsequent I/O */
if (!hid_meta_in_dispatch(&hg->meta_in, &hg->in_buf, nbytes)) {
log_warning("Failed to process input report from \"%s\" device"
" on dev node %s", hg->strings.str_prod, hg->dev_node);
return false;
}
if (hg->in_buf == NULL) {
/* BEEP BOOP FIRMWARE PROGRAMMED BY SHITLORD DETECTED */
/* (ok, a less obnoxious explanation: The initial report
solicitation failed, so the report handler did not have a
valid buffer until we gave it one. This means that we have
to allocate one for it to work with manually). */
hg->in_buf = xmalloc(hid_meta_in_get_buffer_size(&hg->meta_in));
}
/* Event dispatched successfully. Kick off the next async read. */
memset(&hg->in_ovl, 0, sizeof(hg->in_ovl));
if (!ReadFile(hg->fd, hg->in_buf, hg->in_nbytes, NULL,
&hg->in_ovl) && GetLastError() != ERROR_IO_PENDING) {
log_warning("Read error %08x from \"%s\" device on dev node %s",
(unsigned int) GetLastError(), hg->strings.str_prod,
hg->dev_node);
return false;
}
}
return true;
}
static void hid_generic_close(struct hid *hid)
{
struct hid_generic *hg = containerof(hid, struct hid_generic, super);
/* Must close the FD first. I/O might be in flight and NT could potentially
complete an I/O while we're freeing our buffers and completely trash our
process heap by doing so. */
CloseHandle(hg->fd);
free(hg->dev_node);
free(hg->out_buf);
free(hg->in_buf);
hid_meta_out_fini(&hg->meta_out);
hid_meta_in_fini(&hg->meta_in);
hid_generic_strings_fini(&hg->strings);
HidD_FreePreparsedData(hg->ppd);
free(hid);
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef GENINPUT_HID_GENERIC_H
#define GENINPUT_HID_GENERIC_H
#include <windows.h>
#include <stdbool.h>
#include <stddef.h>
#include "geninput/hid.h"
bool hid_generic_open(struct hid_fd **hid, const char *dev_node,
HANDLE iocp, uintptr_t iocp_ctx);
#endif
+410
View File
@@ -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);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef GENINPUT_HID_META_IN_H
#define GENINPUT_HID_META_IN_H
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdint.h>
#include "geninput/hid.h"
#include "geninput/hid-report-in.h"
struct hid_control_in {
struct hid_report_in *report;
uint16_t collection_id;
};
struct hid_meta_in {
PHIDP_PREPARSED_DATA ppd;
HIDP_CAPS caps_tlc;
HIDP_BUTTON_CAPS *caps_btn;
HIDP_VALUE_CAPS *caps_val;
struct hid_control *controls;
size_t ncontrols;
size_t nbuttons;
bool reports_muxed;
struct hid_control_in *priv_controls;
struct hid_report_in *reports;
uint8_t nreports;
};
bool hid_meta_in_init(struct hid_meta_in *meta, PHIDP_PREPARSED_DATA ppd);
bool hid_meta_in_dispatch(struct hid_meta_in *meta, uint8_t **bytes,
size_t nbytes);
size_t hid_meta_in_get_buffer_size(const struct hid_meta_in *meta);
const struct hid_control *hid_meta_in_get_controls(
const struct hid_meta_in *meta);
size_t hid_meta_in_get_ncontrols(const struct hid_meta_in *meta);
uint32_t hid_meta_in_get_tlc_usage(const struct hid_meta_in *meta);
bool hid_meta_in_get_value(struct hid_meta_in *meta,
size_t control_no, int32_t *value);
void hid_meta_in_fini(struct hid_meta_in *meta);
#endif
+402
View File
@@ -0,0 +1,402 @@
#define LOG_MODULE "hid-generic"
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "geninput/hid-meta-out.h"
#include "geninput/hid-report-out.h"
#include "util/log.h"
#include "util/mem.h"
static bool hid_meta_out_init_caps(struct hid_meta_out *meta,
PHIDP_PREPARSED_DATA ppd);
static bool hid_meta_out_init_arrays(struct hid_meta_out *meta);
static void hid_meta_out_init_lights(struct hid_meta_out *meta, HANDLE fd);
static struct hid_report_out *hid_meta_out_lookup_report(
struct hid_meta_out *meta, uint8_t report_id);
static void hid_meta_out_fini_arrays(struct hid_meta_out *meta);
static void hid_meta_out_fini_caps(struct hid_meta_out *meta);
bool hid_meta_out_init(struct hid_meta_out *meta, PHIDP_PREPARSED_DATA ppd, HANDLE fd)
{
if (!hid_meta_out_init_caps(meta, ppd)) {
goto caps_fail;
}
if (!hid_meta_out_init_arrays(meta)) {
goto arrays_fail;
}
hid_meta_out_init_lights(meta, fd);
meta->next_report = 0;
return true;
arrays_fail:
hid_meta_out_fini_caps(meta);
caps_fail:
return false;
}
static bool hid_meta_out_init_caps(struct hid_meta_out *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.NumberOutputButtonCaps;
meta->caps_btn = xmalloc(sizeof(*meta->caps_btn) * len);
if (len > 0) {
status = HidP_GetButtonCaps(HidP_Output, 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.NumberOutputValueCaps;
meta->caps_val = xmalloc(sizeof(*meta->caps_val) * len);
if (len > 0) {
status = HidP_GetValueCaps(HidP_Output, 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 bool hid_meta_out_init_arrays(struct hid_meta_out *meta)
{
const HIDP_BUTTON_CAPS *bc;
const HIDP_VALUE_CAPS *vc;
bool *report_presence;
unsigned int nreports;
unsigned int i;
uint8_t *bytes;
size_t nbytes;
size_t count;
/* N.B. "buttons" here are just one-bit values. You have to use a
completely different API to manipulate these values even though the HID
spec and protocol doesn't really make a distinction between them. */
meta->nlights = 0;
meta->nbuttons = 0;
/* Use heap-based working area */
report_presence = xcalloc(sizeof(*report_presence) * 0x100);
/* Count up buttons, globally and per-report */
for (i = 0 ; i < meta->caps_tlc.NumberOutputButtonCaps ; i++) {
bc = &meta->caps_btn[i];
if (bc->IsRange) {
count = bc->Range.UsageMax - bc->Range.UsageMin + 1;
} else {
count = 1;
}
meta->nlights += count;
meta->nbuttons += count;
report_presence[bc->ReportID] = true;
}
for (i = 0 ; i < meta->caps_tlc.NumberOutputValueCaps ; i++) {
vc = &meta->caps_val[i];
if (vc->IsRange) {
meta->nlights += vc->Range.UsageMax - vc->Range.UsageMin + 1;
} else {
meta->nlights += 1;
}
report_presence[vc->ReportID] = true;
}
/* Count up total number of reports and initialize them */
nreports = 0; /* Allocated */
meta->nreports = 0; /* Initialized so far (see unwind below) */
for (i = 0 ; i < 0x100 ; i++) {
if (report_presence[i]) {
nreports++;
}
}
meta->reports = xmalloc(sizeof(*meta->reports) * nreports);
for (i = 0 ; i < 0x100 ; i++) {
if (report_presence[i]) {
nbytes = meta->caps_tlc.OutputReportByteLength;
bytes = xmalloc(meta->caps_tlc.OutputReportByteLength);
if (!hid_report_out_init(&meta->reports[meta->nreports],
meta->ppd, (uint8_t) i, nbytes)) {
goto r_init_fail;
}
meta->nreports++;
}
}
meta->lights = xcalloc(sizeof(*meta->lights) * meta->nlights);
meta->priv_lights = xmalloc(sizeof(*meta->priv_lights) * meta->nlights);
/* Clean up our scratch space */
free(report_presence);
return true;
r_init_fail:
free(bytes);
for (i = meta->nreports ; i > 0 ; i--) {
hid_report_out_fini(&meta->reports[i - 1]);
}
free(report_presence);
free(meta->reports);
return false;
}
static void hid_meta_out_init_lights(struct hid_meta_out *meta, HANDLE fd)
{
const HIDP_BUTTON_CAPS *bc;
const HIDP_VALUE_CAPS *vc;
struct hid_out_light *priv;
struct hid_light *light;
unsigned int pos;
unsigned int i;
int j;
pos = 0;
for (i = 0 ; i < meta->caps_tlc.NumberOutputButtonCaps ; i++) {
bc = &meta->caps_btn[i];
if (bc->IsRange) {
for (j = 0 ; j <= bc->Range.UsageMax - bc->Range.UsageMin ; j++) {
priv = &meta->priv_lights[pos];
priv->report = hid_meta_out_lookup_report(meta, bc->ReportID);
priv->collection_id = bc->LinkCollection;
priv->size = 1;
light = &meta->lights[pos];
light->usage = (bc->UsagePage << 16) | (bc->Range.UsageMin + j);
light->value_min = 0;
light->value_max = 1;
// Devices may specify their own strings for lights
if(bc->IsStringRange && bc->Range.StringMin != 0) {
HidD_GetIndexedString(fd, bc->Range.StringMin + j, light->name, sizeof(light->name));
} else if(!bc->IsStringRange && bc->NotRange.StringIndex != 0) {
HidD_GetIndexedString(fd, bc->NotRange.StringIndex, light->name, sizeof(light->name));
}
pos++;
}
} else {
priv = &meta->priv_lights[pos];
priv->report = hid_meta_out_lookup_report(meta, bc->ReportID);
priv->collection_id = bc->LinkCollection;
priv->size = 1;
light = &meta->lights[pos];
light->usage = (bc->UsagePage << 16) | bc->NotRange.Usage;
light->value_min = 0;
light->value_max = 1;
if(!bc->IsStringRange && bc->NotRange.StringIndex != 0) {
HidD_GetIndexedString(fd, bc->NotRange.StringIndex, light->name, sizeof(light->name));
}
pos++;
}
}
for (i = 0 ; i < meta->caps_tlc.NumberOutputValueCaps ; i++) {
vc = &meta->caps_val[i];
if (vc->IsRange) {
for (j = 0 ; j <= vc->Range.UsageMax - vc->Range.UsageMin ; j++) {
priv = &meta->priv_lights[pos];
priv->report = hid_meta_out_lookup_report(meta, vc->ReportID);
priv->collection_id = vc->LinkCollection;
priv->size = vc->BitSize;
light = &meta->lights[pos];
light->usage = (vc->UsagePage << 16) | (vc->Range.UsageMin + j);
light->value_min = vc->LogicalMin;
light->value_max = vc->LogicalMax;
// Devices may specify their own strings for lights
if(vc->IsStringRange && vc->Range.StringMin != 0) {
HidD_GetIndexedString(fd, vc->Range.StringMin + j, light->name, sizeof(light->name));
} else if(!vc->IsStringRange && vc->NotRange.StringIndex != 0) {
HidD_GetIndexedString(fd, vc->NotRange.StringIndex, light->name, sizeof(light->name));
}
pos++;
}
} else {
priv = &meta->priv_lights[pos];
priv->report = hid_meta_out_lookup_report(meta, vc->ReportID);
priv->collection_id = vc->LinkCollection;
priv->size = vc->BitSize;
light = &meta->lights[pos];
light->usage = (vc->UsagePage << 16) | vc->NotRange.Usage;
light->value_min = vc->LogicalMin;
light->value_max = vc->LogicalMax;
if(!vc->IsStringRange && vc->NotRange.StringIndex != 0) {
HidD_GetIndexedString(fd, vc->NotRange.StringIndex, light->name, sizeof(light->name));
}
pos++;
}
}
}
static struct hid_report_out *hid_meta_out_lookup_report(
struct hid_meta_out *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;
}
size_t hid_meta_out_get_buffer_size(const struct hid_meta_out *meta)
{
return meta->caps_tlc.OutputReportByteLength;
}
const struct hid_light *hid_meta_out_get_lights(
const struct hid_meta_out *meta)
{
return meta->lights;
}
size_t hid_meta_out_get_nlights(const struct hid_meta_out *meta)
{
return meta->nlights;
}
bool hid_meta_out_set_light(struct hid_meta_out *meta,
size_t light_no, uint32_t intensity)
{
struct hid_out_light *priv;
uint32_t usage;
if (light_no >= meta->nlights) {
return false;
}
priv = &meta->priv_lights[light_no];
usage = meta->lights[light_no].usage;
if (light_no < meta->nbuttons) {
return hid_report_out_set_bit(priv->report, meta->ppd,
priv->collection_id, usage, intensity != 0);
} else {
return hid_report_out_set_value(priv->report, meta->ppd,
priv->collection_id, usage, intensity);
}
}
void hid_meta_out_get_next_report(struct hid_meta_out *meta, uint8_t *bytes)
{
struct hid_report_out *r;
log_assert(meta->nreports > 0);
r = &meta->reports[meta->next_report];
meta->next_report = (meta->next_report + 1) % meta->nreports;
hid_report_out_get_bytes(r, bytes);
}
void hid_meta_out_fini(struct hid_meta_out *meta)
{
hid_meta_out_fini_arrays(meta);
hid_meta_out_fini_caps(meta);
}
static void hid_meta_out_fini_arrays(struct hid_meta_out *meta)
{
unsigned int i;
free(meta->priv_lights);
free(meta->lights);
for (i = 0 ; i < meta->nreports ; i++) {
hid_report_out_fini(&meta->reports[i]);
}
free(meta->reports);
}
static void hid_meta_out_fini_caps(struct hid_meta_out *meta)
{
free(meta->caps_btn);
free(meta->caps_val);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef GENINPUT_HID_META_OUT_H
#define GENINPUT_HID_META_OUT_H
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "geninput/hid.h"
#include "geninput/hid-report-out.h"
struct hid_out_light {
struct hid_report_out *report;
uint16_t collection_id;
uint16_t size;
};
struct hid_meta_out {
PHIDP_PREPARSED_DATA ppd;
HIDP_CAPS caps_tlc;
HIDP_BUTTON_CAPS *caps_btn;
HIDP_VALUE_CAPS *caps_val;
struct hid_light *lights;
size_t nlights;
size_t nbuttons;
struct hid_out_light *priv_lights;
struct hid_report_out *reports;
uint8_t nreports;
uint8_t next_report;
};
bool hid_meta_out_init(struct hid_meta_out *meta, PHIDP_PREPARSED_DATA ppd, HANDLE fd);
void hid_meta_out_get_next_report(struct hid_meta_out *meta, uint8_t *bytes);
size_t hid_meta_out_get_buffer_size(const struct hid_meta_out *meta);
const struct hid_light *hid_meta_out_get_lights(
const struct hid_meta_out *meta);
size_t hid_meta_out_get_nlights(const struct hid_meta_out *meta);
bool hid_meta_out_set_light(struct hid_meta_out *meta,
size_t light_no, uint32_t intensity);
void hid_meta_out_fini(struct hid_meta_out *meta);
#endif
+200
View File
@@ -0,0 +1,200 @@
#include <stdlib.h>
#include <windows.h>
#include "geninput/hid-mgr.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
struct hid_stub {
struct hid_stub *next;
char *dev_node;
struct hid *hid;
};
static CRITICAL_SECTION hid_mgr_cs;
static struct hid_stub *hid_stubs;
static bool hid_stub_check(struct hid_stub *stub, bool ok);
void hid_mgr_init(void)
{
InitializeCriticalSection(&hid_mgr_cs);
hid_stubs = NULL;
}
struct hid_stub *hid_mgr_get_first_stub(void)
{
return hid_stubs;
}
struct hid_stub *hid_mgr_get_next_stub(struct hid_stub *stub)
{
return stub->next;
}
struct hid_stub *hid_mgr_get_named_stub(const char *dev_node)
{
struct hid_stub *pos;
for (pos = hid_stubs ; pos != NULL ; pos = pos->next) {
if (_stricmp(pos->dev_node, dev_node) == 0) {
return pos;
}
}
pos = xmalloc(sizeof(*pos));
pos->next = hid_stubs;
pos->dev_node = str_dup(dev_node);
pos->hid = NULL;
hid_stubs = pos;
return pos;
}
void hid_mgr_lock(void)
{
EnterCriticalSection(&hid_mgr_cs);
}
void hid_mgr_unlock(void)
{
LeaveCriticalSection(&hid_mgr_cs);
}
void hid_mgr_fini(void)
{
struct hid_stub *next;
struct hid_stub *pos;
for (pos = hid_stubs ; pos != NULL ; pos = next) {
next = pos->next;
if (pos->hid != NULL) {
hid_close(pos->hid);
}
free(pos->dev_node);
free(pos);
}
DeleteCriticalSection(&hid_mgr_cs);
}
static bool hid_stub_check(struct hid_stub *stub, bool ok)
{
if (!ok) {
log_warning("Detaching erroring HID at %s", stub->dev_node);
hid_stub_detach(stub);
}
return ok;
}
void hid_stub_attach(struct hid_stub *stub, struct hid *hid)
{
if (stub->hid != NULL) {
hid_close(stub->hid);
}
stub->hid = hid;
}
void hid_stub_detach(struct hid_stub *stub)
{
if (stub->hid != NULL) {
hid_close(stub->hid);
}
stub->hid = NULL;
}
bool hid_stub_is_attached(struct hid_stub *stub)
{
return stub->hid != NULL;
}
bool hid_stub_get_device_usage(struct hid_stub *stub, uint32_t *usage)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_get_device_usage(stub->hid, usage));
}
bool hid_stub_get_controls(struct hid_stub *stub, struct hid_control *controls,
size_t *ncontrols)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_get_controls(stub->hid, controls, ncontrols));
}
bool hid_stub_get_lights(struct hid_stub *stub, struct hid_light *lights,
size_t *nlights)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_get_lights(stub->hid, lights, nlights));
}
const char *hid_stub_get_dev_node(struct hid_stub *stub)
{
return stub->dev_node;
}
bool hid_stub_get_name(struct hid_stub *stub, wchar_t *chars, size_t *nchars)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_get_name(stub->hid, chars, nchars));
}
bool hid_stub_get_value(struct hid_stub *stub, size_t control_no,
int32_t *out_value)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_get_value(stub->hid, control_no, out_value));
}
bool hid_stub_set_light(struct hid_stub *stub, size_t light_no,
uint32_t intensity)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_set_light(stub->hid, light_no, intensity));
}
bool hid_stub_handle_completion(struct hid_stub *stub, OVERLAPPED *ovl,
size_t nbytes)
{
if (stub->hid == NULL) {
return false;
}
return hid_stub_check(stub,
hid_fd_handle_completion((struct hid_fd *) stub->hid, ovl, nbytes));
}
+37
View File
@@ -0,0 +1,37 @@
#ifndef GENINPUT_HID_MGR_H
#define GENINPUT_HID_MGR_H
#include <windows.h>
#include <stdint.h>
#include <stddef.h>
#include <wchar.h>
#include "geninput/hid.h"
void hid_mgr_init(void);
void hid_mgr_lock(void);
void hid_mgr_unlock(void);
struct hid_stub *hid_mgr_get_first_stub(void);
struct hid_stub *hid_mgr_get_next_stub(struct hid_stub *stub);
struct hid_stub *hid_mgr_get_named_stub(const char *dev_node);
void hid_mgr_fini(void);
const char *hid_stub_get_dev_node(struct hid_stub *stub);
void hid_stub_attach(struct hid_stub *stub, struct hid *hid);
void hid_stub_detach(struct hid_stub *stub);
bool hid_stub_get_name(struct hid_stub *stub, wchar_t *name, size_t *nchars);
bool hid_stub_is_attached(struct hid_stub *stub);
bool hid_stub_get_device_usage(struct hid_stub *stub, uint32_t *usage);
bool hid_stub_get_controls(struct hid_stub *stub, struct hid_control *controls,
size_t *ncontrols);
bool hid_stub_get_lights(struct hid_stub *stub, struct hid_light *lights,
size_t *nlights);
bool hid_stub_get_value(struct hid_stub *stub, size_t control_no,
int32_t *out_value);
bool hid_stub_set_light(struct hid_stub *stub, size_t light_no,
uint32_t intensity);
bool hid_stub_handle_completion(struct hid_stub *stub, OVERLAPPED *ovl,
size_t nbytes);
#endif
+110
View File
@@ -0,0 +1,110 @@
#define LOG_MODULE "hid-generic"
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "geninput/hid-report-in.h"
#include "util/log.h"
#include "util/mem.h"
void hid_report_in_init(struct hid_report_in *r, uint8_t report_id,
uint32_t nbuttons, void *bytes, size_t nbytes,
PHIDP_PREPARSED_DATA ppd)
{
r->id = report_id;
r->nbuttons = nbuttons;
r->buttons = xmalloc(sizeof(*r->buttons) * r->nbuttons);
r->bytes = bytes;
r->nbytes = nbytes;
if (HidP_InitializeReportForID(HidP_Input, report_id, ppd, bytes, nbytes)
!= HIDP_STATUS_SUCCESS) {
log_warning("Error initializing IN report %02X", report_id);
}
}
uint8_t hid_report_in_get_id(struct hid_report_in *r)
{
return r->id;
}
bool hid_report_in_exchange(struct hid_report_in *r, uint8_t **bytes,
uint32_t nbytes)
{
uint8_t *last_bytes;
last_bytes = r->bytes;
r->bytes = *bytes;
r->nbytes = nbytes;
*bytes = last_bytes;
return true;
}
bool hid_report_in_get_bit(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
uint16_t collection_id, uint32_t usage, int32_t *value)
{
unsigned long i;
unsigned long nbuttons;
NTSTATUS result;
nbuttons = r->nbuttons;
result = HidP_GetUsagesEx(HidP_Input, collection_id,
(USAGE_AND_PAGE *) r->buttons, &nbuttons, ppd, (PCHAR) r->bytes,
(unsigned long) r->nbytes);
if (result != HIDP_STATUS_SUCCESS) {
log_warning("HidP_GetUsagesEx failed for report %02X: %08x",
r->id, (unsigned int) result);
return false;
}
*value = 0;
for (i = 0 ; i < nbuttons ; i++) {
if (r->buttons[i] == usage) {
*value = 1;
}
}
return true;
}
bool hid_report_in_get_value(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
uint16_t collection_id, uint32_t usage, int32_t *value)
{
NTSTATUS result;
if (r->bytes == NULL) {
log_warning("Report %02X not yet valid", r->id);
return false;
}
result = HidP_GetUsageValue(HidP_Input, usage >> 16, 0, (uint16_t) usage,
(unsigned long *) value, ppd, (PCHAR) r->bytes, r->nbytes);
if (result != HIDP_STATUS_SUCCESS) {
log_warning("HidP_GetUsageValue(%08x) failed for report %02X: %08x",
usage, r->id, (unsigned int) result);
return false;
}
return true;
}
void hid_report_in_fini(struct hid_report_in *r)
{
free(r->buttons);
free(r->bytes);
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef GENINPUT_HID_REPORT_IN_H
#define GENINPUT_HID_REPORT_IN_H
#include <windows.h>
#include <hidpi.h>
#include <hidsdi.h>
#include <stdint.h>
struct hid_report_in {
uint8_t id;
uint32_t nbuttons;
uint32_t *buttons;
uint8_t *bytes;
uint32_t nbytes;
};
void hid_report_in_init(struct hid_report_in *r, uint8_t report_id,
uint32_t nbuttons, void *bytes, size_t nbytes,
PHIDP_PREPARSED_DATA ppd);
uint8_t hid_report_in_get_id(struct hid_report_in *r);
bool hid_report_in_exchange(struct hid_report_in *r, uint8_t **bytes,
uint32_t nbytes);
bool hid_report_in_get_bit(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
uint16_t collection_id, uint32_t usage, int32_t *value);
bool hid_report_in_get_value(struct hid_report_in *r, PHIDP_PREPARSED_DATA ppd,
uint16_t collection_id, uint32_t usage, int32_t *value);
void hid_report_in_fini(struct hid_report_in *r);
#endif
+112
View File
@@ -0,0 +1,112 @@
#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-report-out.h"
#include "util/log.h"
#include "util/mem.h"
bool hid_report_out_init(struct hid_report_out *r, PHIDP_PREPARSED_DATA ppd,
uint8_t id, size_t nbytes)
{
NTSTATUS status;
r->id = id;
r->nbytes = nbytes;
r->bytes = xmalloc(nbytes);
status = HidP_InitializeReportForID(HidP_Output, r->id, ppd,
(PCHAR) r->bytes, r->nbytes);
if (status != HIDP_STATUS_SUCCESS) {
log_warning("Report %02X: HidP_InitializeReportForID failed: %08x",
id, (unsigned int) status);
goto fail;
}
return true;
fail:
free(r->bytes);
return false;
}
void hid_report_out_get_bytes(const struct hid_report_out *r, uint8_t *bytes)
{
memcpy(bytes, r->bytes, r->nbytes);
}
bool hid_report_out_set_bit(struct hid_report_out *r,
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
bool value)
{
NTSTATUS status;
unsigned long count;
uint16_t usage_hi;
uint16_t usage_lo;
usage_hi = (uint16_t) (usage >> 16);
usage_lo = (uint16_t) (usage >> 0);
count = 1;
if (value) {
status = HidP_SetUsages(HidP_Output, usage_hi, collection_id,
&usage_lo, &count, ppd, (PCHAR) r->bytes, r->nbytes);
if (status != HIDP_STATUS_SUCCESS) {
log_warning("HidP_SetUsages failed: %08x", (unsigned int) status);
return false;
}
} else {
status = HidP_UnsetUsages(HidP_Output, usage_hi, collection_id,
&usage_lo, &count, ppd, (PCHAR) r->bytes, r->nbytes);
if (status != HIDP_STATUS_SUCCESS
&& status != HIDP_STATUS_BUTTON_NOT_PRESSED /* jfc */) {
log_warning("HidP_UnsetUsages failed: %08x", (unsigned int) status);
return false;
}
}
return true;
}
bool hid_report_out_set_value(struct hid_report_out *r,
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
uint32_t value)
{
NTSTATUS status;
uint16_t usage_hi;
uint16_t usage_lo;
usage_hi = (uint16_t) (usage >> 16);
usage_lo = (uint16_t) (usage >> 0);
status = HidP_SetUsageValue(HidP_Output, usage_hi, collection_id,
usage_lo, value, ppd, (PCHAR) r->bytes, r->nbytes);
if (status != HIDP_STATUS_SUCCESS) {
log_warning("HidP_SetUsages failed: %08x", (unsigned int) status);
return false;
}
return true;
}
void hid_report_out_fini(struct hid_report_out *r)
{
free(r->bytes);
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef GENINPUT_HID_REPORT_OUT_H
#define GENINPUT_HID_REPORT_OUT_H
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdint.h>
struct hid_report_out {
uint8_t id;
uint8_t *bytes;
size_t nbytes;
};
bool hid_report_out_init(struct hid_report_out *r, PHIDP_PREPARSED_DATA ppd,
uint8_t id, size_t nbytes);
void hid_report_out_get_bytes(const struct hid_report_out *r, uint8_t *bytes);
bool hid_report_out_set_bit(struct hid_report_out *r,
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
bool value);
bool hid_report_out_set_value(struct hid_report_out *r,
PHIDP_PREPARSED_DATA ppd, uint16_t collection_id, uint32_t usage,
uint32_t value);
void hid_report_out_fini(struct hid_report_out *r);
#endif
+77
View File
@@ -0,0 +1,77 @@
#include <string.h>
#include <wchar.h>
#include "geninput/dev-list.h"
#include "geninput/hid.h"
#include "util/log.h"
#include "util/str.h"
wchar_t *hid_ri_init_name(const GUID *class_guid, const char *dev_node)
{
struct dev_list devs;
wchar_t *name;
dev_list_init(&devs, class_guid);
name = NULL;
while (dev_list_next(&devs)) {
if (_stricmp(dev_list_get_dev_node(&devs), dev_node) == 0) {
name = wstr_dup(dev_list_get_dev_name(&devs));
break;
}
}
if (name == NULL) {
/* Shouldn't ever happen, but... */
name = wstr_dup(L"???");
}
dev_list_fini(&devs);
return name;
}
bool hid_ri_get_name(wchar_t *chars, size_t *nchars, const wchar_t *src_chars)
{
size_t len;
log_assert(nchars != NULL);
len = src_chars != NULL ? wcslen(src_chars) + 1 : 0;
if (chars == NULL) {
*nchars = len;
return true;
} else if (*nchars >= len) {
memcpy(chars, src_chars, len * sizeof(wchar_t));
return true;
} else {
return false;
}
}
bool hid_ri_get_controls(struct hid_control *controls, size_t *ncontrols,
const struct hid_control *src_controls, size_t src_ncontrols)
{
log_assert(ncontrols != NULL);
if (controls == NULL) {
*ncontrols = src_ncontrols;
return true;
} else if (*ncontrols >= src_ncontrols) {
*ncontrols = src_ncontrols;
memcpy(controls, src_controls, src_ncontrols * sizeof(*src_controls));
return true;
} else {
return false;
}
}
+115
View File
@@ -0,0 +1,115 @@
#ifndef GENINPUT_HID_H
#define GENINPUT_HID_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <wchar.h>
#include <windows.h>
#define HID_FLAG_NULLABLE 0x01
#define HID_FLAG_RELATIVE 0x02
DEFINE_GUID(hid_guid, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, \
0x11, 0x11, 0x00, 0x00, 0x30);
/* Alright, I'd take the vtable stuff from C++, because this is just gross.
But you can keep everything else from that trainwreck of a language. */
struct hid;
struct hid_control {
uint32_t usage;
int32_t value_min;
int32_t value_max;
uint32_t flags;
};
struct hid_light {
uint32_t usage;
/* I'm going to go ahead and assume nobody will declare a negative light
intensity on their lighting device.
I will probably regret this decision. */
uint32_t value_min;
uint32_t value_max;
wchar_t name[126];
};
struct hid_vtbl {
void (*close)(struct hid *hid);
bool (*get_device_usage)(const struct hid *hid, uint32_t *usage);
bool (*get_name)(const struct hid *hid, wchar_t *chars, size_t *nchars);
bool (*get_controls)(const struct hid *hid, struct hid_control *controls,
size_t *ncontrols);
bool (*get_lights)(const struct hid *hid, struct hid_light *lights,
size_t *nlights);
bool (*get_value)(struct hid *hid, size_t control_no, int32_t *out_value);
bool (*set_light)(struct hid *hid, size_t light_no, uint32_t intensity);
};
struct hid {
const struct hid_vtbl *vptr;
};
#define hid_close(hid) \
(hid)->vptr->close(hid)
#define hid_get_device_usage(hid, usage) \
(hid)->vptr->get_device_usage(hid, usage)
#define hid_get_name(hid, chars, nchars) \
(hid)->vptr->get_name(hid, chars, nchars)
#define hid_get_controls(hid, controls, ncontrols) \
(hid)->vptr->get_controls(hid, controls, ncontrols)
#define hid_get_lights(hid, lights, nlights) \
(hid)->vptr->get_lights(hid, lights, nlights)
#define hid_get_value(hid, control_no, out_value) \
(hid)->vptr->get_value(hid, control_no, out_value)
#define hid_set_light(hid, light_no, illuminated) \
(hid)->vptr->set_light(hid, light_no, illuminated)
struct hid_ri;
struct hid_ri_vtbl {
struct hid_vtbl super;
void (*handle_event)(struct hid_ri *hid, const RAWINPUT *ri);
};
struct hid_ri {
const struct hid_ri_vtbl *vptr;
};
wchar_t *hid_ri_init_name(const GUID *class_guid, const char *dev_node);
bool hid_ri_get_name(wchar_t *chars, size_t *nchars,
const wchar_t *src_chars);
bool hid_ri_get_controls(struct hid_control *controls, size_t *ncontrols,
const struct hid_control *src_controls, size_t src_ncontrols);
#define hid_ri_handle_event(hid_ri, ri) \
(hid_ri)->vptr->handle_event(hid_ri, ri)
struct hid_fd;
struct hid_fd_vtbl {
struct hid_vtbl super;
bool (*handle_completion)(struct hid_fd *hid, OVERLAPPED *ovl,
size_t nbytes);
};
struct hid_fd {
const struct hid_fd_vtbl *vptr;
};
#define hid_fd_handle_completion(hid_fd, ovl, nbytes) \
(hid_fd)->vptr->handle_completion(hid_fd, ovl, nbytes)
#endif
+67
View File
@@ -0,0 +1,67 @@
#include <windows.h>
#include <dbt.h>
#include <string.h>
#include "geninput/hid.h"
#include "geninput/hotplug.h"
#include "geninput/io-thread.h"
#include "geninput/ri.h"
#include "util/log.h"
static HDEVNOTIFY hotplug_handle;
void hotplug_init(HWND wnd)
{
DEV_BROADCAST_DEVICEINTERFACE filter;
memset(&filter, 0, sizeof(filter));
filter.dbcc_size = sizeof(filter);
filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
filter.dbcc_classguid = hid_guid;
hotplug_handle = RegisterDeviceNotification(wnd, &filter,
DEVICE_NOTIFY_WINDOW_HANDLE);
if (hotplug_handle == NULL) {
log_fatal("Failed to register for HID hotplug notifications: %08x",
(unsigned int) GetLastError());
}
}
void hotplug_handle_msg(WPARAM wparam, const DEV_BROADCAST_HDR *hdr)
{
const DEV_BROADCAST_DEVICEINTERFACE *dev;
if (wparam != DBT_DEVICEARRIVAL) {
return;
}
if (hdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE) {
return;
}
dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
if (memcmp(&dev->dbcc_classguid, &hid_guid, sizeof(hid_guid))) {
return;
}
log_misc("HID hotplug detected: %s", dev->dbcc_name);
io_thread_add_device(dev->dbcc_name);
ri_scan_devices();
}
void hotplug_fini(void)
{
BOOL ok;
ok = UnregisterDeviceNotification(hotplug_handle);
if (!ok) {
log_warning("Failed to unregister HID hotplug notifications: %08x",
(unsigned int) GetLastError());
}
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef GENINPUT_HOTPLUG_H
#define GENINPUT_HOTPLUG_H
#include <windows.h>
#include <dbt.h>
void hotplug_init(HWND wnd);
void hotplug_handle_msg(WPARAM wparam, const DEV_BROADCAST_HDR *hdr);
void hotplug_fini(void);
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef GENINPUT_INPUT_CONFIG_H
#define GENINPUT_INPUT_CONFIG_H
#include <stdbool.h>
#include <stdint.h>
#include "bemanitools/input.h"
#include "geninput/mapper.h"
void mapper_config_save(const char *game_type);
void mapper_clear_action_map(uint8_t action, uint8_t page);
void mapper_clear_light_map(const struct mapped_light *ml);
bool mapper_get_action_map(uint8_t action, uint8_t page,
struct mapped_action *ma);
bool mapper_get_analog_map(uint8_t analog, struct mapped_analog *ma);
int32_t mapper_get_analog_sensitivity(uint8_t analog);
uint8_t mapper_get_nanalogs(void);
uint8_t mapper_get_npages(void);
bool mapper_is_analog_absolute(uint8_t analog);
action_iter_t mapper_iterate_actions(void);
light_iter_t mapper_iterate_lights(void);
void mapper_set_action_map(uint8_t action, uint8_t page, uint8_t bit,
const struct mapped_action *ma);
bool mapper_set_analog_map(uint8_t analog, const struct mapped_analog *ma);
bool mapper_set_analog_sensitivity(uint8_t analog, int32_t sensitivity);
void mapper_set_nanalogs(uint8_t nanalogs);
void mapper_set_nlights(uint8_t nlights);
void mapper_set_light_map(const struct mapped_light *ml, uint8_t game_light);
#endif
+223
View File
@@ -0,0 +1,223 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include "bemanitools/glue.h"
#include "bemanitools/input.h"
#include "geninput/hid.h"
#include "geninput/hid-mgr.h"
#include "geninput/io-thread.h"
#include "geninput/mapper.h"
#include "geninput/mapper-s11n.h"
#include "util/fs.h"
#include "util/log.h"
#include "util/msg-thread.h"
#include "util/str.h"
#include "util/thread.h"
static HINSTANCE input_hinst;
static volatile long input_init_count;
static FILE *mapper_config_open(const char *game_type, const char *mode)
{
char path[MAX_PATH];
str_format(path, sizeof(path), "%s_v5_00a07.bin", game_type);
return fopen_appdata("DJHACKERS", path, mode);
}
void input_set_loggers(log_formatter_t misc, log_formatter_t info,
log_formatter_t warning, log_formatter_t fatal)
{
log_to_external(misc, info, warning, fatal);
}
void input_init(thread_create_t create, thread_join_t join,
thread_destroy_t destroy)
{
if (InterlockedIncrement(&input_init_count) != 1) {
return;
}
log_info("Generic input subsystem is starting up");
hid_mgr_init();
mapper_inst = mapper_impl_create();
thread_api_init(create, join, destroy);
msg_thread_init(input_hinst);
io_thread_init();
}
void input_fini(void)
{
if (InterlockedDecrement(&input_init_count) != 0) {
return;
}
msg_thread_fini();
io_thread_fini();
mapper_impl_destroy(mapper_inst);
hid_mgr_fini();
log_info("Generic input subsystem has shut down");
}
bool mapper_config_load(const char *game_type)
{
struct mapper *m;
FILE *f;
f = mapper_config_open(game_type, "rb");
if (f == NULL) {
goto open_fail;
}
m = mapper_impl_config_load(f);
if (m == NULL) {
goto read_fail;
}
mapper_impl_destroy(mapper_inst);
mapper_inst = m;
fclose(f);
return true;
read_fail:
fclose(f);
open_fail:
return false;
}
void mapper_config_save(const char *game_type)
{
FILE *f;
f = mapper_config_open(game_type, "wb");
if (f == NULL) {
return;
}
mapper_impl_config_save(mapper_inst, f);
fclose(f);
}
void mapper_clear_action_map(uint8_t action, uint8_t page)
{
mapper_impl_clear_action_map(mapper_inst, action, page);
}
void mapper_clear_light_map(const struct mapped_light *ml)
{
mapper_impl_clear_light_map(mapper_inst, ml);
}
bool mapper_get_action_map(uint8_t action, uint8_t page,
struct mapped_action *ma)
{
return mapper_impl_get_action_map(mapper_inst, action, page, ma);
}
bool mapper_get_analog_map(uint8_t analog, struct mapped_analog *ma)
{
return mapper_impl_get_analog_map(mapper_inst, analog, ma);
}
int32_t mapper_get_analog_sensitivity(uint8_t analog)
{
return mapper_impl_get_analog_sensitivity(mapper_inst, analog);
}
uint8_t mapper_get_nanalogs(void)
{
return mapper_impl_get_nanalogs(mapper_inst);
}
uint8_t mapper_get_npages(void)
{
return mapper_impl_get_npages(mapper_inst);
}
bool mapper_is_analog_absolute(uint8_t analog)
{
return mapper_impl_is_analog_absolute(mapper_inst, analog);
}
action_iter_t mapper_iterate_actions(void)
{
return mapper_impl_iterate_actions(mapper_inst);
}
light_iter_t mapper_iterate_lights(void)
{
return mapper_impl_iterate_lights(mapper_inst);
}
void mapper_set_action_map(uint8_t action, uint8_t page, uint8_t bit,
const struct mapped_action *ma)
{
mapper_impl_set_action_map(mapper_inst, action, page, bit, ma);
}
bool mapper_set_analog_map(uint8_t analog, const struct mapped_analog *ma)
{
return mapper_impl_set_analog_map(mapper_inst, analog, ma);
}
bool mapper_set_analog_sensitivity(uint8_t analog, int32_t sensitivity)
{
return mapper_impl_set_analog_sensitivity(mapper_inst, analog, sensitivity);
}
void mapper_set_light_map(const struct mapped_light *ml, uint8_t game_light)
{
mapper_impl_set_light_map(mapper_inst, ml, game_light);
}
void mapper_set_nanalogs(uint8_t nanalogs)
{
mapper_impl_set_nanalogs(mapper_inst, nanalogs);
}
void mapper_set_nlights(uint8_t nlights)
{
mapper_impl_set_nlights(mapper_inst, nlights);
}
uint8_t mapper_read_analog(uint8_t analog)
{
return mapper_impl_read_analog(mapper_inst, analog);
}
uint64_t mapper_update(void)
{
return mapper_impl_update(mapper_inst);
}
void mapper_write_light(uint8_t light, uint8_t intensity)
{
mapper_impl_write_light(mapper_inst, light, intensity);
}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *ctx)
{
if (reason == DLL_PROCESS_ATTACH) {
input_hinst = hinst;
}
return TRUE;
}
+204
View File
@@ -0,0 +1,204 @@
#include <windows.h>
#include <stdlib.h>
#include "geninput/dev-list.h"
#include "geninput/hid.h"
#include "geninput/hid-generic.h"
#include "geninput/hid-mgr.h"
#include "geninput/io-thread.h"
#include "geninput/pacdrive.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/thread.h"
enum io_thread_cmd {
IO_THREAD_CMD_STOP,
IO_THREAD_CMD_ADD_DEVICE,
};
struct io_thread_msg {
enum io_thread_cmd cmd;
const char *dev_node;
HANDLE barrier;
};
static HANDLE io_thread_cp;
static int io_thread_id;
static void io_thread_proc_add_device(const char *dev_node);
static void io_thread_proc_init(void);
static void io_thread_proc_main_loop(void);
static void io_thread_proc_fini(void);
static int io_thread_proc(void *param);
static void io_thread_proc_add_device(const char *dev_node)
{
struct hid_stub *stub;
struct hid_fd *hid_fd;
uintptr_t ctx;
hid_mgr_lock();
stub = hid_mgr_get_named_stub(dev_node);
if (!hid_stub_is_attached(stub)) {
ctx = (uintptr_t) stub;
if (pac_open(&hid_fd, dev_node, io_thread_cp, ctx)
|| hid_generic_open(&hid_fd, dev_node, io_thread_cp, ctx)) {
hid_stub_attach(stub, (struct hid *) hid_fd);
}
}
hid_mgr_unlock();
}
static void io_thread_proc_init(void)
{
const char *dev_node;
struct dev_list devs;
io_thread_cp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
dev_list_init(&devs, &hid_guid);
while (dev_list_next(&devs)) {
dev_node = dev_list_get_dev_node(&devs);
io_thread_proc_add_device(dev_node);
}
dev_list_fini(&devs);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
log_info("USB I/O thread ready, thread id = %d",
(unsigned int) GetCurrentThreadId());
}
static void io_thread_proc_main_loop(void)
{
uintptr_t ctx;
OVERLAPPED *p_ovl;
DWORD nbytes;
struct hid_stub *stub;
struct io_thread_msg *msg;
BOOL ok;
for (;;) {
ok = GetQueuedCompletionStatus(io_thread_cp, &nbytes,
(ULONG_PTR *) &ctx, &p_ovl, 0);
if (p_ovl != NULL) {
/* An I/O completed (depending on the value of `ok' either
successfuly or not), either way dispatch the completion event */
stub = (struct hid_stub *) ctx;
hid_mgr_lock();
if (!ok) {
log_warning("Async IO returned failure: %08x",
(unsigned int) GetLastError());
hid_stub_detach(stub);
} else {
ok = hid_stub_handle_completion(stub, p_ovl, nbytes);
}
hid_mgr_unlock();
} else if (ok) {
/* No OVERLAPPED*, so this is a command message queued from a
different thread. */
msg = (struct io_thread_msg *) ctx;
if (msg->cmd == IO_THREAD_CMD_STOP) {
break;
} else if (msg->cmd == IO_THREAD_CMD_ADD_DEVICE) {
io_thread_proc_add_device(msg->dev_node);
SetEvent(msg->barrier);
}
} else if (GetLastError() == WAIT_TIMEOUT) {
/* Failure, but a benign one: timeout due to no event.
I don't exactly know why, but we need to back off here for a bit
or we end up soaking the CPU (in kernel mode...). It's probably
a bemanitools bug instead of an NT deficiency but I'll have to
track down the root cause later. */
Sleep(1);
} else {
/* Otherwise idk what's going on */
log_warning("Spurious wakeup in I/O mux: %08x",
(unsigned int) GetLastError());
}
}
}
static void io_thread_proc_fini(void)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
CloseHandle(io_thread_cp);
log_misc("USB I/O thread shutting down");
}
static int io_thread_proc(void *param)
{
HANDLE barrier;
barrier = param;
io_thread_proc_init();
SetEvent(barrier);
io_thread_proc_main_loop();
io_thread_proc_fini();
return 0;
}
void io_thread_init(void)
{
HANDLE barrier;
barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
io_thread_id = thread_create(io_thread_proc, barrier,
16384, 0);
WaitForSingleObject(barrier, INFINITE);
CloseHandle(barrier);
}
void io_thread_add_device(const char *dev_node)
{
struct io_thread_msg msg;
msg.cmd = IO_THREAD_CMD_ADD_DEVICE;
msg.dev_node = dev_node;
msg.barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
WaitForSingleObject(msg.barrier, INFINITE);
CloseHandle(msg.barrier);
}
void io_thread_fini(void)
{
struct io_thread_msg msg;
msg.cmd = IO_THREAD_CMD_STOP;
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
thread_join(io_thread_id, NULL);
thread_destroy(io_thread_id);
}
+13
View File
@@ -0,0 +1,13 @@
#ifndef GENINPUT_IO_THREAD_H
#define GENINPUT_IO_THREAD_H
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
void io_thread_init(void);
void io_thread_add_device(const char *dev_node);
void io_thread_fini(void);
#endif
+55
View File
@@ -0,0 +1,55 @@
#include <stddef.h>
#include <stdint.h>
const size_t kbd_basic_nusages = 243;
const size_t kbd_ext_nusages = 100;
const uint8_t kbd_basic_usages[] = {
0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
0x34, 0x35, 0xe1, 0x32, 0x1d, 0x1b, 0x06, 0x19,
0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
0xe2, 0x2c, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x64, 0x44,
0x45, 0x67, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x68, 0x69, 0x6a, 0x6b,
0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x00,
0x88, 0x00, 0x00, 0x87, 0x00, 0x00, 0x94, 0x93,
0x92, 0x8a, 0x00, 0x8b, 0x00, 0x89, 0x85, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x91, 0x90,
};
const uint8_t kbd_ext_usages[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65, 0xe9, 0xea,
0x00, 0x00, 0x00, 0xeb,
};
+13
View File
@@ -0,0 +1,13 @@
#ifndef GENINPUT_KBD_DATA_H
#define GENINPUT_KBD_DATA_H
#include <stddef.h>
#include <stdint.h>
extern const uint8_t kbd_basic_usages[];
extern const size_t kbd_basic_nusages;
extern const size_t kbd_ext_nusages;
extern const uint8_t kbd_ext_usages[];
#endif
+184
View File
@@ -0,0 +1,184 @@
#define LOG_MODULE "kbd"
#include <windows.h>
#include <hidsdi.h>
#include <initguid.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "geninput/hid.h"
#include "geninput/kbd.h"
#include "geninput/kbd-data.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
DEFINE_GUID(kbd_guid,
0x884B96C3,
0x56EF,
0x11D1,
0xBC, 0x8C, 0x00, 0xA0, 0xC9, 0x14, 0x05, 0xDD);
static struct hid_control kbd_controls[512];
static size_t kbd_ncontrols;
static void kbd_static_init(void);
static bool kbd_get_device_usage(const struct hid *hid, uint32_t *usage);
static bool kbd_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars);
static bool kbd_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols);
static bool kbd_get_lights(const struct hid *hid,
struct hid_light *lights, size_t *nlights);
static bool kbd_get_value(struct hid *hid, size_t control_no,
int32_t *out_value);
static bool kbd_set_light(struct hid *hid, size_t light_no,
uint32_t intensity);
static void kbd_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri);
static void kbd_free(struct hid *hid);
static const struct hid_ri_vtbl kbd_vtbl = {{
/* .free = */ kbd_free,
/* .get_device_usage = */ kbd_get_device_usage,
/* .get_name = */ kbd_get_name,
/* .get_controls = */ kbd_get_controls,
/* .get_lights = */ kbd_get_lights,
/* .get_value = */ kbd_get_value,
/* .set_light = */ kbd_set_light },
/* .handle_event = */ kbd_handle_event
};
struct kbd {
struct hid_ri super;
wchar_t *name;
uint32_t state[512];
};
static void kbd_static_init(void)
{
uint8_t code;
size_t i;
if (kbd_ncontrols == 0) {
kbd_ncontrols = kbd_basic_nusages + kbd_ext_nusages;
for (i = 0 ; i < kbd_ncontrols ; i++) {
if (i < kbd_basic_nusages) {
code = kbd_basic_usages[i];
} else {
code = kbd_ext_usages[i - kbd_basic_nusages];
}
kbd_controls[i].usage = 0x00070000 | code;
kbd_controls[i].value_min = 0;
kbd_controls[i].value_max = 1;
kbd_controls[i].flags = 0;
}
}
}
void kbd_create(struct hid_ri **hid_ri, const char *dev_node)
{
struct kbd *kbd;
char *tmp;
kbd_static_init();
kbd = xcalloc(sizeof(*kbd));
kbd->super.vptr = &kbd_vtbl;
kbd->name = hid_ri_init_name(&kbd_guid, dev_node);
*hid_ri = &kbd->super;
/* Output some debugging info */
if (!wstr_narrow(kbd->name, &tmp)) {
tmp = str_dup("???");
}
/* Dump diagnostics */
log_misc("Opened keyboard on dev node %s", dev_node);
log_misc("... Product: %s", tmp);
free(tmp);
}
static bool kbd_get_device_usage(const struct hid *hid, uint32_t *usage)
{
*usage = KBD_DEVICE_USAGE_KEYBOARD;
return true;
}
static bool kbd_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars)
{
struct kbd *kbd = containerof(hid, struct kbd, super);
return hid_ri_get_name(chars, nchars, kbd->name);
}
static bool kbd_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols)
{
return hid_ri_get_controls(controls, ncontrols, kbd_controls,
kbd_ncontrols);
}
static bool kbd_get_lights(const struct hid *hid,
struct hid_light *lights, size_t *nlights)
{
log_assert(nlights != NULL);
*nlights = 0;
return true;
}
static bool kbd_get_value(struct hid *hid, size_t control_no,
int32_t *out_value)
{
struct kbd *kbd = containerof(hid, struct kbd, super);
log_assert(control_no < kbd_ncontrols);
*out_value = kbd->state[control_no];
return true;
}
static bool kbd_set_light(struct hid *hid, size_t light_no,
uint32_t intensity)
{
return false;
}
static void kbd_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri)
{
struct kbd *kbd = containerof(hid_ri, struct kbd, super);
uint32_t value;
value = (ri->data.keyboard.Flags & RI_KEY_BREAK) == 0;
if (ri->data.keyboard.Flags & RI_KEY_E0) {
if (ri->data.keyboard.MakeCode < kbd_ext_nusages) {
kbd->state[ri->data.keyboard.MakeCode + kbd_basic_nusages] = value;
}
} else if (!(ri->data.keyboard.Flags & RI_KEY_E1)) {
if (ri->data.keyboard.MakeCode < kbd_basic_nusages) {
kbd->state[ri->data.keyboard.MakeCode] = value;
}
}
}
static void kbd_free(struct hid *hid)
{
struct kbd *kbd = containerof(hid, struct kbd, super);
free(kbd->name);
free(kbd);
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef GENINPUT_KBD_H
#define GENINPUT_KBD_H
#include "geninput/hid.h"
#define KBD_DEVICE_USAGE_KEYBOARD 0x00010006
#define KBD_DEVICE_USAGE_KEYPAD 0x00010007
void kbd_create(struct hid_ri **hid_ri, const char *dev_node);
#endif
+275
View File
@@ -0,0 +1,275 @@
#include <stdlib.h>
#include "geninput/mapper.h"
#include "geninput/mapper-s11n.h"
#include "util/fs.h"
#include "util/mem.h"
static bool mapper_impl_config_load_actions(struct mapper *m, FILE *f);
static bool mapper_impl_config_load_analogs(struct mapper *m, FILE *f);
static bool mapper_impl_config_load_lights(struct mapper *m, FILE *f);
static void mapper_impl_config_save_actions(struct mapper *m, FILE *f);
static void mapper_impl_config_save_analogs(struct mapper *m, FILE *f);
static void mapper_impl_config_save_lights(struct mapper *m, FILE *f);
struct mapper *mapper_impl_config_load(FILE *f)
{
struct mapper *m;
hid_mgr_lock(); /* Need to convert from HID objects to /dev nodes */
m = mapper_impl_create();
if ( !mapper_impl_config_load_actions(m, f) ||
!mapper_impl_config_load_analogs(m, f) ||
!mapper_impl_config_load_lights(m, f)) {
mapper_impl_destroy(m);
m = NULL;
}
hid_mgr_unlock();
return m;
}
static bool mapper_impl_config_load_actions(struct mapper *m, FILE *f)
{
char *dev_node;
struct mapped_action ma;
uint8_t action;
uint8_t page;
uint8_t bit;
uint32_t count;
uint32_t i;
if (!read_u32(f, &count)) {
return false;
}
for (i = 0 ; i < count ; i++) {
memset(&ma, 0, sizeof(ma));
if (!read_str(f, &dev_node)) {
return false;
}
ma.hid = hid_mgr_get_named_stub(dev_node);
free(dev_node);
if ( !read_u32(f, &ma.control_no) ||
!read_u32(f, &ma.value_min) ||
!read_u32(f, &ma.value_max) ||
!read_u8(f, &action) ||
!read_u8(f, &page) ||
!read_u8(f, &bit)) {
return false;
}
mapper_impl_set_action_map(m, action, page, bit, &ma);
}
return true;
}
static bool mapper_impl_config_load_analogs(struct mapper *m, FILE *f)
{
char *dev_node;
struct mapped_analog ma;
int32_t sensitivity;
uint8_t nanalogs;
uint8_t i;
if (!read_u8(f, &nanalogs)) {
return false;
}
mapper_impl_set_nanalogs(m, nanalogs);
for (i = 0 ; i < nanalogs ; i++) {
memset(&ma, 0, sizeof(ma));
if (!read_str(f, &dev_node)) {
return false;
}
if (dev_node[0] != '\0') {
ma.hid = hid_mgr_get_named_stub(dev_node);
if (!read_u32(f, &ma.control_no)) {
return false;
}
if (!read_u32(f, &sensitivity)) {
return false;
}
mapper_impl_set_analog_map(m, i, &ma);
mapper_impl_set_analog_sensitivity(m, i, sensitivity);
}
free(dev_node);
}
return true;
}
static bool mapper_impl_config_load_lights(struct mapper *m, FILE *f)
{
char *dev_node;
struct mapped_light ml;
uint8_t game_light;
uint8_t nlights;
uint32_t count;
uint32_t i;
if (!read_u8(f, &nlights)) {
return false;
}
mapper_impl_set_nlights(m, nlights);
if (!read_u32(f, &count)) {
return false;
}
for (i = 0 ; i < count ; i++) {
memset(&ml, 0, sizeof(ml));
if (!read_str(f, &dev_node)) {
return false;
}
ml.hid = hid_mgr_get_named_stub(dev_node);
free(dev_node);
if (!read_u32(f, &ml.light_no)) {
return false;
}
if (!read_u8(f, &game_light)) {
return false;
}
mapper_impl_set_light_map(m, &ml, game_light);
}
return true;
}
void mapper_impl_config_save(struct mapper *m, FILE *f)
{
hid_mgr_lock();
mapper_impl_config_save_actions(m, f);
mapper_impl_config_save_analogs(m, f);
mapper_impl_config_save_lights(m, f);
hid_mgr_unlock();
}
static void mapper_impl_config_save_actions(struct mapper *m, FILE *f)
{
action_iter_t pos;
struct mapped_action ma;
uint32_t count;
uint8_t action;
uint8_t page;
uint8_t bit;
/* Count up bindings */
count = 0;
for (pos = mapper_impl_iterate_actions(m)
; action_iter_is_valid(pos)
; action_iter_next(pos)) {
count++;
}
action_iter_free(pos);
write_u32(f, &count);
/* Write out bindings */
for (pos = mapper_impl_iterate_actions(m)
; action_iter_is_valid(pos)
; action_iter_next(pos)) {
action_iter_get_mapping(pos, &ma);
action = action_iter_get_action(pos);
page = action_iter_get_page(pos);
bit = action_iter_get_bit(pos);
write_str(f, hid_stub_get_dev_node(ma.hid));
write_u32(f, &ma.control_no);
write_u32(f, &ma.value_min);
write_u32(f, &ma.value_max);
write_u8(f, &action);
write_u8(f, &page);
write_u8(f, &bit);
}
action_iter_free(pos);
}
static void mapper_impl_config_save_analogs(struct mapper *m, FILE *f)
{
struct mapped_analog ma;
int32_t sensitivity;
uint8_t nanalogs;
uint8_t i;
nanalogs = mapper_impl_get_nanalogs(m);
write_u8(f, &nanalogs);
for (i = 0 ; i < nanalogs ; i++) {
mapper_impl_get_analog_map(m, i, &ma);
if (ma.hid == NULL) {
write_str(f, "");
} else {
sensitivity = mapper_impl_get_analog_sensitivity(m, i);
write_str(f, hid_stub_get_dev_node(ma.hid));
write_u32(f, &ma.control_no);
write_u32(f, &sensitivity);
}
}
}
static void mapper_impl_config_save_lights(struct mapper *m, FILE *f)
{
light_iter_t pos;
struct mapped_light ml;
uint8_t game_light;
uint8_t nlights;
uint32_t count;
nlights = mapper_impl_get_nlights(m);
write_u8(f, &nlights);
count = 0;
for (pos = mapper_impl_iterate_lights(m)
; light_iter_is_valid(pos)
; light_iter_next(pos)) {
count++;
}
write_u32(f, &count);
light_iter_free(pos);
for (pos = mapper_impl_iterate_lights(m)
; light_iter_is_valid(pos)
; light_iter_next(pos)) {
light_iter_get_mapping(pos, &ml);
game_light = light_iter_get_game_light(pos);
write_str(f, hid_stub_get_dev_node(ml.hid));
write_u32(f, &ml.light_no);
write_u8(f, &game_light);
}
light_iter_free(pos);
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef GENINPUT_MAPPER_S11N_H
#define GENINPUT_MAPPER_S11N_H
#include <stdio.h>
#include "geninput/mapper.h"
struct mapper *mapper_impl_config_load(FILE *f);
void mapper_impl_config_save(struct mapper *m, FILE *f);
#endif
+626
View File
@@ -0,0 +1,626 @@
#include <math.h>
#include <stdbool.h>
#include "geninput/hid-mgr.h"
#include "geninput/mapper.h"
#include "util/array.h"
#include "util/log.h"
#include "util/mem.h"
struct action_iter {
struct mapper *mapper;
size_t i;
};
struct action_mapping {
struct mapped_action src;
uint8_t action;
uint8_t page;
uint8_t bit;
};
struct analog_mapping {
struct mapped_analog src;
int32_t sensitivity;
bool bound;
bool valid;
bool absolute;
double affine_scale;
double affine_bias;
uint8_t pos;
};
struct light_iter {
struct mapper *mapper;
size_t i;
};
struct light_mapping {
struct mapped_light dest;
uint8_t game_light;
bool bound;
bool valid;
double affine_scale;
int32_t affine_bias;
};
struct mapper {
struct array actions;
struct analog_mapping *analogs;
uint8_t nanalogs;
struct array light_maps;
uint8_t *lights;
uint8_t nlights;
};
static uint64_t action_mapping_update(struct action_mapping *am);
static void analog_mapping_bind(struct analog_mapping *am);
static void analog_mapping_update(struct analog_mapping *am);
static void light_mapping_bind(struct light_mapping *lm);
static void light_mapping_send(struct light_mapping *lm,
const struct mapper *m);
struct mapper *mapper_inst;
void action_iter_get_mapping(action_iter_t iter, struct mapped_action *ma)
{
struct action_mapping *am;
log_assert(action_iter_is_valid(iter));
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
*ma = am->src;
}
uint8_t action_iter_get_action(action_iter_t iter)
{
struct action_mapping *am;
log_assert(action_iter_is_valid(iter));
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
return am->action;
}
uint8_t action_iter_get_page(action_iter_t iter)
{
struct action_mapping *am;
log_assert(action_iter_is_valid(iter));
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
return am->page;
}
uint8_t action_iter_get_bit(action_iter_t iter)
{
struct action_mapping *am;
log_assert(action_iter_is_valid(iter));
am = array_item(struct action_mapping, &iter->mapper->actions, iter->i);
return am->bit;
}
bool action_iter_is_valid(action_iter_t iter)
{
return iter->i < iter->mapper->actions.nitems;
}
void action_iter_next(action_iter_t iter)
{
iter->i++;
}
void action_iter_free(action_iter_t iter)
{
free(iter);
}
static uint64_t action_mapping_update(struct action_mapping *am)
{
int32_t value;
if (am->src.hid == NULL) {
return 0;
}
if (!hid_stub_get_value(am->src.hid, am->src.control_no, &value)) {
return 0;
}
if (value < am->src.value_min || value > am->src.value_max) {
return 0;
}
return 1ULL << am->bit;
}
static void analog_mapping_bind(struct analog_mapping *am)
{
const struct hid_control *ctl;
struct hid_control *controls;
size_t ncontrols;
am->bound = true;
am->valid = false;
if (am->src.hid == NULL) {
goto unbound_fail;
}
if (!hid_stub_get_controls(am->src.hid, NULL, &ncontrols)) {
goto size_fail;
}
if (am->src.control_no >= ncontrols) {
goto bounds_fail;
}
controls = xmalloc(sizeof(*controls) * ncontrols);
if (!hid_stub_get_controls(am->src.hid, controls, &ncontrols)) {
goto read_fail;
}
ctl = &controls[am->src.control_no];
am->affine_bias = ctl->value_min;
am->affine_scale = ctl->value_max - ctl->value_min;
am->absolute = !(ctl->flags & HID_FLAG_RELATIVE);
am->valid = true;
read_fail:
free(controls);
bounds_fail:
size_fail:
unbound_fail:
return;
}
static void analog_mapping_update(struct analog_mapping *am)
{
double tmp;
int32_t value;
if (!am->bound) {
analog_mapping_bind(am);
}
if (!am->valid) {
return;
}
if (!hid_stub_get_value(am->src.hid, am->src.control_no, &value)) {
return;
}
if (am->absolute) {
tmp = (value - am->affine_bias) / am->affine_scale;
am->pos = (uint8_t) ((tmp + 0.5) * 256.0);
} else {
am->pos += (int8_t) (value * exp(am->sensitivity / 256.0));
}
}
void light_iter_get_mapping(light_iter_t iter, struct mapped_light *ml)
{
struct light_mapping *lm;
log_assert(light_iter_is_valid(iter));
lm = array_item(struct light_mapping, &iter->mapper->light_maps, iter->i);
*ml = lm->dest;
}
uint8_t light_iter_get_game_light(light_iter_t iter)
{
struct light_mapping *lm;
log_assert(light_iter_is_valid(iter));
lm = array_item(struct light_mapping, &iter->mapper->light_maps, iter->i);
return lm->game_light;
}
bool light_iter_is_valid(light_iter_t iter)
{
return iter->i < iter->mapper->light_maps.nitems;
}
void light_iter_next(light_iter_t iter)
{
iter->i++;
}
void light_iter_free(light_iter_t iter)
{
free(iter);
}
static void light_mapping_bind(struct light_mapping *lm)
{
const struct hid_light *light;
struct hid_light *light_defs;
size_t nlights;
lm->bound = true;
lm->valid = false;
if (lm->dest.hid == NULL) {
goto unbound_fail;
}
if (!hid_stub_get_lights(lm->dest.hid, NULL, &nlights)) {
goto size_fail;
}
if (lm->dest.light_no >= nlights) {
goto bounds_fail;
}
light_defs = xmalloc(sizeof(*light_defs) * nlights);
if (!hid_stub_get_lights(lm->dest.hid, light_defs, &nlights)) {
goto read_fail;
}
light = &light_defs[lm->dest.light_no];
lm->affine_bias = light->value_min;
lm->affine_scale = light->value_max - light->value_min;
lm->valid = true;
read_fail:
free(light_defs);
bounds_fail:
size_fail:
unbound_fail:
return;
}
static void light_mapping_send(struct light_mapping *lm,
const struct mapper *m)
{
double tmp;
uint32_t value;
uint8_t intensity;
if (!lm->bound) {
light_mapping_bind(lm);
}
if (!lm->valid) {
return;
}
if (lm->game_light >= m->nlights) {
return;
}
intensity = m->lights[lm->game_light];
tmp = (intensity / 256.0) * lm->affine_scale;
value = (int32_t) (tmp + 0.5) + lm->affine_bias;
hid_stub_set_light(lm->dest.hid, lm->dest.light_no, value);
}
struct mapper *mapper_impl_create(void)
{
struct mapper *m;
m = xmalloc(sizeof(*m));
array_init(&m->actions);
m->analogs = NULL;
m->nanalogs = 0;
array_init(&m->light_maps);
m->lights = NULL;
m->nlights = 0;
return m;
}
void mapper_impl_clear_action_map(struct mapper *m, uint8_t action,
uint8_t page)
{
struct action_mapping *am;
size_t i;
for (i = 0 ; i < m->actions.nitems ; i++) {
am = array_item(struct action_mapping, &m->actions, i);
if (am->action == action && am->page == page) {
array_remove(struct action_mapping, &m->actions, i);
return;
}
}
}
void mapper_impl_clear_light_map(struct mapper *m,
const struct mapped_light *ml)
{
struct light_mapping *lm;
size_t i;
for (i = 0 ; i < m->light_maps.nitems ; ) {
lm = array_item(struct light_mapping, &m->light_maps, i);
if (memcmp(&lm->dest, ml, sizeof(*ml)) == 0) {
array_remove(struct light_mapping, &m->light_maps, i);
} else {
i++;
}
}
}
bool mapper_impl_get_action_map(struct mapper *m, uint8_t action, uint8_t page,
struct mapped_action *ma)
{
const struct action_mapping *am;
size_t i;
for (i = 0 ; i < m->actions.nitems ; i++) {
am = array_item(struct action_mapping, &m->actions, i);
if (am->action == action && am->page == page) {
*ma = am->src;
return true;
}
}
return false;
}
bool mapper_impl_get_analog_map(struct mapper *m, uint8_t analog,
struct mapped_analog *ma)
{
memset(ma, 0, sizeof(*ma));
if (analog >= m->nanalogs) {
return false;
}
*ma = m->analogs[analog].src;
return true;
}
int32_t mapper_impl_get_analog_sensitivity(struct mapper *m, uint8_t analog)
{
if (analog >= m->nanalogs) {
return 0;
}
return m->analogs[analog].sensitivity;
}
uint8_t mapper_impl_get_nanalogs(struct mapper *m)
{
return m->nanalogs;
}
uint8_t mapper_impl_get_nlights(struct mapper *m)
{
return m->nlights;
}
uint8_t mapper_impl_get_npages(struct mapper *m)
{
const struct action_mapping *am;
size_t i;
int max_page;
max_page = -1;
for (i = 0 ; i < m->actions.nitems ; i++) {
am = array_item(struct action_mapping, &m->actions, i);
if (max_page < am->page) {
max_page = am->page;
}
}
return (uint8_t) (max_page + 1);
}
action_iter_t mapper_impl_iterate_actions(struct mapper *m)
{
struct action_iter *iter;
iter = xmalloc(sizeof(*iter));
iter->mapper = m;
iter->i = 0;
return iter;
}
light_iter_t mapper_impl_iterate_lights(struct mapper *m)
{
struct light_iter *iter;
iter = xmalloc(sizeof(*iter));
iter->mapper = m;
iter->i = 0;
return iter;
}
bool mapper_impl_is_analog_absolute(struct mapper *m, uint8_t analog)
{
struct analog_mapping *am;
if (analog >= m->nanalogs) {
return false;
}
am = &m->analogs[analog];
if (!am->bound) {
analog_mapping_bind(am);
}
return m->analogs[analog].absolute;
}
void mapper_impl_set_action_map(struct mapper *m, uint8_t action, uint8_t page,
uint8_t bit, const struct mapped_action *ma)
{
struct action_mapping *am;
size_t i;
for (i = 0 ; i < m->actions.nitems ; i++) {
am = array_item(struct action_mapping, &m->actions, i);
if (am->action == action && am->page == page) {
am->src = *ma;
return;
}
}
am = array_append(struct action_mapping, &m->actions);
am->src = *ma;
am->action = action;
am->page = page;
am->bit = bit;
}
bool mapper_impl_set_analog_map(struct mapper *m, uint8_t analog,
const struct mapped_analog *ma)
{
struct analog_mapping *am;
if (analog >= m->nanalogs) {
return false;
}
am = &m->analogs[analog];
am->src = *ma;
am->bound = false;
return true;
}
bool mapper_impl_set_analog_sensitivity(struct mapper *m, uint8_t analog,
int32_t sensitivity)
{
if (analog >= m->nanalogs) {
return false;
}
m->analogs[analog].sensitivity = sensitivity;
return true;
}
void mapper_impl_set_light_map(struct mapper *m, const struct mapped_light *ml,
uint8_t game_light)
{
struct light_mapping *lm;
size_t i;
for (i = 0 ; i < m->light_maps.nitems ; i++) {
lm = array_item(struct light_mapping, &m->light_maps, i);
if (memcmp(&lm->dest, ml, sizeof(*ml)) == 0) {
lm->game_light = game_light;
return;
}
}
lm = array_append(struct light_mapping, &m->light_maps);
memset(lm, 0, sizeof(*lm));
lm->dest = *ml;
lm->game_light = game_light;
}
void mapper_impl_set_nanalogs(struct mapper *m, uint8_t nanalogs)
{
free(m->analogs);
m->analogs = xcalloc(sizeof(*m->analogs) * nanalogs);
m->nanalogs = nanalogs;
}
void mapper_impl_set_nlights(struct mapper *m, uint8_t nlights)
{
free(m->lights);
m->lights = xcalloc(sizeof(*m->lights) * nlights);
m->nlights = nlights;
}
uint8_t mapper_impl_read_analog(struct mapper *m, uint8_t analog)
{
if (analog >= m->nanalogs) {
return 0;
}
return m->analogs[analog].pos;
}
uint64_t mapper_impl_update(struct mapper *m)
{
struct action_mapping *am;
struct light_mapping *lm;
size_t i;
uint64_t result;
hid_mgr_lock();
for (i = 0 ; i < m->light_maps.nitems ; i++) {
lm = array_item(struct light_mapping, &m->light_maps, i);
light_mapping_send(lm, m);
}
for (i = 0 ; i < m->nanalogs ; i++) {
analog_mapping_update(&m->analogs[i]);
}
result = 0;
for (i = 0 ; i < m->actions.nitems ; i++) {
am = array_item(struct action_mapping, &m->actions, i);
result |= action_mapping_update(am);
}
hid_mgr_unlock();
return result;
}
void mapper_impl_write_light(struct mapper *m, uint8_t light,
uint8_t intensity)
{
if (light >= m->nlights) {
return;
}
m->lights[light] = intensity;
}
void mapper_impl_destroy(struct mapper *m)
{
free(m->lights);
array_fini(&m->light_maps);
free(m->analogs);
array_fini(&m->actions);
free(m);
}
+82
View File
@@ -0,0 +1,82 @@
#ifndef GENINPUT_MAPPER_H
#define GENINPUT_MAPPER_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "geninput/hid-mgr.h"
#include "util/defs.h"
typedef struct action_iter *action_iter_t;
typedef struct light_iter *light_iter_t;
struct mapper;
struct mapped_action {
struct hid_stub *hid;
uint32_t control_no;
int32_t value_min;
int32_t value_max;
};
struct mapped_analog {
struct hid_stub *hid;
size_t control_no;
};
struct mapped_light {
struct hid_stub *hid;
size_t light_no;
};
extern struct mapper *mapper_inst;
void action_iter_get_mapping(action_iter_t iter, struct mapped_action *ma);
uint8_t action_iter_get_action(action_iter_t iter);
uint8_t action_iter_get_page(action_iter_t iter);
uint8_t action_iter_get_bit(action_iter_t iter);
bool action_iter_is_valid(action_iter_t iter);
void action_iter_next(action_iter_t iter);
void action_iter_free(action_iter_t iter);
void light_iter_get_mapping(light_iter_t iter, struct mapped_light *ml);
uint8_t light_iter_get_game_light(light_iter_t iter);
bool light_iter_is_valid(light_iter_t iter);
void light_iter_next(light_iter_t iter);
void light_iter_free(light_iter_t iter);
struct mapper *mapper_impl_create(void);
void mapper_impl_clear_action_map(struct mapper *m, uint8_t action,
uint8_t page);
void mapper_impl_clear_light_map(struct mapper *m,
const struct mapped_light *ml);
bool mapper_impl_get_analog_map(struct mapper *m, uint8_t analog,
struct mapped_analog *ma);
bool mapper_impl_get_action_map(struct mapper *m, uint8_t action, uint8_t page,
struct mapped_action *ma);
int32_t mapper_impl_get_analog_sensitivity(struct mapper *m, uint8_t analog);
uint8_t mapper_impl_get_nanalogs(struct mapper *m);
uint8_t mapper_impl_get_nlights(struct mapper *m);
uint8_t mapper_impl_get_npages(struct mapper *m);
bool mapper_impl_is_analog_absolute(struct mapper *m, uint8_t analog);
action_iter_t mapper_impl_iterate_actions(struct mapper *m);
light_iter_t mapper_impl_iterate_lights(struct mapper *m);
void mapper_impl_set_action_map(struct mapper *m, uint8_t action, uint8_t page,
uint8_t bit, const struct mapped_action *ma);
bool mapper_impl_set_analog_map(struct mapper *m, uint8_t analog,
const struct mapped_analog *ma);
bool mapper_impl_set_analog_sensitivity(struct mapper *m, uint8_t analog,
int32_t sensitivity);
void mapper_impl_set_light_map(struct mapper *m, const struct mapped_light *ml,
uint8_t game_light);
void mapper_impl_set_nanalogs(struct mapper *m, uint8_t nanalogs);
void mapper_impl_set_nlights(struct mapper *m, uint8_t nlights);
uint8_t mapper_impl_read_analog(struct mapper *m, uint8_t analog);
uint64_t mapper_impl_update(struct mapper *m);
void mapper_impl_write_light(struct mapper *m, uint8_t light,
uint8_t intensity);
void mapper_impl_destroy(struct mapper *m);
#endif
+257
View File
@@ -0,0 +1,257 @@
#define LOG_MODULE "mouse"
#include <windows.h>
#include <initguid.h>
#include <malloc.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "geninput/hid.h"
#include "geninput/mouse.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
enum mouse_ctl_no {
MOUSE_CTL_BTN1,
MOUSE_CTL_BTN2,
MOUSE_CTL_BTN3,
MOUSE_CTL_BTN4,
MOUSE_CTL_BTN5,
MOUSE_CTL_X,
MOUSE_CTL_Y,
MOUSE_CTL_WHEEL,
MOUSE_CTL_COUNT
};
struct mouse {
struct hid_ri super;
wchar_t *name;
long last_x;
long last_y;
long values[MOUSE_CTL_COUNT];
};
static const struct hid_control mouse_controls[MOUSE_CTL_COUNT] = {
/* Left, right, middle, AUX1, AUX2 buttons */
{ 0x00090001, 0x00000000, 0x00000001, 0 },
{ 0x00090002, 0x00000000, 0x00000001, 0 },
{ 0x00090003, 0x00000000, 0x00000001, 0 },
{ 0x00090004, 0x00000000, 0x00000001, 0 },
{ 0x00090005, 0x00000000, 0x00000001, 0 },
/* X axis */
{ 0x00010030, 0xC0000000, 0x3FFFFFFF, HID_FLAG_RELATIVE },
/* Y axis */
{ 0x00010031, 0xC0000000, 0x3FFFFFFF, HID_FLAG_RELATIVE },
/* Mouse wheel */
{ 0x00010038, 0xFFFF8000, 0x00007FFF, HID_FLAG_RELATIVE },
};
static const uint16_t mouse_down_flg[] = {
RI_MOUSE_BUTTON_1_DOWN,
RI_MOUSE_BUTTON_2_DOWN,
RI_MOUSE_BUTTON_3_DOWN,
RI_MOUSE_BUTTON_4_DOWN,
RI_MOUSE_BUTTON_5_DOWN,
};
static const uint16_t mouse_up_flg[] = {
RI_MOUSE_BUTTON_1_UP,
RI_MOUSE_BUTTON_2_UP,
RI_MOUSE_BUTTON_3_UP,
RI_MOUSE_BUTTON_4_UP,
RI_MOUSE_BUTTON_5_UP,
};
DEFINE_GUID(mouse_guid, 0x378DE44C, 0x56EF, 0x11D1, 0xBC, 0x8C, \
0x00, 0xA0, 0xC9, 0x14, 0x05, 0xDD);
static bool mouse_get_device_usage(const struct hid *hid, uint32_t *usage);
static bool mouse_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars);
static bool mouse_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols);
static bool mouse_get_lights(const struct hid *hid, struct hid_light *lights,
size_t *nlights);
static bool mouse_get_value(struct hid *hid, size_t control_no,
int32_t *out_value);
static bool mouse_set_light(struct hid *hid, size_t light_no,
uint32_t intensity);
static void mouse_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri);
static void mouse_close(struct hid *hid);
static const struct hid_ri_vtbl mouse_vtbl = {{
/* .close = */ mouse_close,
/* .get_device_usage = */ mouse_get_device_usage,
/* .get_name = */ mouse_get_name,
/* .get_controls = */ mouse_get_controls,
/* .get_lights = */ mouse_get_lights,
/* .get_value = */ mouse_get_value,
/* .set_light = */ mouse_set_light },
/* .handle_event = */ mouse_handle_event
};
void mouse_create(struct hid_ri **hid_ri, const char *dev_node)
{
struct mouse *m;
char *tmp;
m = xcalloc(sizeof(*m));
m->super.vptr = &mouse_vtbl;
m->name = hid_ri_init_name(&mouse_guid, dev_node);
*hid_ri = &m->super;
if (!wstr_narrow(m->name, &tmp)) {
tmp = str_dup("???");
}
log_misc("Opened mouse on dev node %s", dev_node);
log_misc("... Product: %s", tmp);
free(tmp);
}
static bool mouse_get_device_usage(const struct hid *hid, uint32_t *usage)
{
*usage = MOUSE_DEVICE_USAGE;
return true;
}
static bool mouse_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars)
{
struct mouse *m = containerof(hid, struct mouse, super);
return hid_ri_get_name(chars, nchars, m->name);
}
static bool mouse_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols)
{
return hid_ri_get_controls(controls, ncontrols, mouse_controls,
MOUSE_CTL_COUNT);
}
static bool mouse_get_lights(const struct hid *hid,
struct hid_light *lights, size_t *nlights)
{
log_assert(nlights != NULL);
if (lights == NULL) {
*nlights = 0;
}
return true;
}
static bool mouse_get_value(struct hid *hid, size_t control_no,
int32_t *out_value)
{
struct mouse *m = containerof(hid, struct mouse, super);
long tmp;
log_assert(control_no < MOUSE_CTL_COUNT);
switch (control_no) {
case MOUSE_CTL_BTN1:
case MOUSE_CTL_BTN2:
case MOUSE_CTL_BTN3:
case MOUSE_CTL_BTN4:
case MOUSE_CTL_BTN5:
*out_value = m->values[control_no];
return true;
case MOUSE_CTL_X:
case MOUSE_CTL_Y:
case MOUSE_CTL_WHEEL:
do {
tmp = m->values[control_no];
} while (InterlockedCompareExchange(
&m->values[control_no], 0, tmp) != tmp);
*out_value = tmp;
return true;
default:
return false;
}
}
static bool mouse_set_light(struct hid *hid, size_t light_no,
uint32_t intensity)
{
return false;
}
static void mouse_handle_event(struct hid_ri *hid_ri, const RAWINPUT *ri)
{
struct mouse *m = containerof(hid_ri, struct mouse, super);
long tmp;
long dwheel;
long dx;
long dy;
int i;
if (ri->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) {
dx = ri->data.mouse.lLastX - m->last_x;
dy = ri->data.mouse.lLastY - m->last_y;
m->last_x = ri->data.mouse.lLastX;
m->last_y = ri->data.mouse.lLastY;
} else {
dx = ri->data.mouse.lLastX;
dy = ri->data.mouse.lLastY;
m->last_x += dx;
m->last_y += dy;
}
do {
tmp = m->values[MOUSE_CTL_X];
} while (InterlockedCompareExchange(
&m->values[MOUSE_CTL_X], tmp + dx, tmp) != tmp);
do {
tmp = m->values[MOUSE_CTL_Y];
} while (InterlockedCompareExchange(
&m->values[MOUSE_CTL_Y], tmp + dy, tmp) != tmp);
if (ri->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) {
dwheel = ri->data.mouse.usButtonData;
do {
tmp = m->values[MOUSE_CTL_WHEEL];
} while (InterlockedCompareExchange(
&m->values[MOUSE_CTL_WHEEL], tmp + dwheel, tmp) != tmp);
}
for (i = 0 ; i < 5 ; i++) {
if (ri->data.mouse.usButtonFlags & mouse_down_flg[i]) {
m->values[i] = 1;
} else if (ri->data.mouse.usButtonFlags & mouse_up_flg[i]) {
m->values[i] = 0;
}
}
}
static void mouse_close(struct hid *hid)
{
struct mouse *m = containerof(hid, struct mouse, super);
free(m->name);
free(m);
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef GENINPUT_MOUSE_H
#define GENINPUT_MOUSE_H
#include "geninput/hid.h"
#define MOUSE_DEVICE_USAGE 0x00010002
void mouse_create(struct hid_ri **hid_ri, const char *dev_node);
#endif
+51
View File
@@ -0,0 +1,51 @@
#include <windows.h>
#include <dbt.h>
#include <stdint.h>
#include "geninput/hotplug.h"
#include "geninput/ri.h"
#include "util/log.h"
#include "util/msg-thread.h"
void msg_window_setup(HWND hwnd)
{
ri_init(hwnd);
hotplug_init(hwnd);
log_info("Message pump thread ready, thread id = %d",
(unsigned int) GetCurrentThreadId());
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
}
LRESULT WINAPI msg_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
LPARAM lparam)
{
switch (msg) {
case WM_INPUT:
ri_handle_msg((HRAWINPUT) lparam);
return 0;
case WM_DEVICECHANGE:
hotplug_handle_msg(wparam, (DEV_BROADCAST_HDR *) lparam);
return TRUE;
default:
return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
void msg_window_teardown(HWND hwnd)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
log_info("Message pump thread shutting down");
hotplug_fini();
ri_fini();
}
+235
View File
@@ -0,0 +1,235 @@
#define LOG_MODULE "pacdrive"
#include <windows.h>
#include <hidsdi.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "geninput/hid.h"
#include "geninput/io-thread.h"
#include "geninput/pacdrive.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
/* The PacDrive appears to have a malformed descriptor for its OUT report.
Since PacDrives are fairly prevalent, we have to have a special-case HID
driver for them that uses a hard-coded report structure. */
struct pac_report {
uint8_t report_id;
uint8_t static_00;
uint8_t static_dd;
uint8_t leds_hi;
uint8_t leds_lo;
};
struct pac {
struct hid_fd super;
HANDLE fd;
OVERLAPPED ovl;
struct pac_report io_buf;
uint16_t leds;
};
static struct hid_light pac_lights[16];
static const wchar_t pac_name[] = L"PacDrive Shim";
static bool pac_get_device_usage(const struct hid *hid, uint32_t *usage);
static bool pac_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars);
static bool pac_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols);
static bool pac_get_lights(const struct hid *hid, struct hid_light *lights,
size_t *nlights);
static bool pac_get_value(struct hid *hid, size_t control_no, int32_t *value);
static bool pac_set_light(struct hid *hid, size_t light_no,
uint32_t intensity);
static bool pac_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
size_t nbytes);
static void pac_close(struct hid *hid);
static const struct hid_fd_vtbl pac_vtbl = {{
/* .close = */ pac_close,
/* .get_device_usage = */ pac_get_device_usage,
/* .get_name = */ pac_get_name,
/* .get_controls = */ pac_get_controls,
/* .get_lights = */ pac_get_lights,
/* .get_value = */ pac_get_value,
/* .set_light = */ pac_set_light },
/* .handle_event = */ pac_handle_event
};
bool pac_open(struct hid_fd **hid_out, const char *dev_node, HANDLE iocp,
uintptr_t iocp_ctx)
{
HIDD_ATTRIBUTES attrs;
HANDLE fd;
struct pac *pac;
int i;
fd = CreateFile(dev_node, GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL);
if (fd == INVALID_HANDLE_VALUE) {
goto open_fail;
}
if (!HidD_GetAttributes(fd, &attrs)) {
goto attrs_fail;
}
if (attrs.VendorID != 0xD209 || (attrs.ProductID & 0xFFF8) != 0x1500) {
goto id_fail;
}
pac = xcalloc(sizeof(*pac));
pac->super.vptr = &pac_vtbl;
pac->fd = fd;
CreateIoCompletionPort(fd, iocp, iocp_ctx, 0);
if (!WriteFile(fd, &pac->io_buf, sizeof(pac->io_buf), NULL, &pac->ovl)
&& GetLastError() != ERROR_IO_PENDING) {
log_warning("%s: Initial WriteFile failed: %08x",
dev_node, (unsigned int) GetLastError());
goto write_fail;
}
for (i = 0 ; i < lengthof(pac_lights) ; i++) {
pac_lights[i].usage = 0x0008004B; /* LED, Generic Indicator */
pac_lights[i].value_min = 0;
pac_lights[i].value_max = 1;
}
log_misc("Opened PacDrive on %s", dev_node);
*hid_out = &pac->super;
return true;
write_fail:
free(pac);
id_fail:
attrs_fail:
CloseHandle(fd);
open_fail:
return false;
}
static bool pac_get_device_usage(const struct hid *hid, uint32_t *usage)
{
*usage = 0x00010000;
return true;
}
static bool pac_get_name(const struct hid *hid, wchar_t *chars,
size_t *nchars)
{
log_assert(nchars != NULL);
if (chars != NULL) {
if (*nchars < lengthof(pac_name)) {
return false;
}
memcpy(chars, pac_name, sizeof(pac_name));
}
*nchars = lengthof(pac_name);
return true;
}
static bool pac_get_controls(const struct hid *hid,
struct hid_control *controls, size_t *ncontrols)
{
log_assert(ncontrols != NULL);
*ncontrols = 0;
return true;
}
static bool pac_get_lights(const struct hid *hid, struct hid_light *lights,
size_t *nlights)
{
log_assert(nlights != NULL);
if (lights != NULL) {
if (*nlights < lengthof(pac_lights)) {
return false;
}
memcpy(lights, pac_lights, sizeof(pac_lights));
}
*nlights = lengthof(pac_lights);
return true;
}
static bool pac_get_value(struct hid *hid, size_t control_no, int32_t *value)
{
return false;
}
static bool pac_set_light(struct hid *hid, size_t light_no,
uint32_t intensity)
{
struct pac *pac = containerof(hid, struct pac, super);
if (light_no >= lengthof(pac_lights)) {
return false;
}
if (intensity != 0) {
pac->leds |= (1 << light_no);
} else {
pac->leds &= ~(1 << light_no);
}
return true;
}
static bool pac_handle_event(struct hid_fd *hid_fd, OVERLAPPED *ovl,
size_t nbytes)
{
struct pac *pac = containerof(hid_fd, struct pac, super);
/* OUT report successfully sent, send another */
pac->io_buf.leds_hi = HIBYTE(pac->leds);
pac->io_buf.leds_lo = LOBYTE(pac->leds);
memset(&pac->ovl, 0, sizeof(pac->ovl));
if (!WriteFile(pac->fd, &pac->io_buf, sizeof(pac->io_buf), NULL,
&pac->ovl) && GetLastError() != ERROR_IO_PENDING) {
log_warning("WriteFile failed: %08x", (unsigned int) GetLastError());
return false;
}
return true;
}
static void pac_close(struct hid *hid)
{
struct pac *pac = containerof(hid, struct pac, super);
CloseHandle(pac->fd);
free(pac);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef GENINPUT_PACDRIVE_H
#define GENINPUT_PACDRIVE_H
#include <stdbool.h>
#include <stddef.h>
#include "geninput/io-thread.h"
bool pac_open(struct hid_fd **hid_out, const char *dev_node, HANDLE iocp,
uintptr_t iocp_ctx);
#endif
+187
View File
@@ -0,0 +1,187 @@
#include <stdlib.h>
#include "geninput/hid.h"
#include "geninput/hid-mgr.h"
#include "geninput/kbd.h"
#include "geninput/mouse.h"
#include "geninput/ri.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
struct ri_handle {
HANDLE fake_fd;
struct hid_ri *hid_ri;
};
static struct ri_handle *ri_handles;
static unsigned int ri_ndevs;
void ri_init(HWND hwnd)
{
RAWINPUTDEVICE filter[3];
memset(filter, 0, sizeof(filter));
filter[0].usUsagePage = KBD_DEVICE_USAGE_KEYBOARD >> 16;
filter[0].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYBOARD;
filter[0].dwFlags = RIDEV_INPUTSINK;
filter[0].hwndTarget = hwnd;
filter[1].usUsagePage = KBD_DEVICE_USAGE_KEYPAD >> 16;
filter[1].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYPAD;
filter[1].dwFlags = RIDEV_INPUTSINK;
filter[1].hwndTarget = hwnd;
filter[2].usUsagePage = MOUSE_DEVICE_USAGE >> 16;
filter[2].usUsage = (uint16_t) MOUSE_DEVICE_USAGE;
filter[2].dwFlags = RIDEV_INPUTSINK;
filter[2].hwndTarget = hwnd;
if (!RegisterRawInputDevices(filter, lengthof(filter), sizeof(filter[0]))) {
log_fatal("RegisterRawInputDevices failed: %x",
(unsigned int) GetLastError());
}
ri_scan_devices();
}
void ri_scan_devices(void)
{
char *dev_node;
struct hid_stub *stub;
RAWINPUTDEVICELIST *ridl;
unsigned int ridl_size;
unsigned int size;
unsigned int i;
hid_mgr_lock();
GetRawInputDeviceList(NULL, &ri_ndevs, sizeof(*ridl));
/* Param 2 of GetRawInputDeviceList changes meaning depending on whether or
not param 1 is NULL */
ridl_size = sizeof(*ridl) * ri_ndevs;
ridl = xmalloc(ridl_size);
if (GetRawInputDeviceList(ridl, &ridl_size, sizeof(*ridl)) == -1) {
log_fatal("GetRawInputDeviceList data failed");
}
ri_handles = xrealloc(ri_handles, sizeof(*ri_handles) * ri_ndevs);
for (i = 0 ; i < ri_ndevs ; i++) {
ri_handles[i].fake_fd = NULL;
ri_handles[i].hid_ri = NULL;
size = 0;
GetRawInputDeviceInfo(ridl[i].hDevice, RIDI_DEVICENAME, NULL, &size);
dev_node = xmalloc(size);
if (GetRawInputDeviceInfo(ridl[i].hDevice, RIDI_DEVICENAME, dev_node,
&size) == -1) {
log_warning("GetRawInputDeviceInfo(RIDI_DEVICENAME) data failed");
goto skip;
}
if (size > 1 && dev_node[0] == '\\' && dev_node[1] == '?') {
/* WinXP (and only WinXP, not Vista or up) return device nodes
starting with \??\ here instead of \\?\ like every other part
of Windows does. I'm assuming this is a bug. */
dev_node[1] = '\\';
}
stub = hid_mgr_get_named_stub(dev_node);
/* NOTE: hid_mgr owns the ridev, not us. Raw Input devices do not
get destroyed until geninput shuts down, they merely cease to
update if the corresponding physical device is unplugged. */
if (ridl[i].dwType == RIM_TYPEKEYBOARD) {
ri_handles[i].fake_fd = ridl[i].hDevice;
kbd_create(&ri_handles[i].hid_ri, dev_node);
hid_stub_attach(stub, (struct hid *) ri_handles[i].hid_ri);
} else if (ridl[i].dwType == RIM_TYPEMOUSE) {
ri_handles[i].fake_fd = ridl[i].hDevice;
mouse_create(&ri_handles[i].hid_ri, dev_node);
hid_stub_attach(stub, (struct hid *) ri_handles[i].hid_ri);
}
skip:
free(dev_node);
}
free(ridl);
hid_mgr_unlock();
}
void ri_handle_msg(HRAWINPUT msg)
{
unsigned int i;
unsigned int nbytes;
RAWINPUT ri;
nbytes = sizeof(ri);
if (GetRawInputData(msg, RID_INPUT, &ri, &nbytes, sizeof(ri.header))
== (UINT) -1) {
log_warning("GetRawInputData failed: %x",
(unsigned int) GetLastError());
return;
}
if (ri.header.hDevice == NULL) {
/* WTF?? I've seen this happen while remote-controlling someone's
desktop using TeamViewer, possibly due to an API hook injected by TV
doing something strange. However, if it's actually me misusing the
API somehow then I'd like to trap this case so that it doesn't blow
up my RawInput dispatcher. */
return;
}
for (i = 0 ; i < ri_ndevs ; i++) {
if (ri_handles[i].fake_fd == ri.header.hDevice) {
hid_ri_handle_event(ri_handles[i].hid_ri, &ri);
return;
}
}
}
void ri_fini(void)
{
RAWINPUTDEVICE filter[3];
free(ri_handles);
ri_handles = NULL;
ri_ndevs = 0;
filter[0].usUsagePage = KBD_DEVICE_USAGE_KEYBOARD >> 16;
filter[0].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYBOARD;
filter[0].dwFlags = RIDEV_REMOVE;
filter[0].hwndTarget = NULL;
filter[1].usUsagePage = KBD_DEVICE_USAGE_KEYPAD >> 16;
filter[1].usUsage = (uint16_t) KBD_DEVICE_USAGE_KEYPAD;
filter[1].dwFlags = RIDEV_REMOVE;
filter[1].hwndTarget = NULL;
filter[2].usUsagePage = MOUSE_DEVICE_USAGE >> 16;
filter[2].usUsage = (uint16_t) MOUSE_DEVICE_USAGE;
filter[2].dwFlags = RIDEV_REMOVE;
filter[2].hwndTarget = NULL;
if (!RegisterRawInputDevices(filter, lengthof(filter), sizeof(filter[0]))) {
log_warning("RegisterRawInputDevices(RIDEV_REMOVE) failed: %x",
(unsigned int) GetLastError());
}
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef GENINPUT_RI_H
#define GENINPUT_RI_H
#include <windows.h>
void ri_init(HWND hwnd);
void ri_scan_devices(void);
void ri_handle_msg(HRAWINPUT msg);
void ri_fini(void);
#endif