#include "hid_common.h"
#include <linux/input.h>
#include <string.h>
#include <sys/ioctl.h>
#ifndef HIDIOCREVOKE
#define HIDIOCREVOKE _IOW('H', 0x0D, int)
#endif
FIXTURE(hidraw) {
struct uhid_device hid;
int hidraw_fd;
};
static void close_hidraw(FIXTURE_DATA(hidraw) * self)
{
if (self->hidraw_fd)
close(self->hidraw_fd);
self->hidraw_fd = 0;
}
FIXTURE_TEARDOWN(hidraw) {
void *uhid_err;
uhid_destroy(_metadata, &self->hid);
close_hidraw(self);
pthread_join(self->hid.tid, &uhid_err);
}
#define TEARDOWN_LOG(fmt, ...) do { \
TH_LOG(fmt, ##__VA_ARGS__); \
hidraw_teardown(_metadata, self, variant); \
} while (0)
FIXTURE_SETUP(hidraw)
{
int err;
err = setup_uhid(_metadata, &self->hid, BUS_USB, 0x0001, 0x0a37, rdesc, sizeof(rdesc));
ASSERT_OK(err);
self->hidraw_fd = open_hidraw(&self->hid);
ASSERT_GE(self->hidraw_fd, 0) TH_LOG("open_hidraw");
}
TEST_F(hidraw, test_create_uhid)
{
}
TEST_F(hidraw, raw_event)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 42;
uhid_send_event(_metadata, &self->hid, buf, 6);
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
ASSERT_EQ(buf[0], 1);
ASSERT_EQ(buf[1], 42);
}
TEST_F(hidraw, raw_event_revoked)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 42;
uhid_send_event(_metadata, &self->hid, buf, 6);
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
ASSERT_EQ(buf[0], 1);
ASSERT_EQ(buf[1], 42);
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
buf[0] = 1;
buf[1] = 43;
uhid_send_event(_metadata, &self->hid, buf, 6);
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while reading the hidraw node: %d",
errno);
}
TEST_F(hidraw, ioctl_revoked)
{
int err, desc_size = 0;
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
ASSERT_EQ(err, -1) TH_LOG("ioctl_hidraw");
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while doing an ioctl: %d",
errno);
}
TEST_F(hidraw, poll_revoked)
{
struct pollfd pfds[1];
__u8 buf[10] = {0};
int err, ready;
pfds[0].fd = self->hidraw_fd;
pfds[0].events = POLLIN;
buf[0] = 1;
buf[1] = 42;
uhid_send_event(_metadata, &self->hid, buf, 6);
while (true) {
ready = poll(pfds, 1, 5000);
ASSERT_EQ(ready, 1) TH_LOG("poll return value");
if (pfds[0].revents & POLLIN) {
memset(buf, 0, sizeof(buf));
err = read(self->hidraw_fd, buf, sizeof(buf));
ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
ASSERT_EQ(buf[0], 1);
ASSERT_EQ(buf[1], 42);
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
} else {
break;
}
}
ASSERT_TRUE(pfds[0].revents & POLLHUP);
}
TEST_F(hidraw, write_event_revoked)
{
struct timespec time_to_wait;
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 2;
buf[2] = 42;
pthread_mutex_lock(&uhid_output_mtx);
memset(output_report, 0, sizeof(output_report));
clock_gettime(CLOCK_REALTIME, &time_to_wait);
time_to_wait.tv_sec += 2;
err = write(self->hidraw_fd, buf, 3);
ASSERT_EQ(err, 3) TH_LOG("unexpected error while writing to hidraw node: %d", err);
err = pthread_cond_timedwait(&uhid_output_cond, &uhid_output_mtx, &time_to_wait);
ASSERT_OK(err) TH_LOG("error while calling waiting for the condition");
ASSERT_EQ(output_report[0], 1);
ASSERT_EQ(output_report[1], 2);
ASSERT_EQ(output_report[2], 42);
err = ioctl(self->hidraw_fd, HIDIOCREVOKE, NULL);
ASSERT_OK(err) TH_LOG("couldn't revoke the hidraw fd");
buf[0] = 1;
buf[1] = 43;
err = write(self->hidraw_fd, buf, 3);
ASSERT_LT(err, 0) TH_LOG("unexpected success while writing to hidraw node: %d", err);
ASSERT_EQ(errno, ENODEV) TH_LOG("unexpected error code while writing to hidraw node: %d",
errno);
pthread_mutex_unlock(&uhid_output_mtx);
}
TEST_F(hidraw, ioctl_rdescsize)
{
int desc_size = 0;
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESCSIZE ioctl failed");
ASSERT_EQ(desc_size, sizeof(rdesc))
TH_LOG("expected size %zu, got %d", sizeof(rdesc), desc_size);
}
TEST_F(hidraw, ioctl_rdesc)
{
struct hidraw_report_descriptor desc;
int err;
desc.size = sizeof(rdesc);
err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc);
ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed");
ASSERT_EQ(memcmp(desc.value, rdesc, sizeof(rdesc)), 0)
TH_LOG("report descriptor data mismatch");
}
TEST_F(hidraw, ioctl_rdesc_small_buffer)
{
struct hidraw_report_descriptor desc;
int err;
size_t small_size = sizeof(rdesc) / 2;
desc.size = small_size;
err = ioctl(self->hidraw_fd, HIDIOCGRDESC, &desc);
ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRDESC ioctl failed with small buffer");
ASSERT_EQ(memcmp(desc.value, rdesc, small_size), 0)
TH_LOG("partial report descriptor data mismatch");
}
TEST_F(hidraw, ioctl_rawinfo)
{
struct hidraw_devinfo devinfo;
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRAWINFO, &devinfo);
ASSERT_EQ(err, 0) TH_LOG("HIDIOCGRAWINFO ioctl failed");
ASSERT_EQ(devinfo.bustype, BUS_USB)
TH_LOG("expected bustype 0x03, got 0x%x", devinfo.bustype);
ASSERT_EQ(devinfo.vendor, 0x0001)
TH_LOG("expected vendor 0x0001, got 0x%x", devinfo.vendor);
ASSERT_EQ(devinfo.product, 0x0a37)
TH_LOG("expected product 0x0a37, got 0x%x", devinfo.product);
}
TEST_F(hidraw, ioctl_gfeature)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGFEATURE ioctl failed, got %d", err);
ASSERT_EQ(buf[0], feature_data[0])
TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
ASSERT_EQ(buf[1], feature_data[1])
TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
}
TEST_F(hidraw, ioctl_gfeature_invalid)
{
__u8 buf[10] = {0};
int err;
buf[0] = 2;
err = ioctl(self->hidraw_fd, HIDIOCGFEATURE(sizeof(buf)), buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE should have failed with invalid report ID");
ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
}
TEST_F(hidraw, ioctl_invalid_nr)
{
char buf[256] = {0};
int err;
unsigned int bad_cmd;
bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x00, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0) should have failed");
ASSERT_EQ(errno, ENOTTY)
TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0), got errno %d", errno);
bad_cmd = _IOC(_IOC_READ, 'H', 0x00, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0) should have failed");
ASSERT_EQ(errno, ENOTTY)
TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0), got errno %d", errno);
bad_cmd = _IOC(_IOC_READ, 'H', 0x42, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("ioctl read-only with wrong _IOC_NR (0x42) should have failed");
ASSERT_EQ(errno, ENOTTY)
TH_LOG("expected ENOTTY for wrong read-only _IOC_NR (0x42), got errno %d", errno);
bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x42, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("ioctl read-write with wrong _IOC_NR (0x42) should have failed");
ASSERT_EQ(errno, ENOTTY)
TH_LOG("expected ENOTTY for wrong read-write _IOC_NR (0x42), got errno %d", errno);
}
TEST_F(hidraw, ioctl_invalid_type)
{
char buf[256] = {0};
int err;
unsigned int bad_cmd;
bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'I', 0x01, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("ioctl with wrong _IOC_TYPE (I) should have failed");
ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_NR, got errno %d", errno);
}
TEST_F(hidraw, ioctl_gfeature_invalid_dir)
{
__u8 buf[10] = {0};
int err;
unsigned int bad_cmd;
buf[0] = 1;
bad_cmd = _IOC(_IOC_WRITE, 'H', 0x07, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed");
ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
bad_cmd = _IOC(_IOC_READ, 'H', 0x07, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGFEATURE with wrong _IOC_DIR should have failed");
ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
}
TEST_F(hidraw, ioctl_readonly_invalid_dir)
{
char buf[256] = {0};
int err;
unsigned int bad_cmd;
bad_cmd = _IOC(_IOC_WRITE, 'H', 0x04, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed");
ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
bad_cmd = _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x04, sizeof(buf));
err = ioctl(self->hidraw_fd, bad_cmd, buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGRAWNAME with wrong _IOC_DIR should have failed");
ASSERT_EQ(errno, EINVAL) TH_LOG("expected EINVAL for wrong _IOC_DIR, got errno %d", errno);
}
TEST_F(hidraw, ioctl_sfeature)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 0x42;
buf[2] = 0x24;
err = ioctl(self->hidraw_fd, HIDIOCSFEATURE(3), buf);
ASSERT_EQ(err, 3) TH_LOG("HIDIOCSFEATURE ioctl failed, got %d", err);
}
TEST_F(hidraw, ioctl_ginput)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf);
ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGINPUT ioctl failed, got %d", err);
ASSERT_EQ(buf[0], feature_data[0])
TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
ASSERT_EQ(buf[1], feature_data[1])
TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
}
TEST_F(hidraw, ioctl_ginput_invalid)
{
__u8 buf[10] = {0};
int err;
buf[0] = 2;
err = ioctl(self->hidraw_fd, HIDIOCGINPUT(sizeof(buf)), buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGINPUT should have failed with invalid report ID");
ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
}
TEST_F(hidraw, ioctl_sinput)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 0x55;
buf[2] = 0xAA;
err = ioctl(self->hidraw_fd, HIDIOCSINPUT(3), buf);
ASSERT_EQ(err, 3) TH_LOG("HIDIOCSINPUT ioctl failed, got %d", err);
}
TEST_F(hidraw, ioctl_goutput)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf);
ASSERT_EQ(err, sizeof(feature_data)) TH_LOG("HIDIOCGOUTPUT ioctl failed, got %d", err);
ASSERT_EQ(buf[0], feature_data[0])
TH_LOG("expected feature_data[0] = %d, got %d", feature_data[0], buf[0]);
ASSERT_EQ(buf[1], feature_data[1])
TH_LOG("expected feature_data[1] = %d, got %d", feature_data[1], buf[1]);
}
TEST_F(hidraw, ioctl_goutput_invalid)
{
__u8 buf[10] = {0};
int err;
buf[0] = 2;
err = ioctl(self->hidraw_fd, HIDIOCGOUTPUT(sizeof(buf)), buf);
ASSERT_LT(err, 0) TH_LOG("HIDIOCGOUTPUT should have failed with invalid report ID");
ASSERT_EQ(errno, EIO) TH_LOG("expected EIO, got errno %d", errno);
}
TEST_F(hidraw, ioctl_soutput)
{
__u8 buf[10] = {0};
int err;
buf[0] = 1;
buf[1] = 0x33;
buf[2] = 0xCC;
err = ioctl(self->hidraw_fd, HIDIOCSOUTPUT(3), buf);
ASSERT_EQ(err, 3) TH_LOG("HIDIOCSOUTPUT ioctl failed, got %d", err);
}
TEST_F(hidraw, ioctl_rawname)
{
char name[256] = {0};
char expected_name[64];
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(name)), name);
ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWNAME ioctl failed, got %d", err);
snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id);
ASSERT_EQ(strcmp(name, expected_name), 0)
TH_LOG("expected name '%s', got '%s'", expected_name, name);
}
TEST_F(hidraw, ioctl_rawphys)
{
char phys[256] = {0};
char expected_phys[64];
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRAWPHYS(sizeof(phys)), phys);
ASSERT_GT(err, 0) TH_LOG("HIDIOCGRAWPHYS ioctl failed, got %d", err);
snprintf(expected_phys, sizeof(expected_phys), "%d", self->hid.dev_id);
ASSERT_EQ(strcmp(phys, expected_phys), 0)
TH_LOG("expected phys '%s', got '%s'", expected_phys, phys);
}
TEST_F(hidraw, ioctl_rawuniq)
{
char uniq[256] = {0};
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRAWUNIQ(sizeof(uniq)), uniq);
ASSERT_GE(err, 0) TH_LOG("HIDIOCGRAWUNIQ ioctl failed, got %d", err);
ASSERT_EQ(strlen(uniq), 0) TH_LOG("expected empty uniq, got '%s'", uniq);
}
TEST_F(hidraw, ioctl_strings_small_buffer)
{
char small_buf[8] = {0};
char expected_name[64];
int err;
err = ioctl(self->hidraw_fd, HIDIOCGRAWNAME(sizeof(small_buf)), small_buf);
ASSERT_EQ(err, sizeof(small_buf))
TH_LOG("HIDIOCGRAWNAME with small buffer failed, got %d", err);
snprintf(expected_name, sizeof(expected_name), "test-uhid-device-%d", self->hid.dev_id);
ASSERT_EQ(strncmp(small_buf, expected_name, sizeof(small_buf)), 0)
TH_LOG("expected truncated name to match first %zu chars", sizeof(small_buf));
}
int main(int argc, char **argv)
{
return test_harness_run(argc, argv);
}