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:
