Files
djhackersdev_bemanitools/src/main/geninput/pacdrive.c
T
icex2 b6e588116d Various import fixes caused by clang-format
Added format protection block to avoid re-ordering of header files
according to formatting rules.
2019-10-22 21:04:45 +02:00

244 lines
5.6 KiB
C

#define LOG_MODULE "pacdrive"
// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <hidsdi.h>
// clang-format on
#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);
}