Ignore:
File:
1 edited

Legend:

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

    r3f0ef89d r3b77628  
    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
     41/** Callback for getting host controller handle.
     42 *
     43 * @param dev Device in question.
     44 * @param[out] handle Devman handle of the host controller.
     45 * @return Error code.
     46 */
     47static int usb_iface_get_hc_handle(device_t *dev, devman_handle_t *handle)
     48{
     49        assert(dev);
     50        assert(dev->parent != NULL);
     51
     52        device_t *parent = dev->parent;
     53
     54        if (parent->ops && parent->ops->interfaces[USB_DEV_IFACE]) {
     55                usb_iface_t *usb_iface
     56                    = (usb_iface_t *) parent->ops->interfaces[USB_DEV_IFACE];
     57                assert(usb_iface != NULL);
     58                if (usb_iface->get_hc_handle) {
     59                        int rc = usb_iface->get_hc_handle(parent, handle);
     60                        return rc;
     61                }
     62        }
     63
     64        return ENOTSUP;
     65}
     66
     67static usb_iface_t usb_iface = {
     68        .get_hc_handle = usb_iface_get_hc_handle
     69};
     70
     71static device_ops_t child_ops = {
     72        .interfaces[USB_DEV_IFACE] = &usb_iface
    5173};
    5274
     
    113135}
    114136
    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 
    238137/** Create DDF match ids from USB device descriptor.
    239138 *
     
    242141 * @return Error code.
    243142 */
    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 {
     143int usb_drv_create_match_ids_from_device_descriptor(
     144    match_id_list_t *matches,
     145    const usb_standard_device_descriptor_t *device_descriptor)
     146{
     147        int rc;
     148       
    248149        /*
    249150         * Unless the vendor id is 0, the pair idVendor-idProduct
     
    252153        if (device_descriptor->vendor_id != 0) {
    253154                /* First, with release number. */
    254                 ADD_MATCHID_OR_RETURN(matches, 100,
    255                     "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
     155                rc = usb_add_match_id(matches, 100,
     156                    "usb&vendor=%d&product=%d&release=" BCD_FMT,
    256157                    (int) device_descriptor->vendor_id,
    257158                    (int) device_descriptor->product_id,
    258159                    BCD_ARGS(device_descriptor->device_version));
     160                if (rc != EOK) {
     161                        return rc;
     162                }
    259163               
    260164                /* Next, without release number. */
    261                 ADD_MATCHID_OR_RETURN(matches, 90,
    262                     "usb&vendor=0x%04x&product=0x%04x",
     165                rc = usb_add_match_id(matches, 90, "usb&vendor=%d&product=%d",
    263166                    (int) device_descriptor->vendor_id,
    264167                    (int) device_descriptor->product_id);
     168                if (rc != EOK) {
     169                        return rc;
     170                }
    265171        }       
    266172
    267173        /*
    268174         * If the device class points to interface we skip adding
    269          * class directly but we add a multi interface device.
     175         * class directly.
    270176         */
    271177        if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
    272                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
     178                rc = usb_add_match_id(matches, 50, "usb&class=%s",
    273179                    usb_str_class(device_descriptor->device_class));
    274         } else {
    275                 ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
     180                if (rc != EOK) {
     181                        return rc;
     182                }
    276183        }
    277184       
     
    279186}
    280187
     188/** Create DDF match ids from USB configuration descriptor.
     189 * The configuration descriptor is expected to be in the complete form,
     190 * i.e. including interface, endpoint etc. descriptors.
     191 *
     192 * @param matches List of match ids to extend.
     193 * @param config_descriptor Configuration descriptor returned by given device.
     194 * @param total_size Size of the @p config_descriptor.
     195 * @return Error code.
     196 */
     197int usb_drv_create_match_ids_from_configuration_descriptor(
     198    match_id_list_t *matches,
     199    const void *config_descriptor, size_t total_size)
     200{
     201        /*
     202         * Iterate through config descriptor to find the interface
     203         * descriptors.
     204         */
     205        size_t position = sizeof(usb_standard_configuration_descriptor_t);
     206        while (position + 1 < total_size) {
     207                uint8_t *current_descriptor
     208                    = ((uint8_t *) config_descriptor) + position;
     209                uint8_t cur_descr_len = current_descriptor[0];
     210                uint8_t cur_descr_type = current_descriptor[1];
     211
     212                if (cur_descr_len == 0) {
     213                        return ENOENT;
     214                }
     215               
     216                position += cur_descr_len;
     217               
     218                if (cur_descr_type != USB_DESCTYPE_INTERFACE) {
     219                        continue;
     220                }
     221               
     222                /*
     223                 * Finally, we found an interface descriptor.
     224                 */
     225                usb_standard_interface_descriptor_t *interface
     226                    = (usb_standard_interface_descriptor_t *)
     227                    current_descriptor;
     228               
     229                int rc = usb_add_match_id(matches, 50,
     230                    "usb&interface&class=%s",
     231                    usb_str_class(interface->interface_class));
     232                if (rc != EOK) {
     233                        return rc;
     234                }
     235        }
     236       
     237        return EOK;
     238}
     239
     240/** Add match ids based on configuration descriptor.
     241 *
     242 * @param hc Open phone to host controller.
     243 * @param matches Match ids list to add matches to.
     244 * @param address USB address of the attached device.
     245 * @param config_count Number of configurations the device has.
     246 * @return Error code.
     247 */
     248static int usb_add_config_descriptor_match_ids(int hc,
     249    match_id_list_t *matches, usb_address_t address,
     250    int config_count)
     251{
     252        int final_rc = EOK;
     253       
     254        int config_index;
     255        for (config_index = 0; config_index < config_count; config_index++) {
     256                int rc;
     257                usb_standard_configuration_descriptor_t config_descriptor;
     258                rc = usb_drv_req_get_bare_configuration_descriptor(hc,
     259                    address,  config_index, &config_descriptor);
     260                if (rc != EOK) {
     261                        final_rc = rc;
     262                        continue;
     263                }
     264
     265                size_t full_config_descriptor_size;
     266                void *full_config_descriptor
     267                    = malloc(config_descriptor.total_length);
     268                rc = usb_drv_req_get_full_configuration_descriptor(hc,
     269                    address, config_index,
     270                    full_config_descriptor, config_descriptor.total_length,
     271                    &full_config_descriptor_size);
     272                if (rc != EOK) {
     273                        final_rc = rc;
     274                        continue;
     275                }
     276                if (full_config_descriptor_size
     277                    != config_descriptor.total_length) {
     278                        final_rc = ERANGE;
     279                        continue;
     280                }
     281               
     282                rc = usb_drv_create_match_ids_from_configuration_descriptor(
     283                    matches,
     284                    full_config_descriptor, full_config_descriptor_size);
     285                if (rc != EOK) {
     286                        final_rc = rc;
     287                        continue;
     288                }
     289               
     290        }
     291       
     292        return final_rc;
     293}
    281294
    282295/** Create match ids describing attached device.
     
    285298 * function exits with error.
    286299 *
    287  * @param ctrl_pipe Control pipe to given device (session must be already
    288  *      started).
     300 * @param hc Open phone to host controller.
    289301 * @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)
     302 * @param address USB address of the attached device.
     303 * @return Error code.
     304 */
     305int usb_drv_create_device_match_ids(int hc, match_id_list_t *matches,
     306    usb_address_t address)
    294307{
    295308        int rc;
     309       
    296310        /*
    297311         * Retrieve device descriptor and add matches from it.
     
    299313        usb_standard_device_descriptor_t device_descriptor;
    300314
    301         rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
     315        rc = usb_drv_req_get_device_descriptor(hc, address,
     316            &device_descriptor);
    302317        if (rc != EOK) {
    303318                return rc;
    304319        }
    305 
    306         rc = usb_device_create_match_ids_from_device_descriptor(
    307             &device_descriptor, matches);
     320       
     321        rc = usb_drv_create_match_ids_from_device_descriptor(matches,
     322            &device_descriptor);
    308323        if (rc != EOK) {
    309324                return rc;
    310325        }
     326       
     327        /*
     328         * Go through all configurations and add matches
     329         * based on interface class.
     330         */
     331        rc = usb_add_config_descriptor_match_ids(hc, matches,
     332            address, device_descriptor.configuration_count);
     333        if (rc != EOK) {
     334                return rc;
     335        }
     336
     337        /*
     338         * As a fallback, provide the simplest match id possible.
     339         */
     340        rc = usb_add_match_id(matches, 1, "usb&fallback");
     341        if (rc != EOK) {
     342                return rc;
     343        }
    311344
    312345        return EOK;
    313346}
    314347
     348
    315349/** Probe for device kind and register it in devman.
    316350 *
     351 * @param[in] hc Open phone to the host controller.
     352 * @param[in] parent Parent device.
    317353 * @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.
    320354 * @param[out] child_handle Handle of the child device.
    321355 * @return Error code.
    322356 */
    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;
     357int usb_drv_register_child_in_devman(int hc, device_t *parent,
     358    usb_address_t address, devman_handle_t *child_handle)
     359{
     360        static size_t device_name_index = 0;
     361
     362        device_t *child = NULL;
    336363        char *child_name = NULL;
    337364        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) {
     365
     366        child = create_device();
     367        if (child == NULL) {
     368                rc = ENOMEM;
    349369                goto failure;
    350370        }
     
    354374         * naming etc., something more descriptive could be created.
    355375         */
    356         rc = asprintf(&child_name, "usbdev%02zu", this_device_name_index);
     376        rc = asprintf(&child_name, "usbdev%02zu", device_name_index);
    357377        if (rc < 0) {
    358378                goto failure;
    359379        }
    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);
     380        child->parent = parent;
     381        child->name = child_name;
     382        child->ops = &child_ops;
     383       
     384        rc = usb_drv_create_device_match_ids(hc, &child->match_ids, address);
     385        if (rc != EOK) {
     386                goto failure;
     387        }
     388
     389        rc = child_device_register(child, parent);
    391390        if (rc != EOK) {
    392391                goto failure;
     
    396395                *child_handle = child->handle;
    397396        }
    398 
    399         if (child_fun != NULL) {
    400                 *child_fun = child;
    401         }
     397       
     398        device_name_index++;
    402399
    403400        return EOK;
     
    407404                child->name = NULL;
    408405                /* This takes care of match_id deallocation as well. */
    409                 ddf_fun_destroy(child);
     406                delete_device(child);
    410407        }
    411408        if (child_name != NULL) {
     
    414411
    415412        return rc;
     413
    416414}
    417415
Note: See TracChangeset for help on using the changeset viewer.