Added format protection block to avoid re-ordering of header files according to formatting rules.
244 lines
5.6 KiB
C
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);
|
|
}
|