recognise.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2010 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 
00035 #include <sys/types.h>
00036 #include <fibril_synch.h>
00037 #include <usb/dev/pipes.h>
00038 #include <usb/dev/recognise.h>
00039 #include <usb/ddfiface.h>
00040 #include <usb/dev/request.h>
00041 #include <usb/classes/classes.h>
00042 #include <stdio.h>
00043 #include <errno.h>
00044 #include <assert.h>
00045 
00047 static size_t device_name_index = 0;
00049 static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
00050 
00052 ddf_dev_ops_t child_ops = {
00053         .interfaces[USB_DEV_IFACE] = &usb_iface_hub_child_impl
00054 };
00055 
00057 #define BCD_INT(a) (((unsigned int)(a)) / 256)
00058 
00059 #define BCD_FRAC(a) (((unsigned int)(a)) % 256)
00060 
00062 #define BCD_FMT "%x.%x"
00063 
00064 #define BCD_ARGS(a) BCD_INT((a)), BCD_FRAC((a))
00065 
00066 /* FIXME: make this dynamic */
00067 #define MATCH_STRING_MAX 256
00068 
00076 static int usb_add_match_id(match_id_list_t *matches, int score,
00077     const char *format, ...)
00078 {
00079         char *match_str = NULL;
00080         match_id_t *match_id = NULL;
00081         int rc;
00082         
00083         match_str = malloc(MATCH_STRING_MAX + 1);
00084         if (match_str == NULL) {
00085                 rc = ENOMEM;
00086                 goto failure;
00087         }
00088 
00089         /*
00090          * FIXME: replace with dynamic allocation of exact size
00091          */
00092         va_list args;
00093         va_start(args, format   );
00094         vsnprintf(match_str, MATCH_STRING_MAX, format, args);
00095         match_str[MATCH_STRING_MAX] = 0;
00096         va_end(args);
00097 
00098         match_id = create_match_id();
00099         if (match_id == NULL) {
00100                 rc = ENOMEM;
00101                 goto failure;
00102         }
00103 
00104         match_id->id = match_str;
00105         match_id->score = score;
00106         add_match_id(matches, match_id);
00107 
00108         return EOK;
00109         
00110 failure:
00111         if (match_str != NULL) {
00112                 free(match_str);
00113         }
00114         if (match_id != NULL) {
00115                 match_id->id = NULL;
00116                 delete_match_id(match_id);
00117         }
00118         
00119         return rc;
00120 }
00121 
00129 #define ADD_MATCHID_OR_RETURN(match_ids, score, format, ...) \
00130         do { \
00131                 int __rc = usb_add_match_id((match_ids), (score), \
00132                     format, ##__VA_ARGS__); \
00133                 if (__rc != EOK) { \
00134                         return __rc; \
00135                 } \
00136         } while (0)
00137 
00147 int usb_device_create_match_ids_from_interface(
00148     const usb_standard_device_descriptor_t *desc_device,
00149     const usb_standard_interface_descriptor_t *desc_interface,
00150     match_id_list_t *matches)
00151 {
00152         if (desc_interface == NULL) {
00153                 return EINVAL;
00154         }
00155         if (matches == NULL) {
00156                 return EINVAL;
00157         }
00158 
00159         if (desc_interface->interface_class == USB_CLASS_USE_INTERFACE) {
00160                 return ENOENT;
00161         }
00162 
00163         const char *classname = usb_str_class(desc_interface->interface_class);
00164         assert(classname != NULL);
00165 
00166 #define IFACE_PROTOCOL_FMT "interface&class=%s&subclass=0x%02x&protocol=0x%02x"
00167 #define IFACE_PROTOCOL_ARGS classname, desc_interface->interface_subclass, \
00168     desc_interface->interface_protocol
00169 
00170 #define IFACE_SUBCLASS_FMT "interface&class=%s&subclass=0x%02x"
00171 #define IFACE_SUBCLASS_ARGS classname, desc_interface->interface_subclass
00172 
00173 #define IFACE_CLASS_FMT "interface&class=%s"
00174 #define IFACE_CLASS_ARGS classname
00175 
00176 #define VENDOR_RELEASE_FMT "vendor=0x%04x&product=0x%04x&release=" BCD_FMT
00177 #define VENDOR_RELEASE_ARGS desc_device->vendor_id, desc_device->product_id, \
00178     BCD_ARGS(desc_device->device_version)
00179 
00180 #define VENDOR_PRODUCT_FMT "vendor=0x%04x&product=0x%04x"
00181 #define VENDOR_PRODUCT_ARGS desc_device->vendor_id, desc_device->product_id
00182 
00183 #define VENDOR_ONLY_FMT "vendor=0x%04x"
00184 #define VENDOR_ONLY_ARGS desc_device->vendor_id
00185 
00186         /*
00187          * If the vendor is specified, create match ids with vendor with
00188          * higher score.
00189          * Then the same ones without the vendor part.
00190          */
00191         if ((desc_device != NULL) && (desc_device->vendor_id != 0)) {
00192                 /* First, interface matches with device release number. */
00193                 ADD_MATCHID_OR_RETURN(matches, 250,
00194                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_PROTOCOL_FMT,
00195                     VENDOR_RELEASE_ARGS, IFACE_PROTOCOL_ARGS);
00196                 ADD_MATCHID_OR_RETURN(matches, 240,
00197                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_SUBCLASS_FMT,
00198                     VENDOR_RELEASE_ARGS, IFACE_SUBCLASS_ARGS);
00199                 ADD_MATCHID_OR_RETURN(matches, 230,
00200                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_CLASS_FMT,
00201                     VENDOR_RELEASE_ARGS, IFACE_CLASS_ARGS);
00202 
00203                 /* Next, interface matches without release number. */
00204                 ADD_MATCHID_OR_RETURN(matches, 220,
00205                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_PROTOCOL_FMT,
00206                     VENDOR_PRODUCT_ARGS, IFACE_PROTOCOL_ARGS);
00207                 ADD_MATCHID_OR_RETURN(matches, 210,
00208                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_SUBCLASS_FMT,
00209                     VENDOR_PRODUCT_ARGS, IFACE_SUBCLASS_ARGS);
00210                 ADD_MATCHID_OR_RETURN(matches, 200,
00211                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_CLASS_FMT,
00212                     VENDOR_PRODUCT_ARGS, IFACE_CLASS_ARGS);
00213 
00214                 /* Finally, interface matches with only vendor. */
00215                 ADD_MATCHID_OR_RETURN(matches, 190,
00216                     "usb&" VENDOR_ONLY_FMT "&" IFACE_PROTOCOL_FMT,
00217                     VENDOR_ONLY_ARGS, IFACE_PROTOCOL_ARGS);
00218                 ADD_MATCHID_OR_RETURN(matches, 180,
00219                     "usb&" VENDOR_ONLY_FMT "&" IFACE_SUBCLASS_FMT,
00220                     VENDOR_ONLY_ARGS, IFACE_SUBCLASS_ARGS);
00221                 ADD_MATCHID_OR_RETURN(matches, 170,
00222                     "usb&" VENDOR_ONLY_FMT "&" IFACE_CLASS_FMT,
00223                     VENDOR_ONLY_ARGS, IFACE_CLASS_ARGS);
00224         }
00225 
00226         /* Now, the same but without any vendor specification. */
00227         ADD_MATCHID_OR_RETURN(matches, 160,
00228             "usb&" IFACE_PROTOCOL_FMT,
00229             IFACE_PROTOCOL_ARGS);
00230         ADD_MATCHID_OR_RETURN(matches, 150,
00231             "usb&" IFACE_SUBCLASS_FMT,
00232             IFACE_SUBCLASS_ARGS);
00233         ADD_MATCHID_OR_RETURN(matches, 140,
00234             "usb&" IFACE_CLASS_FMT,
00235             IFACE_CLASS_ARGS);
00236 
00237 #undef IFACE_PROTOCOL_FMT
00238 #undef IFACE_PROTOCOL_ARGS
00239 #undef IFACE_SUBCLASS_FMT
00240 #undef IFACE_SUBCLASS_ARGS
00241 #undef IFACE_CLASS_FMT
00242 #undef IFACE_CLASS_ARGS
00243 #undef VENDOR_RELEASE_FMT
00244 #undef VENDOR_RELEASE_ARGS
00245 #undef VENDOR_PRODUCT_FMT
00246 #undef VENDOR_PRODUCT_ARGS
00247 #undef VENDOR_ONLY_FMT
00248 #undef VENDOR_ONLY_ARGS
00249 
00250         /* As a last resort, try fallback driver. */
00251         ADD_MATCHID_OR_RETURN(matches, 10, "usb&interface&fallback");
00252 
00253         return EOK;
00254 }
00255 
00262 int usb_device_create_match_ids_from_device_descriptor(
00263     const usb_standard_device_descriptor_t *device_descriptor,
00264     match_id_list_t *matches)
00265 {
00266         /*
00267          * Unless the vendor id is 0, the pair idVendor-idProduct
00268          * quite uniquely describes the device.
00269          */
00270         if (device_descriptor->vendor_id != 0) {
00271                 /* First, with release number. */
00272                 ADD_MATCHID_OR_RETURN(matches, 100,
00273                     "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
00274                     (int) device_descriptor->vendor_id,
00275                     (int) device_descriptor->product_id,
00276                     BCD_ARGS(device_descriptor->device_version));
00277                 
00278                 /* Next, without release number. */
00279                 ADD_MATCHID_OR_RETURN(matches, 90,
00280                     "usb&vendor=0x%04x&product=0x%04x",
00281                     (int) device_descriptor->vendor_id,
00282                     (int) device_descriptor->product_id);
00283         }       
00284 
00285         /*
00286          * If the device class points to interface we skip adding
00287          * class directly but we add a multi interface device.
00288          */
00289         if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
00290                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
00291                     usb_str_class(device_descriptor->device_class));
00292         } else {
00293                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
00294         }
00295         
00296         /* As a last resort, try fallback driver. */
00297         ADD_MATCHID_OR_RETURN(matches, 10, "usb&fallback");
00298 
00299         return EOK;
00300 }
00301 
00302 
00313 int usb_device_create_match_ids(usb_pipe_t *ctrl_pipe,
00314     match_id_list_t *matches)
00315 {
00316         int rc;
00317         /*
00318          * Retrieve device descriptor and add matches from it.
00319          */
00320         usb_standard_device_descriptor_t device_descriptor;
00321 
00322         rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
00323         if (rc != EOK) {
00324                 return rc;
00325         }
00326 
00327         rc = usb_device_create_match_ids_from_device_descriptor(
00328             &device_descriptor, matches);
00329         if (rc != EOK) {
00330                 return rc;
00331         }
00332 
00333         return EOK;
00334 }
00335 
00349 int usb_device_register_child_in_devman(usb_address_t address,
00350     devman_handle_t hc_handle,
00351     ddf_dev_t *parent, devman_handle_t *child_handle,
00352     ddf_dev_ops_t *dev_ops, void *dev_data, ddf_fun_t **child_fun)
00353 {
00354         size_t this_device_name_index;
00355 
00356         fibril_mutex_lock(&device_name_index_mutex);
00357         this_device_name_index = device_name_index;
00358         device_name_index++;
00359         fibril_mutex_unlock(&device_name_index_mutex);
00360 
00361         ddf_fun_t *child = NULL;
00362         char *child_name = NULL;
00363         int rc;
00364         usb_device_connection_t dev_connection;
00365         usb_pipe_t ctrl_pipe;
00366 
00367         rc = usb_device_connection_initialize(&dev_connection, hc_handle, address);
00368         if (rc != EOK) {
00369                 goto failure;
00370         }
00371 
00372         rc = usb_pipe_initialize_default_control(&ctrl_pipe,
00373             &dev_connection);
00374         if (rc != EOK) {
00375                 goto failure;
00376         }
00377         rc = usb_pipe_probe_default_control(&ctrl_pipe);
00378         if (rc != EOK) {
00379                 goto failure;
00380         }
00381 
00382         /*
00383          * TODO: Once the device driver framework support persistent
00384          * naming etc., something more descriptive could be created.
00385          */
00386         rc = asprintf(&child_name, "usb%02zu_a%d",
00387             this_device_name_index, address);
00388         if (rc < 0) {
00389                 goto failure;
00390         }
00391 
00392         child = ddf_fun_create(parent, fun_inner, child_name);
00393         if (child == NULL) {
00394                 rc = ENOMEM;
00395                 goto failure;
00396         }
00397 
00398         if (dev_ops != NULL) {
00399                 child->ops = dev_ops;
00400         } else {
00401                 child->ops = &child_ops;
00402         }
00403 
00404         child->driver_data = dev_data;
00405 
00406         rc = usb_device_create_match_ids(&ctrl_pipe, &child->match_ids);
00407         if (rc != EOK) {
00408                 goto failure;
00409         }
00410 
00411         rc = ddf_fun_bind(child);
00412         if (rc != EOK) {
00413                 goto failure;
00414         }
00415 
00416         if (child_handle != NULL) {
00417                 *child_handle = child->handle;
00418         }
00419 
00420         if (child_fun != NULL) {
00421                 *child_fun = child;
00422         }
00423 
00424         return EOK;
00425 
00426 failure:
00427         if (child != NULL) {
00428                 child->name = NULL;
00429                 /* This takes care of match_id deallocation as well. */
00430                 ddf_fun_destroy(child);
00431         }
00432         if (child_name != NULL) {
00433                 free(child_name);
00434         }
00435 
00436         return rc;
00437 }
00438 
00439 

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