HID stub driver

The following hid-joystick-stub.c code is a simplified implementation of the HID driver, which consumes HID joystick/gamepad events and prints them to standard error. For a detailed explanation of this code, refer to the following "HID stub driver details" section.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define __NOLEGACYUT 1

#include <sys/hiddi.h>
#include <sys/hidut.h>

#define COORDINATES_ABSOLUTE 0x01

/* Device we are going to attach onto.  Change "HIDD_CONNECT_WILDCARD" to
 * vendor and product id from hidview.
 */
#define JOYSTICK_VENDOR_ID HIDD_CONNECT_WILDCARD
#define JOYSTICK_PRODUCT_ID HIDD_CONNECT_WILDCARD

typedef struct client_report {
	struct client_report *next;			   // link
	struct hidd_report_instance *instance; // pointer report instance from insertion
	struct hidd_report *report;			   // registration handle
	_uint16 max_but;					   // max buttons returned in a report
	_uint16 *cbtnbuf;					   // current button buffer
	_uint16 *pbtnbuf;					   // previous button buffer
	_uint16 *rbtnbuf;					   // repeated button buffer
	_uint32 data_flags;
} client_report_t;

typedef struct _client_ctrl {
	struct hidd_connection *connection;
	client_report_t *reports;
} client_ctrl_t;

client_ctrl_t ClientCtrl;

void hid_insertion(struct hidd_connection *, hidd_device_instance_t *);
void hid_removal(struct hidd_connection *, hidd_device_instance_t *);
void hid_report(struct hidd_connection *, struct hidd_report *, void *, _uint32, _uint32, void *);
void show_device_info(struct hidd_connection *, hidd_device_instance_t *);
int get_data_properties(struct hidd_report_instance *, client_report_t *);

void show_buttons(_uint16 *, _uint16, char *);
void joystick_demo_signal_handler(int);

int main(int argc, char *argv[]) {
	hidd_device_ident_t interest = {HIDD_CONNECT_WILDCARD, HIDD_CONNECT_WILDCARD, HIDD_CONNECT_WILDCARD};
	hidd_funcs_t funcs = {_HIDDI_NFUNCS, hid_insertion, hid_removal, hid_report, NULL};
	hidd_connect_parm_t parm = {NULL, HID_VERSION, HIDD_VERSION, 0, 0, 0, 0, HIDD_CONNECT_WAIT};
	int status, opt;

	while ((opt = getopt(argc, argv, "s:")) != -1) {
		switch (opt) {
		case 's':
			parm.path = optarg;
			break;
		default:
			break;
		}
	}

	interest.vendor_id = JOYSTICK_VENDOR_ID;
	interest.product_id = JOYSTICK_PRODUCT_ID;

	fprintf(stderr, "Looking for device %x %x\n", interest.vendor_id, interest.product_id);

	parm.funcs = &funcs;
	parm.device_ident = &interest;

	signal(SIGHUP, SIG_IGN);
	signal(SIGPWR, SIG_IGN);
	signal(SIGTERM, joystick_demo_signal_handler);
	signal(SIGKILL, joystick_demo_signal_handler);

	if ((status = hidd_connect(&parm, &ClientCtrl.connection)) != EOK) {
		fprintf(stderr, "Can't connect to HID Server %s\n", strerror(status));
		return 1;
	}

	while (1) {
		sleep(10);
	}

	wait(NULL);

	return EXIT_SUCCESS;
}

