UHID Device Reports Do Not Generate OS Activity

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I am trying to simulate a keyboard using the uhid interface. The fake
device shows up in dmesg, but attempting to press the "a" key does
nothing I can see. No input appears in the active terminal, nor in an
xev window.

The program receives UHID_OPEN, UHID_START, and UHID_CLOSE.

Many different report descriptors were tried. The one currently
uncommented is the one distributed with the USB implementor forum's
HID report descriptor tool. The other is one found in a description of
a boot device compatible keyboard.

Thanks in advance,
     R0b0t1
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>

#include <errno.h>
#include <fcntl.h>
#include <poll.h>

#include <sys/ioctl.h>
#include <linux/uhid.h>

#define handle_error(m) \
	do { perror(m); exit(EXIT_FAILURE); } while (0)

static unsigned char rdesc[] = {
	0x05, 0x01,
	0x09, 0x06,
	0xa1, 0x01,

	0x05, 0x07,
	0x19, 0xe0,
	0x29, 0xe7,
	0x15, 0x00,
	0x25, 0x01,
	0x75, 0x01,
	0x95, 0x08,
	0x81, 0x02,

	0x95, 0x01,
	0x75, 0x08,
	0x81, 0x03,

	0x95, 0x05,
	0x75, 0x01,
	0x05, 0x08,
	0x19, 0x01,
	0x29, 0x05,
	0x91, 0x02,

	0x95, 0x01,
	0x75, 0x03,
	0x91, 0x03,

	0x95, 0x06,
	0x75, 0x08,
	0x15, 0x00,
	0x25, 0x65,
	0x05, 0x07,
	0x19, 0x00,
	0x29, 0x65,
	0x81, 0x00,

	0xc0
};

/*
static unsigned char rdesc[] = {
	0x05, 0x01, // Usage Page (Generic Desktop)
	0x09, 0x06, // Usage (Keyboard)

	0xa1, 0x01, // Collection (Application)
	
	// Modifier report.
	0x75, 0x01, // Report Size (1)
	0x95, 0x08, // Report Count (8)
	0x05, 0x07, // Usage Page (Key Codes)
	0x19, 0xe0, // Usage Minimum (224)
	0x29, 0xe7, // Usage Maximum (231)
	0x15, 0x00, // Logical Minimum (0) - Shorten (0x14)?
	0x25, 0x01, // Logical Maximum (1)
	0x81, 0x02, // Input (Data, Variable, Absolute)

	// Reserved byte.
	0x95, 0x01, // Report Count (1)
	0x75, 0x08, // Report Size (8)
	0x81, 0x01, // Input (Constant)

	// LED report.
	0x95, 0x05, // Report Count (5)
	0x75, 0x01, // Report Size (1)
	0x05, 0x08, // Usage Page (LEDs)
	0x19, 0x01, // Usage Minimum (1)
	0x29, 0x05, // Usage Maximum (5)
	0x91, 0x02, // Output (Data, Variable, Absolute)

	// LED report padding.
	0x95, 0x01, // Report Count (1)
	0x75, 0x03, // Report Size (3)
	0x91, 0x01, // Output (Constant)

	// Key code report.
	0x95, 0x06, // Report Count (6)
	0x75, 0x08, // Report Size (8)
	0x15, 0x00, // Logical Minimum (0)
	0x26, 0xff, 0x00, // Logical Maximum (255)
	0x05, 0x07, // Usage Page (Key Codes)
	0x19, 0x00, // Usage Minimum (0)
	0x29, 0xff, // Usage Maximum (255)
	0x81, 0x00, // Input (Data, Array)

	0xc0		// End Collection
};
*/


static int
uhid_write(int fd, const struct uhid_event *he)
{
	ssize_t r = write(fd, he, sizeof(*he));
	if (r < 0) {
		fprintf(stderr, "write: cannot write to uhid: %m\n");
		return -errno;
	} else if (r != sizeof(*he)) {
		fprintf(stderr, "write: wrong size written to uhid: %ld != %lu\n",
				r, sizeof(*he));
		return -EFAULT;
	} else return 0;
}

