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

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

usb: Use new target packing scheme for IPC.

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