void hid_insertion(struct hidd_connection *connection, hidd_device_instance_t *device_instance)
{
	struct hidd_report_instance *report_instance;
	struct hidd_report *report;
	client_report_t *client_report = NULL;
	int status;
	_uint16 max_but;
	int x, y;
	struct hidd_collection **hidd_collections, **hidd_mcollections;
	_uint16 num_col, num_mcol;
	_uint16 usage_page, usage;

	fprintf(stderr, "Device inserted...\n");
	show_device_info(connection, device_instance);

	/* Get root level collections */
	hidd_get_collections(device_instance, NULL, &hidd_collections, &num_col);

	for (x = 0; x < num_col; x++) {
		hidd_collection_usage(hidd_collections[x], &usage_page, &usage);
		switch (usage_page) {
		case HIDD_PAGE_DESKTOP:
			switch (usage) {
			case HIDD_USAGE_JOYSTICK:
			case HIDD_USAGE_GAMEPAD:
			case HIDD_USAGE_MOUSE: /* Buttons for joystick */
				/* Get pointer collection */
				if (hidd_get_report_instance(hidd_collections[0], 0, HID_INPUT_REPORT, &report_instance) == EOK) {
					hidd_num_buttons(report_instance, &max_but);

					fprintf(stderr, "Input report found, NUM_Buttons %d\n", max_but);

					if ((status = hidd_report_attach(connection, device_instance, report_instance, 0, sizeof(client_report_t) + ((max_but * 3) * sizeof(_int32)), &report)) == EOK) {
						client_report = hidd_report_extra(report);
						client_report->report = report;
						client_report->instance = report_instance;
						client_report->max_but = max_but;
						client_report->cbtnbuf = (_uint16 *)(client_report + 1);						// setup pointer  to button data
						client_report->pbtnbuf = client_report->cbtnbuf + ((max_but) * sizeof(_int32)); // setup
						client_report->rbtnbuf = client_report->pbtnbuf + ((max_but) * sizeof(_int32)); // setup	client_report->cbtnbuf 			= (_uint16 *)(client_report + 1); // setup pointer  to button data
						get_data_properties(report_instance, client_report);

						hidd_set_idle(report, 0);
					}
				}

				hidd_get_collections(NULL, hidd_collections[x], &hidd_mcollections, &num_mcol);

				for (y = 0; y < num_mcol; y++) {
					if (num_mcol && hidd_get_report_instance(hidd_mcollections[y], 0, HID_INPUT_REPORT, &report_instance) == EOK) {
						hidd_num_buttons(report_instance, &max_but);

						fprintf(stderr, "Input report found, NUM_Buttons %d\n", max_but);

						if ((status = hidd_report_attach(connection, device_instance, report_instance, 0, sizeof(client_report_t) + ((max_but * 3) * sizeof(_int32)), &report)) == EOK) {
							client_report = hidd_report_extra(report);
							client_report->report = report;
							client_report->instance = report_instance;
							client_report->max_but = max_but;
							client_report->cbtnbuf = (_uint16 *)(client_report + 1);						// setup pointer  to button data
							client_report->pbtnbuf = client_report->cbtnbuf + ((max_but) * sizeof(_int32)); // setup
							client_report->rbtnbuf = client_report->pbtnbuf + ((max_but) * sizeof(_int32)); // setup
							get_data_properties(report_instance, client_report);
						}
					}
				}
				break;
			default:
				break;
			}
			break;
		}
	}
}

void hid_removal(struct hidd_connection *connection, hidd_device_instance_t *instance) {
	fprintf(stderr, "Device removed\n");

	/* detach from all reports you have register on this device */
	hidd_reports_detach(connection, instance);
}

