101 lines
4.3 KiB
C
101 lines
4.3 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include "hid_device.h"
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/hidraw.h>
|
|
#include <linux/input.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/file.h>
|
|
#include <unistd.h>
|
|
#include "../src/profile.inc"
|
|
static unsigned calls, closes, opens;
|
|
static int mode, flags, ret_override = -2;
|
|
/* Linker wrappers never forward ANY call to /dev or ioctl. The actual
|
|
* production transport runs against these syscall doubles, including validation. */
|
|
int __wrap_open(const char *path, int oflag, ...)
|
|
{
|
|
++opens;
|
|
assert(!strcmp(path, "/dev/hidraw77")); flags = oflag;
|
|
if (mode == 8) { errno = EACCES; return -1; }
|
|
return 77;
|
|
}
|
|
int __wrap_open64(const char *path, int oflag, ...) { return __wrap_open(path, oflag); }
|
|
int __wrap_close(int fd) { assert(fd == 77); ++closes; return 0; }
|
|
int __wrap_flock(int fd, int operation)
|
|
{
|
|
assert(fd == 77 && operation == (LOCK_EX | LOCK_NB));
|
|
if (mode == 7) { errno = EWOULDBLOCK; return -1; }
|
|
return 0;
|
|
}
|
|
int __wrap_ioctl(int fd, unsigned long request, ...)
|
|
{
|
|
assert(fd == 77); ++calls;
|
|
va_list ap; va_start(ap, request); void *argument = va_arg(ap, void *); va_end(ap);
|
|
if (request == HIDIOCGRAWINFO) {
|
|
struct hidraw_devinfo *info = argument;
|
|
info->bustype = mode == 1 ? BUS_USB : BUS_I2C;
|
|
info->vendor = mode == 2 ? 0x1234 : 0x0b05;
|
|
info->product = mode == 3 ? 0x9999 : 0x5570;
|
|
return 0;
|
|
}
|
|
if (request == HIDIOCGRDESCSIZE) {
|
|
*(int *)argument = mode == 4 ? HID_MAX_DESCRIPTOR_SIZE+1 : (int)sizeof profile;
|
|
return 0;
|
|
}
|
|
if (request == HIDIOCGRDESC) {
|
|
struct hidraw_report_descriptor *d = argument;
|
|
assert(d->size == sizeof profile);
|
|
memcpy(d->value, profile, sizeof profile);
|
|
if (mode == 5) d->value[0] ^= 1;
|
|
if (mode == 6) ++d->size;
|
|
return 0;
|
|
}
|
|
assert(request == HIDIOCGFEATURE(23) || request == HIDIOCGFEATURE(29) ||
|
|
request == HIDIOCSFEATURE(2) || request == HIDIOCSFEATURE(3) || request == HIDIOCSFEATURE(10));
|
|
uint8_t *r = argument;
|
|
unsigned n = _IOC_SIZE(request);
|
|
if (_IOC_NR(request) == _IOC_NR(HIDIOCSFEATURE(2))) r[0] = 0; /* ensure caller const bytes survive */
|
|
if (ret_override == -1) { errno = EIO; return -1; }
|
|
return ret_override == -2 ? (int)n : ret_override;
|
|
}
|
|
int main(void)
|
|
{
|
|
assert(hid_open_verified("hidraw77") == 77 && (flags & O_ACCMODE) == O_RDONLY);
|
|
assert(__wrap_close(77) == 0);
|
|
assert(hid_open_control("hidraw77") == 77 && (flags & O_ACCMODE) == O_RDWR && (flags & O_NOFOLLOW) && (flags & O_CLOEXEC));
|
|
assert(__wrap_close(77) == 0);
|
|
for (mode = 1; mode <= 8; ++mode) {
|
|
unsigned before = closes;
|
|
assert(hid_open_control("hidraw77") == -1);
|
|
assert(closes == before + (mode == 8 ? 0u : 1u));
|
|
assert(errno == (mode <= 3 ? ENODEV : mode <= 6 ? EPROTO : mode == 7 ? EWOULDBLOCK : EACCES));
|
|
}
|
|
mode = 0;
|
|
int fd = 77;
|
|
uint8_t r[29] = {0x41};
|
|
assert(hid_get(&fd, r, 23) == 23);
|
|
r[0] = 0x43; assert(hid_get(&fd, r, 29) == 29);
|
|
const uint8_t selector[] = {0x42, 0, 0}, host[] = {0x46, 0}, automatic[] = {0x46, 1};
|
|
assert(hid_set(&fd, selector, 3) == 3 && selector[0] == 0x42);
|
|
assert(hid_set(&fd, host, 2) == 2 && host[0] == 0x46);
|
|
assert(hid_set(&fd, automatic, 2) == 2);
|
|
uint8_t range[10] = {0x45, 1, 0, 0, 0, 0, 180, 190, 254, 255};
|
|
assert(hid_set(&fd, range, 10) == 10 && range[0] == 0x45);
|
|
unsigned before = calls;
|
|
range[1] = 8; assert(hid_set(&fd, range, 10) == -1 && errno == EINVAL);
|
|
range[1] = 1; range[4] = 1; assert(hid_set(&fd, range, 10) == -1);
|
|
assert(hid_set(&fd, selector, 2) == -1 && hid_set(NULL, host, 2) == -1);
|
|
r[0] = 0x31; assert(hid_get(&fd, r, 23) == -1);
|
|
assert(calls == before);
|
|
ret_override = 1; assert(hid_set(&fd, host, 2) == -1 && errno == EPROTO);
|
|
ret_override = 0; assert(hid_set(&fd, host, 2) == -1 && errno == EPROTO);
|
|
ret_override = 3; assert(hid_set(&fd, host, 2) == -1 && errno == EPROTO);
|
|
ret_override = -1; assert(hid_set(&fd, host, 2) == -1 && errno == EIO);
|
|
assert(hid_open_control("../../hidraw77") == -1 && errno == EINVAL);
|
|
assert(opens == 10);
|
|
puts("Production hidraw transport verified via syscall doubles; no device opened.");
|
|
return 0;
|
|
}
|