static int
uhid_event(int fd)
{
	struct uhid_event he;
	ssize_t sz;
	if ((sz = read(fd, &he, sizeof(he))) == -1)
		handle_error("read");

	switch (he.type) {
	case UHID_START:
		fprintf(stderr, "UHID_START from uhid-dev.\n");
		break;
	case UHID_STOP:
		fprintf(stderr, "UHID_STOP from uhid-dev.\n");
		break;
	case UHID_OPEN:
		fprintf(stderr, "UHID_OPEN from uhid-dev.\n");
		return 0;
	case UHID_CLOSE:
		fprintf(stderr, "UHID_CLOSE from uhid-dev.\n");
		break;
	case UHID_OUTPUT:
		fprintf(stderr, "UHID_OUTPUT from uhid-dev.\n");
		// TODO: Handle output.
		break;
	case UHID_OUTPUT_EV:
		fprintf(stderr, "UHID_OUTPUT_EV from uhid-dev.\n");
		break;
	default:
		fprintf(stderr, "Invalid event from uhid-dev: %u.\n", he.type);
	}

	return 1;
}

int
main(int argc, char *argv[])
{
	int fd;
	ssize_t sz;
	struct uhid_event he;

	if ((fd = open("/dev/uhid", O_RDWR | O_CLOEXEC)) == -1)
		handle_error("open");

	struct pollfd fds[1] = {
		{.fd = fd, .events = POLLIN}
	};

	memset(&he, 0, sizeof(he));
	he.type = UHID_CREATE2;
	strcpy((char *)he.u.create.name, "kb-daemon-device");
	memcpy(he.u.create2.rd_data, rdesc, sizeof(rdesc));
	he.u.create2.rd_size = sizeof(rdesc);
	he.u.create2.bus = BUS_USB;
	he.u.create2.vendor = 0x15d9;
	he.u.create2.product = 0x0a37;
	he.u.create2.version = 0;
	he.u.create2.country = 0;
	if (uhid_write(fd, &he))
		handle_error("uhid_write");

	char cont = 1;
	while (cont) {
		int n = ppoll(fds, sizeof(fds), NULL, NULL);
		if (n == -1)
			handle_error("ppoll");

		for (int i = 0; i < n; i++) {
			if (fds[i].fd == fd &&
				fds[i].revents & POLLIN)
				cont = uhid_event(fds[i].fd);
		}
	}

	memset(&he, 0, sizeof(he));
	he.type = UHID_INPUT2;
	he.u.input2.size = 8;
	he.u.input2.data[0] = 0x00;
	he.u.input2.data[1] = 0x00;
	he.u.input2.data[2] = 0x04;
	he.u.input2.data[3] = 0x00;
	he.u.input2.data[4] = 0x00;
	he.u.input2.data[5] = 0x00;
	he.u.input2.data[6] = 0x00;
	he.u.input2.data[7] = 0x00;
	if (uhid_write(fd, &he))
		handle_error("uhid_write");

	memset(&he, 0, sizeof(he));
	he.type = UHID_INPUT2;
	he.u.input2.size = 8;
	he.u.input2.data[0] = 0x00;
	he.u.input2.data[1] = 0x00;
	he.u.input2.data[2] = 0x00;
	he.u.input2.data[3] = 0x00;
	he.u.input2.data[4] = 0x00;
	he.u.input2.data[5] = 0x00;
	he.u.input2.data[6] = 0x00;
	he.u.input2.data[7] = 0x00;
	if (uhid_write(fd, &he))
		handle_error("uhid_write");
	
	memset(&he, 0, sizeof(he));
	he.type = UHID_DESTROY;
	if (uhid_write(fd, &he))
		handle_error("uhid_write");
	uhid_event(fd);


	return EXIT_SUCCESS;
}

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux