/** Count number of alternate settings of a interface. * * @param config_descr Full configuration descriptor. * @param config_descr_size Size of @p config_descr in bytes. * @param interface_no Interface number. * @return Number of alternate interfaces for @p interface_no interface. */ size_t usb_interface_count_alternates(const uint8_t *config_descr, size_t config_descr_size, uint8_t interface_no) { assert(config_descr != NULL); assert(config_descr_size > 0); const usb_dp_parser_t dp_parser = { .nesting = usb_dp_standard_descriptor_nesting }; const usb_dp_parser_data_t dp_data = { .data = config_descr, .size = config_descr_size, .arg = NULL }; size_t alternate_count = 0; const void *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser, &dp_data, config_descr); while (iface_ptr != NULL) { const usb_standard_interface_descriptor_t *iface = iface_ptr; if (iface->descriptor_type == USB_DESCTYPE_INTERFACE && iface->interface_number == interface_no) { ++alternate_count; } iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data, config_descr, iface_ptr); } return alternate_count; }
static void browse_descriptor_tree_internal(usb_dp_parser_t *parser, usb_dp_parser_data_t *data, const uint8_t *root, size_t depth, dump_descriptor_in_tree_t callback, void *arg) { if (root == NULL) { return; } callback(root, depth, arg); const uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root); do { browse_descriptor_tree_internal(parser, data, child, depth + 1, callback, arg); child = usb_dp_get_sibling_descriptor(parser, data, root, child); } while (child != NULL); }
/** Browser of the descriptor tree. * * @see usb_dp_walk_simple * * @param parser Descriptor parser. * @param data Data for descriptor parser. * @param root Pointer to current root of the tree. * @param depth Current nesting depth. * @param callback Callback for each found descriptor. * @param arg Custom (user) argument. */ static void usb_dp_browse_simple_internal(const usb_dp_parser_t *parser, const usb_dp_parser_data_t *data, const uint8_t *root, size_t depth, void (*callback)(const uint8_t *, size_t, void *), void *arg) { if (root == NULL) { return; } callback(root, depth, arg); const uint8_t *child = usb_dp_get_nested_descriptor(parser, data, root); do { usb_dp_browse_simple_internal(parser, data, child, depth + 1, callback, arg); child = usb_dp_get_sibling_descriptor(parser, data, root, child); } while (child != NULL); }
/** Process endpoint descriptor. * * @param mapping Endpoint mapping list. * @param mapping_count Number of endpoint mappings in @p mapping. * @param interface Interface descriptor under which belongs the @p endpoint. * @param endpoint Endpoint descriptor. * @return Error code. */ static int process_endpoint( usb_endpoint_mapping_t *mapping, size_t mapping_count, usb_standard_interface_descriptor_t *interface, usb_standard_endpoint_descriptor_t *endpoint_desc, usb_dev_session_t *bus_session) { /* * Get endpoint characteristics. */ /* Actual endpoint number is in bits 0..3 */ const usb_endpoint_t ep_no = endpoint_desc->endpoint_address & 0x0F; const usb_endpoint_description_t description = { /* Endpoint direction is set by bit 7 */ .direction = (endpoint_desc->endpoint_address & 128) ? USB_DIRECTION_IN : USB_DIRECTION_OUT, /* Transfer type is in bits 0..2 and * the enum values corresponds 1:1 */ .transfer_type = endpoint_desc->attributes & 3, /* Get interface characteristics. */ .interface_class = interface->interface_class, .interface_subclass = interface->interface_subclass, .interface_protocol = interface->interface_protocol, }; /* * Find the most fitting mapping and initialize the pipe. */ usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping, mapping_count, &description, interface->interface_number, interface->alternate_setting); if (ep_mapping == NULL) { return ENOENT; } if (ep_mapping->present) { return EEXIST; } int rc = usb_pipe_initialize(&ep_mapping->pipe, ep_no, description.transfer_type, ED_MPS_PACKET_SIZE_GET( uint16_usb2host(endpoint_desc->max_packet_size)), description.direction, ED_MPS_TRANS_OPPORTUNITIES_GET( uint16_usb2host(endpoint_desc->max_packet_size)), bus_session); if (rc != EOK) { return rc; } ep_mapping->present = true; ep_mapping->descriptor = endpoint_desc; ep_mapping->interface = interface; return EOK; } /** Process whole USB interface. * * @param mapping Endpoint mapping list. * @param mapping_count Number of endpoint mappings in @p mapping. * @param parser Descriptor parser. * @param parser_data Descriptor parser data. * @param interface_descriptor Interface descriptor. * @return Error code. */ static int process_interface( usb_endpoint_mapping_t *mapping, size_t mapping_count, const usb_dp_parser_t *parser, const usb_dp_parser_data_t *parser_data, const uint8_t *interface_descriptor, usb_dev_session_t *bus_session) { const uint8_t *descriptor = usb_dp_get_nested_descriptor(parser, parser_data, interface_descriptor); if (descriptor == NULL) { return ENOENT; } do { if (is_endpoint_descriptor(descriptor)) { (void) process_endpoint(mapping, mapping_count, (usb_standard_interface_descriptor_t *) interface_descriptor, (usb_standard_endpoint_descriptor_t *) descriptor, bus_session); } descriptor = usb_dp_get_sibling_descriptor(parser, parser_data, interface_descriptor, descriptor); } while (descriptor != NULL); return EOK; } /** Initialize endpoint pipes from configuration descriptor. * * The mapping array is expected to conform to following rules: * - @c pipe must be uninitialized pipe * - @c description must point to prepared endpoint description * - @c descriptor does not need to be initialized (will be overwritten) * - @c interface does not need to be initialized (will be overwritten) * - @c present does not need to be initialized (will be overwritten) * * After processing the configuration descriptor, the mapping is updated * in the following fashion: * - @c present will be set to @c true when the endpoint was found in the * configuration * - @c descriptor will point inside the configuration descriptor to endpoint * corresponding to given description (or NULL for not found descriptor) * - @c interface will point inside the configuration descriptor to interface * descriptor the endpoint @c descriptor belongs to (or NULL for not found * descriptor) * - @c pipe will be initialized when found, otherwise left untouched * - @c description will be untouched under all circumstances * * @param mapping Endpoint mapping list. * @param mapping_count Number of endpoint mappings in @p mapping. * @param configuration_descriptor Full configuration descriptor (is expected * to be in USB endianness: i.e. as-is after being retrieved from * the device). * @param configuration_descriptor_size Size of @p configuration_descriptor * in bytes. * @param connection Connection backing the endpoint pipes. * @return Error code. */ int usb_pipe_initialize_from_configuration( usb_endpoint_mapping_t *mapping, size_t mapping_count, const uint8_t *config_descriptor, size_t config_descriptor_size, usb_dev_session_t *bus_session) { if (config_descriptor == NULL) return EBADMEM; if (config_descriptor_size < sizeof(usb_standard_configuration_descriptor_t)) { return ERANGE; } /* Go through the mapping and set all endpoints to not present. */ for (size_t i = 0; i < mapping_count; i++) { mapping[i].present = false; mapping[i].descriptor = NULL; mapping[i].interface = NULL; } /* Prepare the descriptor parser. */ const usb_dp_parser_t dp_parser = { .nesting = descriptor_nesting }; const usb_dp_parser_data_t dp_data = { .data = config_descriptor, .size = config_descriptor_size, }; /* * Iterate through all interfaces. */ const uint8_t *interface = usb_dp_get_nested_descriptor(&dp_parser, &dp_data, config_descriptor); if (interface == NULL) { return ENOENT; } do { (void) process_interface(mapping, mapping_count, &dp_parser, &dp_data, interface, bus_session); interface = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data, config_descriptor, interface); } while (interface != NULL); return EOK; } /** Probe default control pipe for max packet size. * * The function tries to get the correct value of max packet size several * time before giving up. * * The session on the pipe shall not be started. * * @param pipe Default control pipe. * @return Error code. */ int usb_pipe_probe_default_control(usb_pipe_t *pipe) { assert(pipe); static_assert(DEV_DESCR_MAX_PACKET_SIZE_OFFSET < CTRL_PIPE_MIN_PACKET_SIZE); if ((pipe->direction != USB_DIRECTION_BOTH) || (pipe->transfer_type != USB_TRANSFER_CONTROL) || (pipe->endpoint_no != 0)) { return EINVAL; } uint8_t dev_descr_start[CTRL_PIPE_MIN_PACKET_SIZE]; size_t transferred_size; int rc; for (size_t attempt_var = 0; attempt_var < 3; ++attempt_var) { rc = usb_request_get_descriptor(pipe, USB_REQUEST_TYPE_STANDARD, USB_REQUEST_RECIPIENT_DEVICE, USB_DESCTYPE_DEVICE, 0, 0, dev_descr_start, CTRL_PIPE_MIN_PACKET_SIZE, &transferred_size); if (rc == EOK) { if (transferred_size != CTRL_PIPE_MIN_PACKET_SIZE) { rc = ELIMIT; continue; } break; } } if (rc != EOK) { return rc; } pipe->max_packet_size = dev_descr_start[DEV_DESCR_MAX_PACKET_SIZE_OFFSET]; return EOK; }
/** Initialize alternate interface representation structure. * * @param[in] alternates Pointer to allocated structure. * @param[in] config_descr Configuration descriptor. * @param[in] config_descr_size Size of configuration descriptor. * @param[in] interface_number Interface number. * @return Error code. */ int usb_alternate_interfaces_init(usb_alternate_interfaces_t *alternates, const uint8_t *config_descr, size_t config_descr_size, int interface_number) { assert(alternates != NULL); assert(config_descr != NULL); assert(config_descr_size > 0); alternates->alternatives = NULL; alternates->alternative_count = 0; alternates->current = 0; /* No interfaces. */ if (interface_number < 0) { return EOK; } alternates->alternative_count = usb_interface_count_alternates(config_descr, config_descr_size, interface_number); if (alternates->alternative_count == 0) { return ENOENT; } alternates->alternatives = calloc(alternates->alternative_count, sizeof(usb_alternate_interface_descriptors_t)); if (alternates->alternatives == NULL) { return ENOMEM; } const usb_dp_parser_t dp_parser = { .nesting = usb_dp_standard_descriptor_nesting }; const usb_dp_parser_data_t dp_data = { .data = config_descr, .size = config_descr_size, .arg = NULL }; usb_alternate_interface_descriptors_t *iterator = &alternates->alternatives[0]; const usb_alternate_interface_descriptors_t *end = &alternates->alternatives[alternates->alternative_count]; const void *iface_ptr = usb_dp_get_nested_descriptor(&dp_parser, &dp_data, dp_data.data); while (iface_ptr != NULL && iterator < end) { const usb_standard_interface_descriptor_t *iface = iface_ptr; if ((iface->descriptor_type != USB_DESCTYPE_INTERFACE) || (iface->interface_number != interface_number)) { /* This is not a valid alternate interface descriptor * for interface with number == interface_number. */ iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data, dp_data.data, iface_ptr); continue; } iterator->interface = iface; iterator->nested_descriptors = iface_ptr + sizeof(*iface); /* Find next interface to count size of nested descriptors. */ iface_ptr = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data, dp_data.data, iface_ptr); const uint8_t *next = (iface_ptr == NULL) ? dp_data.data + dp_data.size : iface_ptr; iterator->nested_descriptors_size = next - iterator->nested_descriptors; ++iterator; } return EOK; }