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:
