Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/usb/src/recognise.c

    r3f0ef89d rb207803  
    2727 */
    2828
    29 /** @addtogroup libusb
     29/** @addtogroup libusb usb
    3030 * @{
    3131 */
     
    3333 * @brief Functions for recognising kind of attached devices.
    3434 */
    35 #include <sys/types.h>
    36 #include <fibril_synch.h>
    37 #include <usb/pipes.h>
    38 #include <usb/recognise.h>
    39 #include <usb/ddfiface.h>
    40 #include <usb/request.h>
     35#include <usb_iface.h>
     36#include <usb/usbdrv.h>
    4137#include <usb/classes/classes.h>
    4238#include <stdio.h>
    4339#include <errno.h>
    44 #include <assert.h>
    45 
    46 static size_t device_name_index = 0;
    47 static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
    48 
    49 ddf_dev_ops_t child_ops = {
    50         .interfaces[USB_DEV_IFACE] = &usb_iface_hub_child_impl
     40
     41static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
     42{
     43        assert(dev);
     44        assert(dev->parent != NULL);
     45
     46        device_t *parent = dev->parent;
     47
     48        if (parent->ops && parent->ops->interfaces[USB_DEV_IFACE]) {
     49                usb_iface_t *usb_iface
     50                    = (usb_iface_t *) parent->ops->interfaces[USB_DEV_IFACE];
     51                assert(usb_iface != NULL);
     52                if (usb_iface->get_hc_handle) {
     53                        int rc = usb_iface->get_hc_handle(parent, handle);
     54                        return rc;
     55                }
     56        }
     57
     58        return ENOTSUP;
     59}
     60
     61static usb_iface_t usb_iface = {
     62        .get_hc_handle = usb_iface_get_hc_handle
     63};
     64
     65static device_ops_t child_ops = {
     66        .interfaces[USB_DEV_IFACE] = &usb_iface
    5167};
    5268
     
    113129}
    114130
    115 #define ADD_MATCHID_OR_RETURN(match_ids, score, format, ...) \
    116         do { \
    117                 int __rc = usb_add_match_id((match_ids), (score), \
    118                     format, ##__VA_ARGS__); \
    119                 if (__rc != EOK) { \
    120                         return __rc; \
    121                 } \
    122         } while (0)
    123 
    124 /** Create device match ids based on its interface.
    125  *
    126  * @param[in] descriptor Interface descriptor.
    127  * @param[out] matches Initialized list of match ids.
    128  * @return Error code (the two mentioned are not the only ones).
    129  * @retval EINVAL Invalid input parameters (expects non NULL pointers).
    130  * @retval ENOENT Interface does not specify class.
    131  */
    132 int usb_device_create_match_ids_from_interface(
    133     const usb_standard_device_descriptor_t *desc_device,
    134     const usb_standard_interface_descriptor_t *desc_interface,
    135     match_id_list_t *matches)
    136 {
    137         if (desc_interface == NULL) {
    138                 return EINVAL;
    139         }
    140         if (matches == NULL) {
    141                 return EINVAL;
    142         }
    143 
    144         if (desc_interface->interface_class == USB_CLASS_USE_INTERFACE) {
    145                 return ENOENT;
    146         }
    147 
    148         const char *classname = usb_str_class(desc_interface->interface_class);
    149         assert(classname != NULL);
    150 
    151 #define IFACE_PROTOCOL_FMT "interface&class=%s&subclass=0x%02x&protocol=0x%02x"
    152 #define IFACE_PROTOCOL_ARGS classname, desc_interface->interface_subclass, \
    153     desc_interface->interface_protocol
    154 
    155 #define IFACE_SUBCLASS_FMT "interface&class=%s&subclass=0x%02x"
    156 #define IFACE_SUBCLASS_ARGS classname, desc_interface->interface_subclass
    157 
    158 #define IFACE_CLASS_FMT "interface&class=%s"
    159 #define IFACE_CLASS_ARGS classname
    160 
    161 #define VENDOR_RELEASE_FMT "vendor=0x%04x&product=0x%04x&release=" BCD_FMT
    162 #define VENDOR_RELEASE_ARGS desc_device->vendor_id, desc_device->product_id, \
    163     BCD_ARGS(desc_device->device_version)
    164 
    165 #define VENDOR_PRODUCT_FMT "vendor=0x%04x&product=0x%04x"
    166 #define VENDOR_PRODUCT_ARGS desc_device->vendor_id, desc_device->product_id
    167 
    168 #define VENDOR_ONLY_FMT "vendor=0x%04x"
    169 #define VENDOR_ONLY_ARGS desc_device->vendor_id
    170 
    171         /*
    172          * If the vendor is specified, create match ids with vendor with
    173          * higher score.
    174          * Then the same ones without the vendor part.
    175          */
    176         if ((desc_device != NULL) && (desc_device->vendor_id != 0)) {
    177                 /* First, interface matches with device release number. */
    178                 ADD_MATCHID_OR_RETURN(matches, 250,
    179                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_PROTOCOL_FMT,
    180                     VENDOR_RELEASE_ARGS, IFACE_PROTOCOL_ARGS);
    181                 ADD_MATCHID_OR_RETURN(matches, 240,
    182                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_SUBCLASS_FMT,
    183                     VENDOR_RELEASE_ARGS, IFACE_SUBCLASS_ARGS);
    184                 ADD_MATCHID_OR_RETURN(matches, 230,
    185                     "usb&" VENDOR_RELEASE_FMT "&" IFACE_CLASS_FMT,
    186                     VENDOR_RELEASE_ARGS, IFACE_CLASS_ARGS);
    187 
    188                 /* Next, interface matches without release number. */
    189                 ADD_MATCHID_OR_RETURN(matches, 220,
    190                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_PROTOCOL_FMT,
    191                     VENDOR_PRODUCT_ARGS, IFACE_PROTOCOL_ARGS);
    192                 ADD_MATCHID_OR_RETURN(matches, 210,
    193                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_SUBCLASS_FMT,
    194                     VENDOR_PRODUCT_ARGS, IFACE_SUBCLASS_ARGS);
    195                 ADD_MATCHID_OR_RETURN(matches, 200,
    196                     "usb&" VENDOR_PRODUCT_FMT "&" IFACE_CLASS_FMT,
    197                     VENDOR_PRODUCT_ARGS, IFACE_CLASS_ARGS);
    198 
    199                 /* Finally, interface matches with only vendor. */
    200                 ADD_MATCHID_OR_RETURN(matches, 190,
    201                     "usb&" VENDOR_ONLY_FMT "&" IFACE_PROTOCOL_FMT,
    202                     VENDOR_ONLY_ARGS, IFACE_PROTOCOL_ARGS);
    203                 ADD_MATCHID_OR_RETURN(matches, 180,
    204                     "usb&" VENDOR_ONLY_FMT "&" IFACE_SUBCLASS_FMT,
    205                     VENDOR_ONLY_ARGS, IFACE_SUBCLASS_ARGS);
    206                 ADD_MATCHID_OR_RETURN(matches, 170,
    207                     "usb&" VENDOR_ONLY_FMT "&" IFACE_CLASS_FMT,
    208                     VENDOR_ONLY_ARGS, IFACE_CLASS_ARGS);
    209         }
    210 
    211         /* Now, the same but without any vendor specification. */
    212         ADD_MATCHID_OR_RETURN(matches, 160,
    213             "usb&" IFACE_PROTOCOL_FMT,
    214             IFACE_PROTOCOL_ARGS);
    215         ADD_MATCHID_OR_RETURN(matches, 150,
    216             "usb&" IFACE_SUBCLASS_FMT,
    217             IFACE_SUBCLASS_ARGS);
    218         ADD_MATCHID_OR_RETURN(matches, 140,
    219             "usb&" IFACE_CLASS_FMT,
    220             IFACE_CLASS_ARGS);
    221 
    222 #undef IFACE_PROTOCOL_FMT
    223 #undef IFACE_PROTOCOL_ARGS
    224 #undef IFACE_SUBCLASS_FMT
    225 #undef IFACE_SUBCLASS_ARGS
    226 #undef IFACE_CLASS_FMT
    227 #undef IFACE_CLASS_ARGS
    228 #undef VENDOR_RELEASE_FMT
    229 #undef VENDOR_RELEASE_ARGS
    230 #undef VENDOR_PRODUCT_FMT
    231 #undef VENDOR_PRODUCT_ARGS
    232 #undef VENDOR_ONLY_FMT
    233 #undef VENDOR_ONLY_ARGS
    234 
    235         return EOK;
    236 }
    237 
    238131/** Create DDF match ids from USB device descriptor.
    239132 *
     
    242135 * @return Error code.
    243136 */
    244 int usb_device_create_match_ids_from_device_descriptor(
    245     const usb_standard_device_descriptor_t *device_descriptor,
    246     match_id_list_t *matches)
    247 {
     137int usb_drv_create_match_ids_from_device_descriptor(
     138    match_id_list_t *matches,
     139    const usb_standard_device_descriptor_t *device_descriptor)
     140{
     141        int rc;
     142       
    248143        /*
    249144         * Unless the vendor id is 0, the pair idVendor-idProduct
     
    252147        if (device_descriptor->vendor_id != 0) {
    253148                /* First, with release number. */
    254                 ADD_MATCHID_OR_RETURN(matches, 100,
    255                     "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
     149                rc = usb_add_match_id(matches, 100,
     150                    "usb&vendor=%d&product=%d&release=" BCD_FMT,
    256151                    (int) device_descriptor->vendor_id,
    257152                    (int) device_descriptor->product_id,
    258153                    BCD_ARGS(device_descriptor->device_version));
     154                if (rc != EOK) {
     155                        return rc;
     156                }
    259157               
    260158                /* Next, without release number. */
    261                 ADD_MATCHID_OR_RETURN(matches, 90,
    262                     "usb&vendor=0x%04x&product=0x%04x",
     159                rc = usb_add_match_id(matches, 90, "usb&vendor=%d&product=%d",
    263160                    (int) device_descriptor->vendor_id,
    264161                    (int) device_descriptor->product_id);
     162                if (rc != EOK) {
     163                        return rc;
     164                }
    265165        }       
    266166
    267167        /*
    268168         * If the device class points to interface we skip adding
    269          * class directly but we add a multi interface device.
     169         * class directly.
    270170         */
    271171        if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
    272                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
     172                rc = usb_add_match_id(matches, 50, "usb&class=%s",
    273173                    usb_str_class(device_descriptor->device_class));
    274         } else {
    275                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
     174                if (rc != EOK) {
     175                        return rc;
     176                }
    276177        }
    277178       
     
    279180}
    280181
     182/** Create DDF match ids from USB configuration descriptor.
     183 * The configuration descriptor is expected to be in the complete form,
     184 * i.e. including interface, endpoint etc. descriptors.
     185 *
     186 * @param matches List of match ids to extend.
     187 * @param config_descriptor Configuration descriptor returned by given device.
     188 * @param total_size Size of the @p config_descriptor.
     189 * @return Error code.
     190 */
     191int usb_drv_create_match_ids_from_configuration_descriptor(
     192    match_id_list_t *matches,
     193    const void *config_descriptor, size_t total_size)
     194{
     195        /*
     196         * Iterate through config descriptor to find the interface
     197         * descriptors.
     198         */
     199        size_t position = sizeof(usb_standard_configuration_descriptor_t);
     200        while (position + 1 < total_size) {
     201                uint8_t *current_descriptor
     202                    = ((uint8_t *) config_descriptor) + position;
     203                uint8_t cur_descr_len = current_descriptor[0];
     204                uint8_t cur_descr_type = current_descriptor[1];
     205               
     206                position += cur_descr_len;
     207               
     208                if (cur_descr_type != USB_DESCTYPE_INTERFACE) {
     209                        continue;
     210                }
     211               
     212                /*
     213                 * Finally, we found an interface descriptor.
     214                 */
     215                usb_standard_interface_descriptor_t *interface
     216                    = (usb_standard_interface_descriptor_t *)
     217                    current_descriptor;
     218               
     219                int rc = usb_add_match_id(matches, 50,
     220                    "usb&interface&class=%s",
     221                    usb_str_class(interface->interface_class));
     222                if (rc != EOK) {
     223                        return rc;
     224                }
     225        }
     226       
     227        return EOK;
     228}
     229
     230/** Add match ids based on configuration descriptor.
     231 *
     232 * @param hc Open phone to host controller.
     233 * @param matches Match ids list to add matches to.
     234 * @param address USB address of the attached device.
     235 * @return Error code.
     236 */
     237static int usb_add_config_descriptor_match_ids(int hc,
     238    match_id_list_t *matches, usb_address_t address,
     239    int config_count)
     240{
     241        int final_rc = EOK;
     242       
     243        int config_index;
     244        for (config_index = 0; config_index < config_count; config_index++) {
     245                int rc;
     246                usb_standard_configuration_descriptor_t config_descriptor;
     247                rc = usb_drv_req_get_bare_configuration_descriptor(hc,
     248                    address,  config_index, &config_descriptor);
     249                if (rc != EOK) {
     250                        final_rc = rc;
     251                        continue;
     252                }
     253
     254                size_t full_config_descriptor_size;
     255                void *full_config_descriptor
     256                    = malloc(config_descriptor.total_length);
     257                rc = usb_drv_req_get_full_configuration_descriptor(hc,
     258                    address, config_index,
     259                    full_config_descriptor, config_descriptor.total_length,
     260                    &full_config_descriptor_size);
     261                if (rc != EOK) {
     262                        final_rc = rc;
     263                        continue;
     264                }
     265                if (full_config_descriptor_size
     266                    != config_descriptor.total_length) {
     267                        final_rc = ERANGE;
     268                        continue;
     269                }
     270               
     271                rc = usb_drv_create_match_ids_from_configuration_descriptor(
     272                    matches,
     273                    full_config_descriptor, full_config_descriptor_size);
     274                if (rc != EOK) {
     275                        final_rc = rc;
     276                        continue;
     277                }
     278               
     279        }
     280       
     281        return final_rc;
     282}
    281283
    282284/** Create match ids describing attached device.
     
    285287 * function exits with error.
    286288 *
    287  * @param ctrl_pipe Control pipe to given device (session must be already
    288  *      started).
     289 * @param hc Open phone to host controller.
    289290 * @param matches Initialized list of match ids.
    290  * @return Error code.
    291  */
    292 int usb_device_create_match_ids(usb_endpoint_pipe_t *ctrl_pipe,
    293     match_id_list_t *matches)
     291 * @param address USB address of the attached device.
     292 * @return Error code.
     293 */
     294int usb_drv_create_device_match_ids(int hc, match_id_list_t *matches,
     295    usb_address_t address)
    294296{
    295297        int rc;
     298       
    296299        /*
    297300         * Retrieve device descriptor and add matches from it.
     
    299302        usb_standard_device_descriptor_t device_descriptor;
    300303
    301         rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
     304        rc = usb_drv_req_get_device_descriptor(hc, address,
     305            &device_descriptor);
    302306        if (rc != EOK) {
    303307                return rc;
    304308        }
    305 
    306         rc = usb_device_create_match_ids_from_device_descriptor(
    307             &device_descriptor, matches);
     309       
     310        rc = usb_drv_create_match_ids_from_device_descriptor(matches,
     311            &device_descriptor);
    308312        if (rc != EOK) {
    309313                return rc;
    310314        }
     315       
     316        /*
     317         * Go through all configurations and add matches
     318         * based on interface class.
     319         */
     320        rc = usb_add_config_descriptor_match_ids(hc, matches,
     321            address, device_descriptor.configuration_count);
     322        if (rc != EOK) {
     323                return rc;
     324        }
     325
     326        /*
     327         * As a fallback, provide the simplest match id possible.
     328         */
     329        rc = usb_add_match_id(matches, 1, "usb&fallback");
     330        if (rc != EOK) {
     331                return rc;
     332        }
    311333
    312334        return EOK;
    313335}
    314336
     337
    315338/** Probe for device kind and register it in devman.
    316339 *
    317  * @param[in] address Address of the (unknown) attached device.
    318  * @param[in] hc_handle Handle of the host controller.
    319  * @param[in] parent Parent device.
    320  * @param[out] child_handle Handle of the child device.
    321  * @return Error code.
    322  */
    323 int usb_device_register_child_in_devman(usb_address_t address,
    324     devman_handle_t hc_handle,
    325     ddf_dev_t *parent, devman_handle_t *child_handle,
    326     ddf_dev_ops_t *dev_ops, void *dev_data, ddf_fun_t **child_fun)
    327 {
    328         size_t this_device_name_index;
    329 
    330         fibril_mutex_lock(&device_name_index_mutex);
    331         this_device_name_index = device_name_index;
    332         device_name_index++;
    333         fibril_mutex_unlock(&device_name_index_mutex);
    334 
    335         ddf_fun_t *child = NULL;
     340 * @param hc Open phone to the host controller.
     341 * @param parent Parent device.
     342 * @param address Address of the (unknown) attached device.
     343 * @return Error code.
     344 */
     345int usb_drv_register_child_in_devman(int hc, device_t *parent,
     346    usb_address_t address, devman_handle_t *child_handle)
     347{
     348        static size_t device_name_index = 0;
     349
     350        device_t *child = NULL;
    336351        char *child_name = NULL;
    337352        int rc;
    338         usb_device_connection_t dev_connection;
    339         usb_endpoint_pipe_t ctrl_pipe;
    340 
    341         rc = usb_device_connection_initialize(&dev_connection, hc_handle, address);
    342         if (rc != EOK) {
    343                 goto failure;
    344         }
    345 
    346         rc = usb_endpoint_pipe_initialize_default_control(&ctrl_pipe,
    347             &dev_connection);
    348         if (rc != EOK) {
     353
     354        child = create_device();
     355        if (child == NULL) {
     356                rc = ENOMEM;
    349357                goto failure;
    350358        }
     
    354362         * naming etc., something more descriptive could be created.
    355363         */
    356         rc = asprintf(&child_name, "usbdev%02zu", this_device_name_index);
     364        rc = asprintf(&child_name, "usbdev%02zu", device_name_index);
    357365        if (rc < 0) {
    358366                goto failure;
    359367        }
    360 
    361         child = ddf_fun_create(parent, fun_inner, child_name);
    362         if (child == NULL) {
    363                 rc = ENOMEM;
    364                 goto failure;
    365         }
    366 
    367         if (dev_ops != NULL) {
    368                 child->ops = dev_ops;
    369         } else {
    370                 child->ops = &child_ops;
    371         }
    372 
    373         child->driver_data = dev_data;
    374 
    375         rc = usb_endpoint_pipe_start_session(&ctrl_pipe);
    376         if (rc != EOK) {
    377                 goto failure;
    378         }
    379 
    380         rc = usb_device_create_match_ids(&ctrl_pipe, &child->match_ids);
    381         if (rc != EOK) {
    382                 goto failure;
    383         }
    384 
    385         rc = usb_endpoint_pipe_end_session(&ctrl_pipe);
    386         if (rc != EOK) {
    387                 goto failure;
    388         }
    389 
    390         rc = ddf_fun_bind(child);
     368        child->parent = parent;
     369        child->name = child_name;
     370        child->ops = &child_ops;
     371       
     372        rc = usb_drv_create_device_match_ids(hc, &child->match_ids, address);
     373        if (rc != EOK) {
     374                goto failure;
     375        }
     376
     377        rc = child_device_register(child, parent);
    391378        if (rc != EOK) {
    392379                goto failure;
     
    396383                *child_handle = child->handle;
    397384        }
    398 
    399         if (child_fun != NULL) {
    400                 *child_fun = child;
    401         }
     385       
     386        device_name_index++;
    402387
    403388        return EOK;
     
    407392                child->name = NULL;
    408393                /* This takes care of match_id deallocation as well. */
    409                 ddf_fun_destroy(child);
     394                delete_device(child);
    410395        }
    411396        if (child_name != NULL) {
     
    414399
    415400        return rc;
     401
    416402}
    417403
Note: See TracChangeset for help on using the changeset viewer.