source: mainline/uspace/lib/usbdev/src/pipesinit.c@ 1561e8b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1561e8b was 1561e8b, checked in by Jan Vesely <jano.vesely@…>, 14 years ago

libusbdev: Use provided usb_device_connection_t wrappers.

  • Property mode set to 100644
File size: 13.6 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 * Initialization of endpoint pipes.
34 *
35 */
36#include <usb/usb.h>
37#include <usb/dev/pipes.h>
38#include <usb/dev/dp.h>
39#include <usb/dev/request.h>
40#include <errno.h>
41#include <assert.h>
42
43#define CTRL_PIPE_MIN_PACKET_SIZE 8
44#define DEV_DESCR_MAX_PACKET_SIZE_OFFSET 7
45
46
47#define NESTING(parentname, childname) \
48 { \
49 .child = USB_DESCTYPE_##childname, \
50 .parent = USB_DESCTYPE_##parentname, \
51 }
52#define LAST_NESTING { -1, -1 }
53
54/** Nesting pairs of standard descriptors. */
55static const usb_dp_descriptor_nesting_t descriptor_nesting[] = {
56 NESTING(CONFIGURATION, INTERFACE),
57 NESTING(INTERFACE, ENDPOINT),
58 NESTING(INTERFACE, HUB),
59 NESTING(INTERFACE, HID),
60 NESTING(HID, HID_REPORT),
61 LAST_NESTING
62};
63
64/** Tells whether given descriptor is of endpoint type.
65 *
66 * @param descriptor Descriptor in question.
67 * @return Whether the given descriptor is endpoint descriptor.
68 */
69static inline bool is_endpoint_descriptor(const uint8_t *descriptor)
70{
71 return descriptor[1] == USB_DESCTYPE_ENDPOINT;
72}
73
74/** Tells whether found endpoint corresponds to endpoint described by user.
75 *
76 * @param wanted Endpoint description as entered by driver author.
77 * @param found Endpoint description obtained from endpoint descriptor.
78 * @return Whether the @p found descriptor fits the @p wanted descriptor.
79 */
80static bool endpoint_fits_description(const usb_endpoint_description_t *wanted,
81 const usb_endpoint_description_t *found)
82{
83#define _SAME(fieldname) ((wanted->fieldname) == (found->fieldname))
84
85 if (!_SAME(direction)) {
86 return false;
87 }
88
89 if (!_SAME(transfer_type)) {
90 return false;
91 }
92
93 if ((wanted->interface_class >= 0) && !_SAME(interface_class)) {
94 return false;
95 }
96
97 if ((wanted->interface_subclass >= 0) && !_SAME(interface_subclass)) {
98 return false;
99 }
100
101 if ((wanted->interface_protocol >= 0) && !_SAME(interface_protocol)) {
102 return false;
103 }
104
105#undef _SAME
106
107 return true;
108}
109
110/** Find endpoint mapping for a found endpoint.
111 *
112 * @param mapping Endpoint mapping list.
113 * @param mapping_count Number of endpoint mappings in @p mapping.
114 * @param found_endpoint Description of found endpoint.
115 * @param interface_number Number of currently processed interface.
116 * @return Endpoint mapping corresponding to @p found_endpoint.
117 * @retval NULL No corresponding endpoint found.
118 */
119static usb_endpoint_mapping_t *find_endpoint_mapping(
120 usb_endpoint_mapping_t *mapping, size_t mapping_count,
121 const usb_endpoint_description_t *found_endpoint,
122 int interface_number, int interface_setting)
123{
124 while (mapping_count > 0) {
125 bool interface_number_fits = (mapping->interface_no < 0)
126 || (mapping->interface_no == interface_number);
127
128 bool interface_setting_fits = (mapping->interface_setting < 0)
129 || (mapping->interface_setting == interface_setting);
130
131 bool endpoint_descriptions_fits = endpoint_fits_description(
132 mapping->description, found_endpoint);
133
134 if (interface_number_fits
135 && interface_setting_fits
136 && endpoint_descriptions_fits) {
137 return mapping;
138 }
139
140 mapping++;
141 mapping_count--;
142 }
143 return NULL;
144}
145
146/** Process endpoint descriptor.
147 *
148 * @param mapping Endpoint mapping list.
149 * @param mapping_count Number of endpoint mappings in @p mapping.
150 * @param interface Interface descriptor under which belongs the @p endpoint.
151 * @param endpoint Endpoint descriptor.
152 * @param wire Connection backing the endpoint pipes.
153 * @return Error code.
154 */
155static int process_endpoint(
156 usb_endpoint_mapping_t *mapping, size_t mapping_count,
157 usb_standard_interface_descriptor_t *interface,
158 usb_standard_endpoint_descriptor_t *endpoint,
159 usb_device_connection_t *wire)
160{
161
162 /*
163 * Get endpoint characteristics.
164 */
165
166 /* Actual endpoint number is in bits 0..3 */
167 const usb_endpoint_t ep_no = endpoint->endpoint_address & 0x0F;
168
169 const usb_endpoint_description_t description = {
170 /* Endpoint direction is set by bit 7 */
171 .direction = (endpoint->endpoint_address & 128)
172 ? USB_DIRECTION_IN : USB_DIRECTION_OUT,
173 /* Transfer type is in bits 0..2 and
174 * the enum values corresponds 1:1 */
175 .transfer_type = endpoint->attributes & 3,
176
177 /* Get interface characteristics. */
178 .interface_class = interface->interface_class,
179 .interface_subclass = interface->interface_subclass,
180 .interface_protocol = interface->interface_protocol,
181 };
182
183 /*
184 * Find the most fitting mapping and initialize the pipe.
185 */
186 usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping,
187 mapping_count, &description,
188 interface->interface_number, interface->alternate_setting);
189 if (ep_mapping == NULL) {
190 return ENOENT;
191 }
192
193 if (ep_mapping->present) {
194 return EEXISTS;
195 }
196
197 int rc = usb_pipe_initialize(&ep_mapping->pipe, wire,
198 ep_no, description.transfer_type, endpoint->max_packet_size,
199 description.direction);
200 if (rc != EOK) {
201 return rc;
202 }
203
204 ep_mapping->present = true;
205 ep_mapping->descriptor = endpoint;
206 ep_mapping->interface = interface;
207
208 return EOK;
209}
210
211/** Process whole USB interface.
212 *
213 * @param mapping Endpoint mapping list.
214 * @param mapping_count Number of endpoint mappings in @p mapping.
215 * @param parser Descriptor parser.
216 * @param parser_data Descriptor parser data.
217 * @param interface_descriptor Interface descriptor.
218 * @return Error code.
219 */
220static int process_interface(
221 usb_endpoint_mapping_t *mapping, size_t mapping_count,
222 const usb_dp_parser_t *parser, const usb_dp_parser_data_t *parser_data,
223 const uint8_t *interface_descriptor)
224{
225 const uint8_t *descriptor = usb_dp_get_nested_descriptor(parser,
226 parser_data, interface_descriptor);
227
228 if (descriptor == NULL) {
229 return ENOENT;
230 }
231
232 do {
233 if (is_endpoint_descriptor(descriptor)) {
234 (void) process_endpoint(mapping, mapping_count,
235 (usb_standard_interface_descriptor_t *)
236 interface_descriptor,
237 (usb_standard_endpoint_descriptor_t *)
238 descriptor,
239 (usb_device_connection_t *) parser_data->arg);
240 }
241
242 descriptor = usb_dp_get_sibling_descriptor(parser, parser_data,
243 interface_descriptor, descriptor);
244 } while (descriptor != NULL);
245
246 return EOK;
247}
248
249/** Initialize endpoint pipes from configuration descriptor.
250 *
251 * The mapping array is expected to conform to following rules:
252 * - @c pipe must be uninitialized pipe
253 * - @c description must point to prepared endpoint description
254 * - @c descriptor does not need to be initialized (will be overwritten)
255 * - @c interface does not need to be initialized (will be overwritten)
256 * - @c present does not need to be initialized (will be overwritten)
257 *
258 * After processing the configuration descriptor, the mapping is updated
259 * in the following fashion:
260 * - @c present will be set to @c true when the endpoint was found in the
261 * configuration
262 * - @c descriptor will point inside the configuration descriptor to endpoint
263 * corresponding to given description (or NULL for not found descriptor)
264 * - @c interface will point inside the configuration descriptor to interface
265 * descriptor the endpoint @c descriptor belongs to (or NULL for not found
266 * descriptor)
267 * - @c pipe will be initialized when found, otherwise left untouched
268 * - @c description will be untouched under all circumstances
269 *
270 * @param mapping Endpoint mapping list.
271 * @param mapping_count Number of endpoint mappings in @p mapping.
272 * @param configuration_descriptor Full configuration descriptor (is expected
273 * to be in USB endianness: i.e. as-is after being retrieved from
274 * the device).
275 * @param configuration_descriptor_size Size of @p configuration_descriptor
276 * in bytes.
277 * @param connection Connection backing the endpoint pipes.
278 * @return Error code.
279 */
280int usb_pipe_initialize_from_configuration(
281 usb_endpoint_mapping_t *mapping, size_t mapping_count,
282 const uint8_t *config_descriptor, size_t config_descriptor_size,
283 usb_device_connection_t *connection)
284{
285 assert(connection);
286
287 if (config_descriptor == NULL) {
288 return EBADMEM;
289 }
290 if (config_descriptor_size
291 < sizeof(usb_standard_configuration_descriptor_t)) {
292 return ERANGE;
293 }
294
295 /* Go through the mapping and set all endpoints to not present. */
296 for (size_t i = 0; i < mapping_count; i++) {
297 mapping[i].present = false;
298 mapping[i].descriptor = NULL;
299 mapping[i].interface = NULL;
300 }
301
302 /* Prepare the descriptor parser. */
303 const usb_dp_parser_t dp_parser = {
304 .nesting = descriptor_nesting
305 };
306 const usb_dp_parser_data_t dp_data = {
307 .data = config_descriptor,
308 .size = config_descriptor_size,
309 .arg = connection
310 };
311
312 /*
313 * Iterate through all interfaces.
314 */
315 const uint8_t *interface = usb_dp_get_nested_descriptor(&dp_parser,
316 &dp_data, config_descriptor);
317 if (interface == NULL) {
318 return ENOENT;
319 }
320 do {
321 (void) process_interface(mapping, mapping_count,
322 &dp_parser, &dp_data, interface);
323 interface = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
324 config_descriptor, interface);
325 } while (interface != NULL);
326
327 return EOK;
328}
329
330/** Initialize USB endpoint pipe.
331 *
332 * @param pipe Endpoint pipe to be initialized.
333 * @param connection Connection to the USB device backing this pipe (the wire).
334 * @param endpoint_no Endpoint number (in USB 1.1 in range 0 to 15).
335 * @param transfer_type Transfer type (e.g. interrupt or bulk).
336 * @param max_packet_size Maximum packet size in bytes.
337 * @param direction Endpoint direction (in/out).
338 * @return Error code.
339 */
340int usb_pipe_initialize(usb_pipe_t *pipe,
341 usb_device_connection_t *connection, usb_endpoint_t endpoint_no,
342 usb_transfer_type_t transfer_type, size_t max_packet_size,
343 usb_direction_t direction)
344{
345 assert(pipe);
346 assert(connection);
347
348 fibril_mutex_initialize(&pipe->guard);
349 pipe->wire = connection;
350 pipe->endpoint_no = endpoint_no;
351 pipe->transfer_type = transfer_type;
352 pipe->max_packet_size = max_packet_size;
353 pipe->direction = direction;
354 pipe->auto_reset_halt = false;
355
356 return EOK;
357}
358
359/** Initialize USB endpoint pipe as the default zero control pipe.
360 *
361 * @param pipe Endpoint pipe to be initialized.
362 * @param connection Connection to the USB device backing this pipe (the wire).
363 * @return Error code.
364 */
365int usb_pipe_initialize_default_control(usb_pipe_t *pipe,
366 usb_device_connection_t *connection)
367{
368 assert(pipe);
369 assert(connection);
370
371 int rc = usb_pipe_initialize(pipe, connection, 0, USB_TRANSFER_CONTROL,
372 CTRL_PIPE_MIN_PACKET_SIZE, USB_DIRECTION_BOTH);
373
374 pipe->auto_reset_halt = true;
375
376 return rc;
377}
378
379/** Probe default control pipe for max packet size.
380 *
381 * The function tries to get the correct value of max packet size several
382 * time before giving up.
383 *
384 * The session on the pipe shall not be started.
385 *
386 * @param pipe Default control pipe.
387 * @return Error code.
388 */
389int usb_pipe_probe_default_control(usb_pipe_t *pipe)
390{
391 assert(pipe);
392 assert(DEV_DESCR_MAX_PACKET_SIZE_OFFSET < CTRL_PIPE_MIN_PACKET_SIZE);
393
394 if ((pipe->direction != USB_DIRECTION_BOTH) ||
395 (pipe->transfer_type != USB_TRANSFER_CONTROL) ||
396 (pipe->endpoint_no != 0)) {
397 return EINVAL;
398 }
399
400
401 usb_pipe_start_long_transfer(pipe);
402
403 uint8_t dev_descr_start[CTRL_PIPE_MIN_PACKET_SIZE];
404 size_t transferred_size;
405 int rc;
406 for (size_t attempt_var = 0; attempt_var < 3; ++attempt_var) {
407 rc = usb_request_get_descriptor(pipe, USB_REQUEST_TYPE_STANDARD,
408 USB_REQUEST_RECIPIENT_DEVICE, USB_DESCTYPE_DEVICE,
409 0, 0, dev_descr_start, CTRL_PIPE_MIN_PACKET_SIZE,
410 &transferred_size);
411 if (rc == EOK) {
412 if (transferred_size != CTRL_PIPE_MIN_PACKET_SIZE) {
413 rc = ELIMIT;
414 continue;
415 }
416 break;
417 }
418 }
419 usb_pipe_end_long_transfer(pipe);
420 if (rc != EOK) {
421 return rc;
422 }
423
424 pipe->max_packet_size
425 = dev_descr_start[DEV_DESCR_MAX_PACKET_SIZE_OFFSET];
426
427 return EOK;
428}
429
430/** Register endpoint with the host controller.
431 *
432 * @param pipe Pipe to be registered.
433 * @param interval Polling interval.
434 * @return Error code.
435 */
436int usb_pipe_register(usb_pipe_t *pipe, unsigned interval)
437{
438 assert(pipe);
439 assert(pipe->wire);
440
441 return usb_device_register_endpoint(pipe->wire,
442 pipe->endpoint_no, pipe->transfer_type,
443 pipe->direction, pipe->max_packet_size, interval);
444}
445
446/** Revert endpoint registration with the host controller.
447 *
448 * @param pipe Pipe to be unregistered.
449 * @return Error code.
450 */
451int usb_pipe_unregister(usb_pipe_t *pipe)
452{
453 assert(pipe);
454 assert(pipe->wire);
455
456 return usb_device_unregister_endpoint(pipe->wire,
457 pipe->endpoint_no, pipe->direction);
458}
459
460/**
461 * @}
462 */
Note: See TracBrowser for help on using the repository browser.