device.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2011 Vojtech Horky
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00036 #include "virthid.h"
00037 #include <errno.h>
00038 #include <stdio.h>
00039 #include <str.h>
00040 #include <assert.h>
00041 #include <usb/classes/classes.h>
00042 
00043 static int on_data_from_device(usbvirt_device_t *dev,
00044     usb_endpoint_t ep, usb_transfer_type_t tr_type,
00045     void *data, size_t data_size, size_t *actual_size)
00046 {
00047         vuhid_data_t *vuhid = dev->device_data;
00048         vuhid_interface_t *iface = vuhid->in_endpoints_mapping[ep];
00049         if (iface == NULL) {
00050                 return ESTALL;
00051         }
00052 
00053         if (iface->on_data_in == NULL) {
00054                 return EBADCHECKSUM;
00055         }
00056 
00057         return iface->on_data_in(iface, data, data_size, actual_size);
00058 }
00059 
00060 static int on_data_to_device(usbvirt_device_t *dev,
00061     usb_endpoint_t ep, usb_transfer_type_t tr_type,
00062     void *data, size_t data_size)
00063 {
00064         vuhid_data_t *vuhid = dev->device_data;
00065         vuhid_interface_t *iface = vuhid->out_endpoints_mapping[ep];
00066         if (iface == NULL) {
00067                 return ESTALL;
00068         }
00069 
00070         if (iface->on_data_out == NULL) {
00071                 return EBADCHECKSUM;
00072         }
00073 
00074         return iface->on_data_out(iface, data, data_size);
00075 }
00076 
00077 static int interface_life_fibril(void *arg)
00078 {
00079         vuhid_interface_t *iface = arg;
00080         vuhid_data_t *hid_data = iface->vuhid_data;
00081 
00082         if (iface->live != NULL) {
00083                 iface->live(iface);
00084         }
00085 
00086         fibril_mutex_lock(&hid_data->iface_count_mutex);
00087         hid_data->iface_died_count++;
00088         fibril_condvar_broadcast(&hid_data->iface_count_cv);
00089         fibril_mutex_unlock(&hid_data->iface_count_mutex);
00090 
00091         return EOK;
00092 }
00093 
00094 
00095 static vuhid_interface_t *find_interface_by_id(vuhid_interface_t **ifaces,
00096     const char *id)
00097 {
00098         if ((ifaces == NULL) || (id == NULL)) {
00099                 return NULL;
00100         }
00101         while (*ifaces != NULL) {
00102                 if (str_cmp((*ifaces)->id, id) == 0) {
00103                         return *ifaces;
00104                 }
00105                 ifaces++;
00106         }
00107 
00108         return NULL;
00109 }
00110 
00111 int add_interface_by_id(vuhid_interface_t **interfaces, const char *id,
00112     usbvirt_device_t *dev)
00113 {
00114         vuhid_interface_t *iface = find_interface_by_id(interfaces, id);
00115         if (iface == NULL) {
00116                 return ENOENT;
00117         }
00118 
00119         if ((iface->in_data_size == 0) && (iface->out_data_size == 0)) {
00120                 return EEMPTY;
00121         }
00122 
00123         /* Already used interface. */
00124         if (iface->vuhid_data != NULL) {
00125                 return EEXISTS;
00126         }
00127 
00128         vuhid_data_t *hid_data = dev->device_data;
00129 
00130         /* Check that we have not run out of available endpoints. */
00131         if ((iface->in_data_size > 0)
00132             && (hid_data->in_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
00133                 return ELIMIT;
00134         }
00135         if ((iface->out_data_size > 0)
00136             && (hid_data->out_endpoint_first_free >= VUHID_ENDPOINT_MAX)) {
00137                 return ELIMIT;
00138         }
00139 
00140         if (dev->descriptors->configuration->descriptor->interface_count >=
00141             VUHID_INTERFACE_MAX) {
00142                 return ELIMIT;
00143         }
00144 
00145         /*
00146          * How may descriptors we would add?
00147          */
00148         /* Start with interface descriptor. */
00149         size_t new_descriptor_count = 1;
00150         /* Having in/out data size positive means we would need endpoint. */
00151         if (iface->in_data_size > 0) {
00152                 new_descriptor_count++;
00153         }
00154         if (iface->out_data_size > 0) {
00155                 new_descriptor_count++;
00156         }
00157         /* Having report descriptor means adding the HID descriptor. */
00158         if (iface->report_descriptor != NULL) {
00159                 new_descriptor_count++;
00160         }
00161 
00162         /* From now on, in case of errors, we goto to error_leave */
00163         int rc;
00164 
00165         /*
00166          * Prepare new descriptors.
00167          */
00168         size_t descr_count = 0;
00169         size_t total_descr_size = 0;
00170         usb_standard_interface_descriptor_t *descr_iface = NULL;
00171         hid_descriptor_t *descr_hid = NULL;
00172         usb_standard_endpoint_descriptor_t *descr_ep_in = NULL;
00173         usb_standard_endpoint_descriptor_t *descr_ep_out = NULL;
00174 
00175         size_t ep_count = 0;
00176         if (iface->in_data_size > 0) {
00177                 ep_count++;
00178         }
00179         if (iface->out_data_size > 0) {
00180                 ep_count++;
00181         }
00182         assert(ep_count > 0);
00183 
00184         /* Interface would be needed always. */
00185         descr_iface = malloc(sizeof(usb_standard_interface_descriptor_t));
00186         if (descr_iface == NULL) {
00187                 rc = ENOMEM;
00188                 goto error_leave;
00189         }
00190         descr_count++;
00191         total_descr_size += sizeof(usb_standard_interface_descriptor_t);
00192         descr_iface->length = sizeof(usb_standard_interface_descriptor_t);
00193         descr_iface->descriptor_type = USB_DESCTYPE_INTERFACE;
00194         descr_iface->interface_number
00195             = dev->descriptors->configuration->descriptor->interface_count;
00196         descr_iface->alternate_setting = 0;
00197         descr_iface->endpoint_count = ep_count;
00198         descr_iface->interface_class = USB_CLASS_HID;
00199         descr_iface->interface_subclass = iface->usb_subclass;
00200         descr_iface->interface_protocol = iface->usb_protocol;
00201         descr_iface->str_interface = 0;
00202 
00203         /* Create the HID descriptor. */
00204         if (iface->report_descriptor != NULL) {
00205                 descr_hid = malloc(sizeof(hid_descriptor_t));
00206                 if (descr_hid == NULL) {
00207                         rc = ENOMEM;
00208                         goto error_leave;
00209                 }
00210                 descr_count++;
00211                 total_descr_size += sizeof(hid_descriptor_t);
00212                 descr_hid->length = sizeof(hid_descriptor_t);
00213                 descr_hid->type = USB_DESCTYPE_HID;
00214                 descr_hid->hid_spec_release = 0x101;
00215                 descr_hid->country_code = 0;
00216                 descr_hid->descriptor_count = 1;
00217                 descr_hid->descriptor1_type = USB_DESCTYPE_HID_REPORT;
00218                 descr_hid->descriptor1_length = iface->report_descriptor_size;
00219         }
00220 
00221         /* Prepare the endpoint descriptors. */
00222         if (iface->in_data_size > 0) {
00223                 descr_ep_in = malloc(sizeof(usb_standard_endpoint_descriptor_t));
00224                 if (descr_ep_in == NULL) {
00225                         rc = ENOMEM;
00226                         goto error_leave;
00227                 }
00228                 descr_count++;
00229                 total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
00230                 descr_ep_in->length = sizeof(usb_standard_endpoint_descriptor_t);
00231                 descr_ep_in->descriptor_type = USB_DESCTYPE_ENDPOINT;
00232                 descr_ep_in->endpoint_address
00233                     = 0x80 | hid_data->in_endpoint_first_free;
00234                 descr_ep_in->attributes = 3;
00235                 descr_ep_in->max_packet_size = iface->in_data_size;
00236                 descr_ep_in->poll_interval = 10;
00237         }
00238         if (iface->out_data_size > 0) {
00239                 descr_ep_out = malloc(sizeof(usb_standard_endpoint_descriptor_t));
00240                 if (descr_ep_out == NULL) {
00241                         rc = ENOMEM;
00242                         goto error_leave;
00243                 }
00244                 descr_count++;
00245                 total_descr_size += sizeof(usb_standard_endpoint_descriptor_t);
00246                 descr_ep_out->length = sizeof(usb_standard_endpoint_descriptor_t);
00247                 descr_ep_out->descriptor_type = USB_DESCTYPE_ENDPOINT;
00248                 descr_ep_out->endpoint_address
00249                     = 0x00 | hid_data->out_endpoint_first_free;
00250                 descr_ep_out->attributes = 3;
00251                 descr_ep_out->max_packet_size = iface->out_data_size;
00252                 descr_ep_out->poll_interval = 10;
00253         }
00254 
00255         /* Extend existing extra descriptors with these ones. */
00256         usbvirt_device_configuration_extras_t *extra_descriptors
00257             = dev->descriptors->configuration->extra;
00258         extra_descriptors = realloc(extra_descriptors,
00259             sizeof(usbvirt_device_configuration_extras_t)
00260             * (dev->descriptors->configuration->extra_count + descr_count));
00261         if (extra_descriptors == NULL) {
00262                 rc = ENOMEM;
00263                 goto error_leave;
00264         }
00265 
00266         /* Launch the "life" fibril. */
00267         iface->vuhid_data = hid_data;
00268         fid_t life_fibril = fibril_create(interface_life_fibril, iface);
00269         if (life_fibril == 0) {
00270                 rc = ENOMEM;
00271                 goto error_leave;
00272         }
00273 
00274         /* Allocation is okay, we can (actually have to now) overwrite the
00275          * original pointer.
00276          */
00277         dev->descriptors->configuration->extra = extra_descriptors;
00278         extra_descriptors += dev->descriptors->configuration->extra_count;
00279 
00280         /* Initialize them. */
00281 #define _APPEND_DESCRIPTOR(descriptor) \
00282         do { \
00283                 if ((descriptor) != NULL) { \
00284                         extra_descriptors->data = (uint8_t *) (descriptor); \
00285                         extra_descriptors->length = (descriptor)->length; \
00286                         extra_descriptors++; \
00287                 } \
00288         } while (0)
00289 
00290         _APPEND_DESCRIPTOR(descr_iface);
00291         _APPEND_DESCRIPTOR(descr_hid);
00292         _APPEND_DESCRIPTOR(descr_ep_in);
00293         _APPEND_DESCRIPTOR(descr_ep_out);
00294 #undef _APPEND_DESCRIPTOR
00295         dev->descriptors->configuration->extra_count += descr_count;
00296 
00297         /*
00298          * Final changes.
00299          * Increase the necessary counters, make endpoint mapping.
00300          *
00301          */
00302         if (iface->in_data_size > 0) {
00303                 hid_data->in_endpoints_mapping[hid_data->in_endpoint_first_free]
00304                     = iface;
00305                 dev->ops->data_in[hid_data->in_endpoint_first_free]
00306                     = on_data_from_device;
00307                 hid_data->in_endpoint_first_free++;
00308         }
00309         if (iface->out_data_size > 0) {
00310                 hid_data->out_endpoints_mapping[hid_data->out_endpoint_first_free]
00311                     = iface;
00312                 dev->ops->data_out[hid_data->out_endpoint_first_free]
00313                     = on_data_to_device;
00314                 hid_data->out_endpoint_first_free++;
00315         }
00316 
00317         hid_data->interface_mapping[
00318             dev->descriptors->configuration->descriptor->interface_count]
00319             = iface;
00320 
00321         dev->descriptors->configuration->descriptor->interface_count++;
00322         dev->descriptors->configuration->descriptor->total_length
00323             += total_descr_size;
00324 
00325         hid_data->iface_count++;
00326         fibril_add_ready(life_fibril);
00327 
00328         return EOK;
00329 
00330 error_leave:
00331         if (descr_iface != NULL) {
00332                 free(descr_iface);
00333         }
00334         if (descr_hid != NULL) {
00335                 free(descr_hid);
00336         }
00337         if (descr_ep_in != NULL) {
00338                 free(descr_ep_in);
00339         }
00340         if (descr_ep_out != NULL) {
00341                 free(descr_ep_out);
00342         }
00343 
00344         return rc;
00345 }
00346 
00347 void wait_for_interfaces_death(usbvirt_device_t *dev)
00348 {
00349         vuhid_data_t *hid_data = dev->device_data;
00350 
00351         fibril_mutex_lock(&hid_data->iface_count_mutex);
00352         while (hid_data->iface_died_count < hid_data->iface_count) {
00353                 fibril_condvar_wait(&hid_data->iface_count_cv,
00354                     &hid_data->iface_count_mutex);
00355         }
00356         fibril_mutex_unlock(&hid_data->iface_count_mutex);
00357 }
00358 

Generated on Thu Jun 2 07:45:43 2011 for HelenOS/USB by  doxygen 1.4.7