HID stub driver details

This section describes hid-joystick-stub.c in detail.

The code declares two custom data structures, client_ctrl_t and client_report_t, to store program-specific and report-specific data, as illustrated in the following example:
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;

Setup and connection

In the following example, the main() function defines which devices it wants to receive reports from, specifies callbacks, and sets up the connections parameters:
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;
Next, it sets up signal handling:
signal(SIGHUP, SIG_IGN);
signal(SIGPWR, SIG_IGN);
signal(SIGTERM, joystick_demo_signal_handler);
signal(SIGKILL, joystick_demo_signal_handler);
Then, it connects to the HID server:
if ((status = hidd_connect(&parm, &ClientCtrl.connection)) != EOK) {
	fprintf(stderr, "Can't connect to HID Server %s\n", strerror(status));
	return 1;
}
Finally, it loops, sleeping for 10 seconds at a time until stopped:
	while (1) {
		sleep(10);
	}
	wait(NULL);
	return EXIT_SUCCESS;
}

Signal handling

Upon receiving a SIGTEM or SIGKILL signal, the signal handler iterates through reports that it's previously attached and detaches them. It then disconnects from the HID server:
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);
}

Insertion and report attachment

Once connected to the HID server, and upon USB HID device insertion, libhiddi invokes the hid_insertion() callback with appropriate parameters. For every invocation, hid_insertion() displays the following device information:
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);
It retrieves root collections:
/* Get root level collections */
hidd_get_collections(device_instance, NULL, &hidd_collections, &num_col);
Then, iterates for every root collection to find the collection usage, and finds only usages for joystick, gamepad, and mouse:
    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 */
For each matching collection, it looks for the report instance. If found, it gets the maximum number of buttons that can be returned (which it later uses to calculate the amount of memory to be allocated), attaches to the report, requesting extra memory for custom report structure and the buttons, and sets the custom report information. It also tries to set the report idle rate to 0:
/* 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);
    }
}
Next, it looks for sub-collections, iterates through the found one, looking for and attaching report instances as described above:
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);
        }
    }
}
Note:
Notice that the driver doesn't look for sub-sub-collections, which, while allowed by the API, isn't valid.

HID report processing

Once the report is attached and new data is received, libhiddi invokes hid_report() with appropriate parameters. The function immediately retrieves report collection, usage page, usage, and button information data:
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);
Next, it retrieves usage values for HIDD_USAGE_X, HIDD_USAGE_Y, HIDD_USAGE_HAT_SWITCH, and HIDD_USAGE_RZ, and a scaled usage value for HIDD_USAGE_HAT_SWITCH:
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");
Next, it retrieves the list of buttons that were in the pressed state at the time of the report and stores this data in the custom report instance structure, which is persisted between invocations of hid_insertion():
/* 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);
It uses the current and previous button data to determine which buttons were just pressed, held pressed, or released:
/* 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 :");
Finally, it sets the previously button data to current button data to be used in the next invocation:
    /* Exchange prev and current ptrs to save copying data */
    tptr = c_report->pbtnbuf;
    c_report->pbtnbuf = c_report->cbtnbuf;
    c_report->cbtnbuf = tptr;
}

The function prints out appropriate messages, as it process the report information.

HID removal

Once a HID device is removed, libhiddi invokes hid_removal() to detach all reports associated with the device:
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);
}
Page updated: