source: mainline/uspace/lib/usb/src/pipesinit.c@ 5fd22d8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5fd22d8 was b7d8fd9, checked in by Vojtech Horky <vojtechhorky@…>, 14 years ago

Add IPC layer for endpoint (bandwidth) reservation

No HC driver currently implements this and no device driver uses this.
Some adjustments might be needed later.

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