HelenOS sources
This source file includes following definitions.
- on_data_from_device
- on_data_to_device
- interface_life_fibril
- find_interface_by_id
- add_interface_by_id
- wait_for_interfaces_death
#include "virthid.h"
#include <errno.h>
#include <stdio.h>
#include <str.h>
#include <assert.h>
#include <usb/classes/classes.h>
static errno_t on_data_from_device(usbvirt_device_t *dev,
usb_endpoint_t ep, usb_transfer_type_t tr_type,
void *data, size_t data_size, size_t *actual_size)
{
vuhid_data_t *vuhid = dev->device_data;
vuhid_interface_t *iface = vuhid->in_endpoints_mapping[ep];
if (iface == NULL) {
return ESTALL;
}
if (iface->on_data_in == NULL) {
return EBADCHECKSUM;
}
return iface->on_data_in(iface, data, data_size, actual_size);
}
static errno_t on_data_to_device(usbvirt_device_t *dev,
usb_endpoint_t ep, usb_transfer_type_t tr_type,
const void *data, size_t data_size)
{
vuhid_data_t *vuhid = dev->device_data;
vuhid_interface_t *iface = vuhid->out_endpoints_mapping[ep];
if (iface == NULL) {
return ESTALL;
}
if (iface->on_data_out == NULL) {
return EBADCHECKSUM;
}
return iface->on_data_out(iface, data, data_size);
}
static errno_t interface_life_fibril(void *arg)
{
vuhid_interface_t *iface = arg;
vuhid_data_t *hid_data = iface->vuhid_data;
if (iface->live != NULL) {
iface->live(iface);
}
fibril_mutex_lock(&hid_data->iface_count_mutex);
hid_data->iface_died_count++;
fibril_condvar_broadcast(&hid_data->iface_count_cv);
fibril_mutex_unlock(&hid_data->iface_count_mutex);
return EOK;
}
static vuhid_interface_t *find_interface_by_id(vuhid_interface_t **ifaces,
const char *id)
{
if ((ifaces == NULL) || (id == NULL)) {
return NULL;
}
while (*ifaces != NULL) {
if (str_cmp((*ifaces)->id, id) == 0) {
return *ifaces;
}
ifaces++;
}
return NULL;
}
errno_t add_interface_by_id(vuhid_interface_t **interfaces, const char *id,
usbvirt_device_t *dev)
{
vuhid_interface_t *iface = find_interface_by_id(interfaces, id);
if (iface == NULL) {
return ENOENT;
}
if ((iface->in_data_size == 0) && (iface->out_data_size == 0)) {
return EEMPTY;
}
if (iface->vuhid_data != NULL) {
return EEXIST;
}
vuhid_data_t *hid_data = dev->device_data;
if ((iface->in_data_size > 0) &&
(hid_data->in_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
return ELIMIT;
}
if ((iface->out_data_size > 0) &&
(hid_data->out_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
return ELIMIT;
}
if (dev->descriptors->configuration->descriptor->interface_count >=
VUHID_INTERFACE_MAX) {
return ELIMIT;
}
size_t new_descriptor_count = 1;
if (iface->in_data_size > 0) {
new_descriptor_count++;
}
if (iface->out_data_size > 0) {
new_descriptor_count++;
}
if (iface->report_descriptor != NULL) {
new_descriptor_count++;
}
errno_t rc;
size_t descr_count = 0;
size_t total_descr_size = 0;
usb_standard_interface_descriptor_t *descr_iface = NULL;
hid_descriptor_t *descr_hid = NULL;
usb_standard_endpoint_descriptor_t *descr_ep_in = NULL;
usb_standard_endpoint_descriptor_t *descr_ep_out = NULL;
size_t ep_count = 0;
if (iface->in_data_size > 0) {
ep_count++;
}
if (iface->out_data_size > 0) {
ep_count++;
}
assert(ep_count > 0);
descr_iface = malloc(sizeof(usb_standard_interface_descriptor_t));
if (descr_iface == NULL) {
rc = ENOMEM;
goto error_leave;
}
descr_count++;
total_descr_size += sizeof(usb_standard_interface_descriptor_t);
descr_iface->length = sizeof(usb_standard_interface_descriptor_t);
descr_iface->descriptor_type = USB_DESCTYPE_INTERFACE;
descr_iface->interface_number =
dev->descriptors->configuration->descriptor->interface_count;
descr_iface->alternate_setting = 0;
descr_iface->endpoint_count = ep_count;
descr_iface->interface_class = USB_CLASS_HID;
descr_iface->interface_subclass = iface->usb_subclass;
descr_iface->interface_protocol = iface->usb_protocol;
descr_iface->str_interface = 0;
if (iface->report_descriptor != NULL) {
descr_hid = malloc(sizeof(hid_descriptor_t));
if (descr_hid == NULL) {
rc = ENOMEM;
goto error_leave;
}
descr_count++;
total_descr_size += sizeof(hid_descriptor_t);
descr_hid->length = sizeof(hid_descriptor_t);
descr_hid->type = USB_DESCTYPE_HID;
descr_hid->hid_spec_release = 0x101;
descr_hid->country_code = 0;
descr_hid->descriptor_count = 1;
descr_hid->descriptor1_type = USB_DESCTYPE_HID_REPORT;
descr_hid->descriptor1_length = iface->report_descriptor_size;
}
if (iface->in_data_size > 0) {
descr_ep_in = malloc(sizeof(usb_standard_endpoint_descriptor_t));
if (descr_ep_in == NULL) {
rc = ENOMEM;
goto error_leave;
}
descr_count++;
total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
descr_ep_in->length = sizeof(usb_standard_endpoint_descriptor_t);
descr_ep_in->descriptor_type = USB_DESCTYPE_ENDPOINT;
descr_ep_in->endpoint_address =
0x80 | hid_data->in_endpoint_first_free;
descr_ep_in->attributes = 3;
descr_ep_in->max_packet_size = iface->in_data_size;
descr_ep_in->poll_interval = 10;
}
if (iface->out_data_size > 0) {
descr_ep_out = malloc(sizeof(usb_standard_endpoint_descriptor_t));
if (descr_ep_out == NULL) {
rc = ENOMEM;
goto error_leave;
}
descr_count++;
total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
descr_ep_out->length = sizeof(usb_standard_endpoint_descriptor_t);
descr_ep_out->descriptor_type = USB_DESCTYPE_ENDPOINT;
descr_ep_out->endpoint_address =
0x00 | hid_data->out_endpoint_first_free;
descr_ep_out->attributes = 3;
descr_ep_out->max_packet_size = iface->out_data_size;
descr_ep_out->poll_interval = 10;
}
usbvirt_device_configuration_extras_t *extra_descriptors;
extra_descriptors = realloc(dev->descriptors->configuration->extra,
sizeof(usbvirt_device_configuration_extras_t) *
(dev->descriptors->configuration->extra_count + descr_count));
if (extra_descriptors == NULL) {
rc = ENOMEM;
goto error_leave;
}
iface->vuhid_data = hid_data;
fid_t life_fibril = fibril_create(interface_life_fibril, iface);
if (life_fibril == 0) {
rc = ENOMEM;
goto error_leave;
}
dev->descriptors->configuration->extra = extra_descriptors;
extra_descriptors += dev->descriptors->configuration->extra_count;
#define _APPEND_DESCRIPTOR(descriptor) \
do { \
if ((descriptor) != NULL) { \
extra_descriptors->data = (uint8_t *) (descriptor); \
extra_descriptors->length = (descriptor)->length; \
extra_descriptors++; \
} \
} while (0)
_APPEND_DESCRIPTOR(descr_iface);
_APPEND_DESCRIPTOR(descr_hid);
_APPEND_DESCRIPTOR(descr_ep_in);
_APPEND_DESCRIPTOR(descr_ep_out);
#undef _APPEND_DESCRIPTOR
dev->descriptors->configuration->extra_count += descr_count;
if (iface->in_data_size > 0) {
hid_data->in_endpoints_mapping[hid_data->in_endpoint_first_free] =
iface;
dev->ops->data_in[hid_data->in_endpoint_first_free] =
on_data_from_device;
hid_data->in_endpoint_first_free++;
}
if (iface->out_data_size > 0) {
hid_data->out_endpoints_mapping[hid_data->out_endpoint_first_free] =
iface;
dev->ops->data_out[hid_data->out_endpoint_first_free] =
on_data_to_device;
hid_data->out_endpoint_first_free++;
}
hid_data->interface_mapping[dev->descriptors->configuration->descriptor->interface_count] =
iface;
dev->descriptors->configuration->descriptor->interface_count++;
dev->descriptors->configuration->descriptor->total_length +=
total_descr_size;
hid_data->iface_count++;
fibril_add_ready(life_fibril);
return EOK;
error_leave:
if (descr_iface != NULL) {
free(descr_iface);
}
if (descr_hid != NULL) {
free(descr_hid);
}
if (descr_ep_in != NULL) {
free(descr_ep_in);
}
if (descr_ep_out != NULL) {
free(descr_ep_out);
}
return rc;
}
void wait_for_interfaces_death(usbvirt_device_t *dev)
{
vuhid_data_t *hid_data = dev->device_data;
fibril_mutex_lock(&hid_data->iface_count_mutex);
while (hid_data->iface_died_count < hid_data->iface_count) {
fibril_condvar_wait(&hid_data->iface_count_cv,
&hid_data->iface_count_mutex);
}
fibril_mutex_unlock(&hid_data->iface_count_mutex);
}
HelenOS homepage, sources at GitHub