source: mainline/uspace/lib/usbdev/src/pipesinit.c@ 89c7d7d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 89c7d7d was 9efad54, checked in by Ondřej Hlavatý <aearsis@…>, 7 years ago

usb: move endpoint descriptor parsing to HC

This better separates responsibilities. Now the device driver does not
care about the contents of an endpoint descriptor, and HC can parse the
values according to device's actual speed.

Currently, it is device driver's responsibility to fetch endpoint
descriptors, map them and register pipes - sending the endpoint
descriptor back to HC. HC then parses it, and fills the pipe
description, which then sends back to device driver. We shall probably
fetch the endpoint descriptor from inside the HC (also fixing the USB
spec violation of communication with EP0).

  • Property mode set to 100644
File size: 10.8 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbdev
30 * @{
31 */
32/** @file
33 * Non trivial initialization of endpoint pipes.
34 *
35 */
36#include <usb/dev/pipes.h>
37#include <usb/dev/dp.h>
38#include <usb/dev/request.h>
39#include <usb/usb.h>
40#include <usb/debug.h>
41#include <usb/descriptor.h>
42
43#include <assert.h>
44#include <errno.h>
45
46#define DEV_DESCR_MAX_PACKET_SIZE_OFFSET 7
47
48#define NESTING(parentname, childname) \
49 { \
50 .child = USB_DESCTYPE_##childname, \
51 .parent = USB_DESCTYPE_##parentname, \
52 }
53#define LAST_NESTING { -1, -1 }
54
55/** Nesting pairs of standard descriptors. */
56static const usb_dp_descriptor_nesting_t descriptor_nesting[] = {
57 NESTING(CONFIGURATION, INTERFACE),
58 NESTING(INTERFACE, ENDPOINT),
59 NESTING(INTERFACE, HUB),
60 NESTING(INTERFACE, HID),
61 NESTING(HID, HID_REPORT),
62 NESTING(ENDPOINT, SSPEED_EP_COMPANION),
63 LAST_NESTING
64};
65
66/** Tells whether given descriptor is of endpoint type.
67 *
68 * @param descriptor Descriptor in question.
69 * @return Whether the given descriptor is endpoint descriptor.
70 */
71static inline bool is_endpoint_descriptor(const uint8_t *descriptor)
72{
73 return descriptor[1] == USB_DESCTYPE_ENDPOINT;
74}
75
76/** Tells whether given descriptor is of superspeed companion type.
77 *
78 * @param descriptor Descriptor in question.
79 * @return Whether the given descriptor is superspeed companion descriptor.
80 */
81static inline bool is_superspeed_companion_descriptor(const uint8_t *descriptor)
82{
83 return descriptor[1] == USB_DESCTYPE_SSPEED_EP_COMPANION;
84}
85
86/** Tells whether found endpoint corresponds to endpoint described by user.
87 *
88 * @param wanted Endpoint description as entered by driver author.
89 * @param found Endpoint description obtained from endpoint descriptor.
90 * @return Whether the @p found descriptor fits the @p wanted descriptor.
91 */
92static bool endpoint_fits_description(const usb_endpoint_description_t *wanted,
93 const usb_endpoint_description_t *found)
94{
95#define _SAME(fieldname) ((wanted->fieldname) == (found->fieldname))
96
97 if (!_SAME(direction)) {
98 return false;
99 }
100
101 if (!_SAME(transfer_type)) {
102 return false;
103 }
104
105 if ((wanted->interface_class >= 0) && !_SAME(interface_class)) {
106 return false;
107 }
108
109 if ((wanted->interface_subclass >= 0) && !_SAME(interface_subclass)) {
110 return false;
111 }
112
113 if ((wanted->interface_protocol >= 0) && !_SAME(interface_protocol)) {
114 return false;
115 }
116
117#undef _SAME
118
119 return true;
120}
121
122/** Find endpoint mapping for a found endpoint.
123 *
124 * @param mapping Endpoint mapping list.
125 * @param mapping_count Number of endpoint mappings in @p mapping.
126 * @param found_endpoint Description of found endpoint.
127 * @param interface_number Number of currently processed interface.
128 * @return Endpoint mapping corresponding to @p found_endpoint.
129 * @retval NULL No corresponding endpoint found.
130 */
131static usb_endpoint_mapping_t *find_endpoint_mapping(
132 usb_endpoint_mapping_t *mapping, size_t mapping_count,
133 const usb_endpoint_description_t *found_endpoint,
134 int interface_number, int interface_setting)
135{
136 while (mapping_count > 0) {
137 bool interface_number_fits = (mapping->interface_no < 0)
138 || (mapping->interface_no == interface_number);
139
140 bool interface_setting_fits = (mapping->interface_setting < 0)
141 || (mapping->interface_setting == interface_setting);
142
143 bool endpoint_descriptions_fits = endpoint_fits_description(
144 mapping->description, found_endpoint);
145
146 if (interface_number_fits
147 && interface_setting_fits
148 && endpoint_descriptions_fits) {
149 return mapping;
150 }
151
152 mapping++;
153 mapping_count--;
154 }
155 return NULL;
156}
157
158/** Process endpoint descriptor.
159 *
160 * @param mapping Endpoint mapping list.
161 * @param mapping_count Number of endpoint mappings in @p mapping.
162 * @param interface Interface descriptor under which belongs the @p endpoint.
163 * @param endpoint Endpoint descriptor.
164 * @param companion Superspeed companion descriptor.
165 * @return Error code.
166 */
167static int process_endpoint(
168 usb_endpoint_mapping_t *mapping, size_t mapping_count,
169 usb_standard_interface_descriptor_t *interface,
170 usb_standard_endpoint_descriptor_t *endpoint_desc,
171 usb_superspeed_endpoint_companion_descriptor_t *companion_desc,
172 usb_dev_session_t *bus_session)
173{
174
175 /*
176 * Get endpoint characteristics.
177 */
178 const usb_endpoint_description_t description = {
179 .transfer_type = USB_ED_GET_TRANSFER_TYPE(*endpoint_desc),
180 .direction = USB_ED_GET_DIR(*endpoint_desc),
181
182 /* Get interface characteristics. */
183 .interface_class = interface->interface_class,
184 .interface_subclass = interface->interface_subclass,
185 .interface_protocol = interface->interface_protocol,
186 };
187
188 /*
189 * Find the most fitting mapping and initialize the pipe.
190 */
191 usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping,
192 mapping_count, &description,
193 interface->interface_number, interface->alternate_setting);
194 if (ep_mapping == NULL) {
195 return ENOENT;
196 }
197
198 if (ep_mapping->present) {
199 return EEXIST;
200 }
201
202 int err = usb_pipe_initialize(&ep_mapping->pipe, bus_session, description.transfer_type);
203 if (err)
204 return err;
205
206 ep_mapping->present = true;
207 ep_mapping->descriptor = endpoint_desc;
208 ep_mapping->companion_descriptor = companion_desc;
209 ep_mapping->interface = interface;
210
211 return EOK;
212}
213
214/** Process whole USB interface.
215 *
216 * @param mapping Endpoint mapping list.
217 * @param mapping_count Number of endpoint mappings in @p mapping.
218 * @param parser Descriptor parser.
219 * @param parser_data Descriptor parser data.
220 * @param interface_descriptor Interface descriptor.
221 * @return Error code.
222 */
223static int process_interface(
224 usb_endpoint_mapping_t *mapping, size_t mapping_count,
225 const usb_dp_parser_t *parser, const usb_dp_parser_data_t *parser_data,
226 const uint8_t *interface_descriptor, usb_dev_session_t *bus_session)
227{
228 const uint8_t *descriptor = usb_dp_get_nested_descriptor(parser,
229 parser_data, interface_descriptor);
230
231 if (descriptor == NULL) {
232 return ENOENT;
233 }
234
235 do {
236 if (is_endpoint_descriptor(descriptor)) {
237 /* Check if companion descriptor is present too, it should immediatelly follow. */
238 const uint8_t *companion_desc = usb_dp_get_nested_descriptor(parser,
239 parser_data, descriptor);
240 if (companion_desc && !is_superspeed_companion_descriptor(companion_desc)) {
241 /* Not what we wanted, don't pass it further. */
242 companion_desc = NULL;
243 }
244
245 (void) process_endpoint(mapping, mapping_count,
246 (usb_standard_interface_descriptor_t *)
247 interface_descriptor,
248 (usb_standard_endpoint_descriptor_t *)
249 descriptor,
250 (usb_superspeed_endpoint_companion_descriptor_t *)
251 companion_desc,
252 bus_session);
253 }
254
255 descriptor = usb_dp_get_sibling_descriptor(parser, parser_data,
256 interface_descriptor, descriptor);
257 } while (descriptor != NULL);
258
259 return EOK;
260}
261
262/** Initialize endpoint pipes from configuration descriptor.
263 *
264 * The mapping array is expected to conform to following rules:
265 * - @c pipe must be uninitialized pipe
266 * - @c description must point to prepared endpoint description
267 * - @c descriptor does not need to be initialized (will be overwritten)
268 * - @c interface does not need to be initialized (will be overwritten)
269 * - @c present does not need to be initialized (will be overwritten)
270 *
271 * After processing the configuration descriptor, the mapping is updated
272 * in the following fashion:
273 * - @c present will be set to @c true when the endpoint was found in the
274 * configuration
275 * - @c descriptor will point inside the configuration descriptor to endpoint
276 * corresponding to given description (or NULL for not found descriptor)
277 * - @c interface will point inside the configuration descriptor to interface
278 * descriptor the endpoint @c descriptor belongs to (or NULL for not found
279 * descriptor)
280 * - @c pipe will be initialized when found, otherwise left untouched
281 * - @c description will be untouched under all circumstances
282 *
283 * @param mapping Endpoint mapping list.
284 * @param mapping_count Number of endpoint mappings in @p mapping.
285 * @param configuration_descriptor Full configuration descriptor (is expected
286 * to be in USB endianness: i.e. as-is after being retrieved from
287 * the device).
288 * @param configuration_descriptor_size Size of @p configuration_descriptor
289 * in bytes.
290 * @param connection Connection backing the endpoint pipes.
291 * @return Error code.
292 */
293int usb_pipe_initialize_from_configuration(
294 usb_endpoint_mapping_t *mapping, size_t mapping_count,
295 const uint8_t *config_descriptor, size_t config_descriptor_size,
296 usb_dev_session_t *bus_session)
297{
298 if (config_descriptor == NULL)
299 return EBADMEM;
300
301 if (config_descriptor_size <
302 sizeof(usb_standard_configuration_descriptor_t)) {
303 return ERANGE;
304 }
305
306 /* Go through the mapping and set all endpoints to not present. */
307 for (size_t i = 0; i < mapping_count; i++) {
308 mapping[i].present = false;
309 mapping[i].descriptor = NULL;
310 mapping[i].interface = NULL;
311 }
312
313 /* Prepare the descriptor parser. */
314 const usb_dp_parser_t dp_parser = {
315 .nesting = descriptor_nesting
316 };
317 const usb_dp_parser_data_t dp_data = {
318 .data = config_descriptor,
319 .size = config_descriptor_size,
320 };
321
322 /*
323 * Iterate through all interfaces.
324 */
325 const uint8_t *interface = usb_dp_get_nested_descriptor(&dp_parser,
326 &dp_data, config_descriptor);
327 if (interface == NULL) {
328 return ENOENT;
329 }
330 do {
331 (void) process_interface(mapping, mapping_count,
332 &dp_parser, &dp_data, interface, bus_session);
333 interface = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
334 config_descriptor, interface);
335 } while (interface != NULL);
336
337 return EOK;
338}
339
340/**
341 * @}
342 */
Note: See TracBrowser for help on using the repository browser.