void hid_report(struct hidd_connection *connection, struct hidd_report *report, void *report_data, _uint32 report_len, _uint32 flags, void *user) {
	client_report_t *c_report = user;
	_uint16 usage_page, usage;
	struct hidd_collection *collection;
	_uint32 x, y, hat, analog_hat;
	_int32 scaled_hat;
	_uint16 num_press;
	_uint16 *tptr;
	_uint16 len = 30;
	hidd_button_t button_list[30];

	hidd_report_collection(c_report->instance, &collection);
	hidd_collection_usage(collection, &usage_page, &usage);
	hidd_get_all_buttons(c_report->instance, report_data, button_list, &len);

	if (!hidd_get_usage_value(c_report->instance, NULL, 1, HIDD_USAGE_X, report_data, &x)) {
		if (!hidd_get_usage_value(c_report->instance, NULL, 1, HIDD_USAGE_Y, report_data, &y)) {
			fprintf(stderr, "x=%3d y=%3d ", x, y);
		}
	}

	if (!hidd_get_usage_value(c_report->instance, NULL, 1, HIDD_USAGE_HAT_SWITCH, report_data, &hat)) {
		fprintf(stderr, "%s Hatswitch=%3d ", (c_report->data_flags & COORDINATES_ABSOLUTE) ? "Absolute" : "Relative", hat);
	}
	if (!hidd_get_scaled_usage_value(c_report->instance, NULL, 1, HIDD_USAGE_HAT_SWITCH, report_data, &scaled_hat)) {
		fprintf(stderr, "%s Scaled Hatswitch=%3d ", (c_report->data_flags & COORDINATES_ABSOLUTE) ? "Absolute" : "Relative", scaled_hat);
	}
	if (!hidd_get_usage_value(c_report->instance, NULL, 1, HIDD_USAGE_RZ, report_data, &analog_hat)) {
		fprintf(stderr, "%s RZ=%3d", (c_report->data_flags & COORDINATES_ABSOLUTE) ? "Absolute" : "Relative", analog_hat);
	}
	fprintf(stderr, "\n");

	/* Get currently pressed button data from report */
	num_press = c_report->max_but;
	hidd_get_buttons(c_report->instance, NULL, HIDD_PAGE_BUTTONS, report_data, c_report->cbtnbuf, &num_press);

	/* Get buttons pressed */
	num_press = c_report->max_but;
	hidd_button_list_diff(c_report->cbtnbuf, c_report->pbtnbuf, c_report->rbtnbuf, &num_press);
	show_buttons(c_report->rbtnbuf, num_press, "Buttons Pressed :");

	/* Get buttons released */
	num_press = c_report->max_but;
	hidd_button_list_diff(c_report->pbtnbuf, c_report->cbtnbuf, c_report->rbtnbuf, &num_press);
	show_buttons(c_report->rbtnbuf, num_press, "Buttons Released :");

	/* Get buttons held */
	num_press = c_report->max_but;
	hidd_button_list_same(c_report->cbtnbuf, c_report->pbtnbuf, c_report->rbtnbuf, &num_press);
	show_buttons(c_report->rbtnbuf, num_press, "Buttons Held :");

	/* Exchange prev and current ptrs to save copying data */
	tptr = c_report->pbtnbuf;
	c_report->pbtnbuf = c_report->cbtnbuf;
	c_report->cbtnbuf = tptr;
}

void joystick_demo_signal_handler(int signo)
{
	int status = 0;
	client_report_t *rep, *nrep;

	for (rep = ClientCtrl.reports; rep; rep = nrep) {
		nrep = rep->next;

		if (EOK != (status = hidd_report_detach(rep->report))) {
			fprintf(stderr, "Error detaching from report\n");
		}
	}

	hidd_disconnect(ClientCtrl.connection);
	_exit(0);
}

int get_data_properties(struct hidd_report_instance *report_instance, client_report_t *client_report)
{
	hidd_report_props_t *report_props;
	_uint16 len;
	int x;

	hidd_get_num_props(report_instance, &len);
	report_props = (hidd_report_props_t *)malloc(len * sizeof(hidd_report_props_t));

	hidd_get_report_props(report_instance, report_props, &len);

	for (x = 0; x < len; x++) {
		if (report_props[x].usage_min == HIDD_USAGE_X || report_props[x].usage_min == HIDD_USAGE_Y) {
			if (!(report_props[x].data_properties & HID_FIELD_RELATIVE)) {
				client_report->data_flags |= COORDINATES_ABSOLUTE;
			}
		}
	}

	return (EOK);
}

void show_device_info(struct hidd_connection *connection, hidd_device_instance_t *dev_instance)
{
	if (dev_instance) {
		char buffer[50];
		char *manufacturer, *product, *serial_number;

		manufacturer = (hidd_get_manufacturer_string(connection, dev_instance, buffer, 50) == EOK) ? buffer : "";
		product = (hidd_get_product_string(connection, dev_instance, buffer, 50) == EOK) ? buffer : "";
		serial_number = (hidd_get_serial_number_string(connection, dev_instance, buffer, 50) == EOK) ? buffer : "";

		fprintf(stderr, "Device Address       : %d\n", dev_instance->devno);
		fprintf(stderr, "Vendor               : 0x%04x (%s)\n", dev_instance->device_ident.vendor_id, manufacturer);
		fprintf(stderr, "Product              : 0x%04x (%s)\n", dev_instance->device_ident.product_id, product);
		fprintf(stderr, "Serial Number        : %s\n", serial_number);
		fprintf(stderr, "Version              : r%x.%02x\n", dev_instance->device_ident.version >> 8, dev_instance->device_ident.version & 0xFF);
	}
}

void show_buttons(_uint16 *buf, _uint16 num_press, char *msg) {
	int x;

	if (num_press) {
		fprintf(stderr, "%s", msg);

		for (x = 0; x < num_press; x++) {
			fprintf(stderr, "0x%x ", buf[x]);
		}

		fprintf(stderr, "\n");
	}
}
Page updated: