Ignore:
File:
1 edited

Legend:

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

    r3b77628 r3f0ef89d  
    3333 * @brief Functions for recognising kind of attached devices.
    3434 */
    35 #include <usb_iface.h>
    36 #include <usb/usbdrv.h>
     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>
    3741#include <usb/classes/classes.h>
    3842#include <stdio.h>
    3943#include <errno.h>
    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  */
    47 static 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 
    67 static usb_iface_t usb_iface = {
    68         .get_hc_handle = usb_iface_get_hc_handle
    69 };
    70 
    71 static device_ops_t child_ops = {
    72         .interfaces[USB_DEV_IFACE] = &usb_iface
     44#include <assert.h>
     45
     46static size_t device_name_index = 0;
     47static FIBRIL_MUTEX_INITIALIZE(device_name_index_mutex);
     48
     49ddf_dev_ops_t child_ops = {
     50        .interfaces[USB_DEV_IFACE] = &usb_iface_hub_child_impl
    7351};
    7452
     
    135113}
    136114
     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 */
     132int 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
    137238/** Create DDF match ids from USB device descriptor.
    138239 *
     
    141242 * @return Error code.
    142243 */
    143 int usb_drv_create_match_ids_from_device_descriptor(
    144     match_id_list_t *matches,
    145     const usb_standard_device_descriptor_t *device_descriptor)
     244int usb_device_create_match_ids_from_device_descriptor(
     245    const usb_standard_device_descriptor_t *device_descriptor,
     246    match_id_list_t *matches)
    146247{
    147         int rc;
    148        
    149248        /*
    150249         * Unless the vendor id is 0, the pair idVendor-idProduct
     
    153252        if (device_descriptor->vendor_id != 0) {
    154253                /* First, with release number. */
    155                 rc = usb_add_match_id(matches, 100,
    156                     "usb&vendor=%d&product=%d&release=" BCD_FMT,
     254                ADD_MATCHID_OR_RETURN(matches, 100,
     255                    "usb&vendor=0x%04x&product=0x%04x&release=" BCD_FMT,
    157256                    (int) device_descriptor->vendor_id,
    158257                    (int) device_descriptor->product_id,
    159258                    BCD_ARGS(device_descriptor->device_version));
    160                 if (rc != EOK) {
    161                         return rc;
    162                 }
    163259               
    164260                /* Next, without release number. */
    165                 rc = usb_add_match_id(matches, 90, "usb&vendor=%d&product=%d",
     261                ADD_MATCHID_OR_RETURN(matches, 90,
     262                    "usb&vendor=0x%04x&product=0x%04x",
    166263                    (int) device_descriptor->vendor_id,
    167264                    (int) device_descriptor->product_id);
    168                 if (rc != EOK) {
    169                         return rc;
    170                 }
    171265        }       
    172266
    173267        /*
    174268         * If the device class points to interface we skip adding
    175          * class directly.
     269         * class directly but we add a multi interface device.
    176270         */
    177271        if (device_descriptor->device_class != USB_CLASS_USE_INTERFACE) {
    178                 rc = usb_add_match_id(matches, 50, "usb&class=%s",
     272                ADD_MATCHID_OR_RETURN(matches, 50, "usb&class=%s",
    179273                    usb_str_class(device_descriptor->device_class));
    180                 if (rc != EOK) {
    181                         return rc;
    182                 }
     274        } else {
     275                ADD_MATCHID_OR_RETURN(matches, 50, "usb&mid");
    183276        }
    184277       
     
    186279}
    187280
    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.
     281
     282/** Create match ids describing attached device.
     283 *
     284 * @warning The list of match ids @p matches may change even when
     285 * function exits with error.
     286 *
     287 * @param ctrl_pipe Control pipe to given device (session must be already
     288 *      started).
     289 * @param matches Initialized list of match ids.
    195290 * @return Error code.
    196291 */
    197 int usb_drv_create_match_ids_from_configuration_descriptor(
    198     match_id_list_t *matches,
    199     const void *config_descriptor, size_t total_size)
     292int usb_device_create_match_ids(usb_endpoint_pipe_t *ctrl_pipe,
     293    match_id_list_t *matches)
    200294{
    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        
     295        int rc;
     296        /*
     297         * Retrieve device descriptor and add matches from it.
     298         */
     299        usb_standard_device_descriptor_t device_descriptor;
     300
     301        rc = usb_request_get_device_descriptor(ctrl_pipe, &device_descriptor);
     302        if (rc != EOK) {
     303                return rc;
     304        }
     305
     306        rc = usb_device_create_match_ids_from_device_descriptor(
     307            &device_descriptor, matches);
     308        if (rc != EOK) {
     309                return rc;
     310        }
     311
    237312        return EOK;
    238313}
    239314
    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  */
    248 static 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 }
    294 
    295 /** Create match ids describing attached device.
    296  *
    297  * @warning The list of match ids @p matches may change even when
    298  * function exits with error.
    299  *
    300  * @param hc Open phone to host controller.
    301  * @param matches Initialized list of match ids.
    302  * @param address USB address of the attached device.
    303  * @return Error code.
    304  */
    305 int usb_drv_create_device_match_ids(int hc, match_id_list_t *matches,
    306     usb_address_t address)
    307 {
    308         int rc;
    309        
    310         /*
    311          * Retrieve device descriptor and add matches from it.
    312          */
    313         usb_standard_device_descriptor_t device_descriptor;
    314 
    315         rc = usb_drv_req_get_device_descriptor(hc, address,
    316             &device_descriptor);
    317         if (rc != EOK) {
    318                 return rc;
    319         }
    320        
    321         rc = usb_drv_create_match_ids_from_device_descriptor(matches,
    322             &device_descriptor);
    323         if (rc != EOK) {
    324                 return rc;
    325         }
    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         }
    344 
    345         return EOK;
    346 }
    347 
    348 
    349315/** Probe for device kind and register it in devman.
    350316 *
    351  * @param[in] hc Open phone to the host controller.
     317 * @param[in] address Address of the (unknown) attached device.
     318 * @param[in] hc_handle Handle of the host controller.
    352319 * @param[in] parent Parent device.
    353  * @param[in] address Address of the (unknown) attached device.
    354320 * @param[out] child_handle Handle of the child device.
    355321 * @return Error code.
    356322 */
    357 int usb_drv_register_child_in_devman(int hc, device_t *parent,
    358     usb_address_t address, devman_handle_t *child_handle)
     323int 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)
    359327{
    360         static size_t device_name_index = 0;
    361 
    362         device_t *child = NULL;
     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;
    363336        char *child_name = NULL;
    364337        int rc;
    365 
    366         child = create_device();
     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) {
     349                goto failure;
     350        }
     351
     352        /*
     353         * TODO: Once the device driver framework support persistent
     354         * naming etc., something more descriptive could be created.
     355         */
     356        rc = asprintf(&child_name, "usbdev%02zu", this_device_name_index);
     357        if (rc < 0) {
     358                goto failure;
     359        }
     360
     361        child = ddf_fun_create(parent, fun_inner, child_name);
    367362        if (child == NULL) {
    368363                rc = ENOMEM;
     
    370365        }
    371366
    372         /*
    373          * TODO: Once the device driver framework support persistent
    374          * naming etc., something more descriptive could be created.
    375          */
    376         rc = asprintf(&child_name, "usbdev%02zu", device_name_index);
    377         if (rc < 0) {
    378                 goto failure;
    379         }
    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);
     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);
    390391        if (rc != EOK) {
    391392                goto failure;
     
    395396                *child_handle = child->handle;
    396397        }
    397        
    398         device_name_index++;
     398
     399        if (child_fun != NULL) {
     400                *child_fun = child;
     401        }
    399402
    400403        return EOK;
     
    404407                child->name = NULL;
    405408                /* This takes care of match_id deallocation as well. */
    406                 delete_device(child);
     409                ddf_fun_destroy(child);
    407410        }
    408411        if (child_name != NULL) {
     
    411414
    412415        return rc;
    413 
    414416}
    415417
Note: See TracChangeset for help on using the changeset viewer.