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

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

libusbdev: Cleanup unused code.

  • Property mode set to 100644
File size: 11.3 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/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 DEV_DESCR_MAX_PACKET_SIZE_OFFSET 7
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 const 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(const 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 const 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 const usb_endpoint_description_t *found_endpoint,
120 int interface_number, int interface_setting)
121{
122 while (mapping_count > 0) {
123 bool interface_number_fits = (mapping->interface_no < 0)
124 || (mapping->interface_no == interface_number);
125
126 bool interface_setting_fits = (mapping->interface_setting < 0)
127 || (mapping->interface_setting == interface_setting);
128
129 bool endpoint_descriptions_fits = endpoint_fits_description(
130 mapping->description, found_endpoint);
131
132 if (interface_number_fits
133 && interface_setting_fits
134 && endpoint_descriptions_fits) {
135 return mapping;
136 }
137
138 mapping++;
139 mapping_count--;
140 }
141 return NULL;
142}
143
144/** Process endpoint descriptor.
145 *
146 * @param mapping Endpoint mapping list.
147 * @param mapping_count Number of endpoint mappings in @p mapping.
148 * @param interface Interface descriptor under which belongs the @p endpoint.
149 * @param endpoint Endpoint descriptor.
150 * @return Error code.
151 */
152static int process_endpoint(
153 usb_endpoint_mapping_t *mapping, size_t mapping_count,
154 usb_standard_interface_descriptor_t *interface,
155 usb_standard_endpoint_descriptor_t *endpoint_desc,
156 usb_dev_session_t *bus_session)
157{
158
159 /*
160 * Get endpoint characteristics.
161 */
162
163 /* Actual endpoint number is in bits 0..3 */
164 const usb_endpoint_t ep_no = endpoint_desc->endpoint_address & 0x0F;
165
166 const usb_endpoint_description_t description = {
167 /* Endpoint direction is set by bit 7 */
168 .direction = (endpoint_desc->endpoint_address & 128)
169 ? USB_DIRECTION_IN : USB_DIRECTION_OUT,
170 /* Transfer type is in bits 0..2 and
171 * the enum values corresponds 1:1 */
172 .transfer_type = endpoint_desc->attributes & 3,
173
174 /* Get interface characteristics. */
175 .interface_class = interface->interface_class,
176 .interface_subclass = interface->interface_subclass,
177 .interface_protocol = interface->interface_protocol,
178 };
179
180 /*
181 * Find the most fitting mapping and initialize the pipe.
182 */
183 usb_endpoint_mapping_t *ep_mapping = find_endpoint_mapping(mapping,
184 mapping_count, &description,
185 interface->interface_number, interface->alternate_setting);
186 if (ep_mapping == NULL) {
187 return ENOENT;
188 }
189
190 if (ep_mapping->present) {
191 return EEXISTS;
192 }
193
194 int rc = usb_pipe_initialize(&ep_mapping->pipe,
195 ep_no, description.transfer_type,
196 uint16_usb2host(endpoint_desc->max_packet_size),
197 description.direction, bus_session);
198 if (rc != EOK) {
199 return rc;
200 }
201
202 ep_mapping->present = true;
203 ep_mapping->descriptor = endpoint_desc;
204 ep_mapping->interface = interface;
205
206 return EOK;
207}
208
209/** Process whole USB interface.
210 *
211 * @param mapping Endpoint mapping list.
212 * @param mapping_count Number of endpoint mappings in @p mapping.
213 * @param parser Descriptor parser.
214 * @param parser_data Descriptor parser data.
215 * @param interface_descriptor Interface descriptor.
216 * @return Error code.
217 */
218static int process_interface(
219 usb_endpoint_mapping_t *mapping, size_t mapping_count,
220 const usb_dp_parser_t *parser, const usb_dp_parser_data_t *parser_data,
221 const uint8_t *interface_descriptor, usb_dev_session_t *bus_session)
222{
223 const uint8_t *descriptor = usb_dp_get_nested_descriptor(parser,
224 parser_data, interface_descriptor);
225
226 if (descriptor == NULL) {
227 return ENOENT;
228 }
229
230 do {
231 if (is_endpoint_descriptor(descriptor)) {
232 (void) process_endpoint(mapping, mapping_count,
233 (usb_standard_interface_descriptor_t *)
234 interface_descriptor,
235 (usb_standard_endpoint_descriptor_t *)
236 descriptor,
237 bus_session);
238 }
239
240 descriptor = usb_dp_get_sibling_descriptor(parser, parser_data,
241 interface_descriptor, descriptor);
242 } while (descriptor != NULL);
243
244 return EOK;
245}
246
247/** Initialize endpoint pipes from configuration descriptor.
248 *
249 * The mapping array is expected to conform to following rules:
250 * - @c pipe must be uninitialized pipe
251 * - @c description must point to prepared endpoint description
252 * - @c descriptor does not need to be initialized (will be overwritten)
253 * - @c interface does not need to be initialized (will be overwritten)
254 * - @c present does not need to be initialized (will be overwritten)
255 *
256 * After processing the configuration descriptor, the mapping is updated
257 * in the following fashion:
258 * - @c present will be set to @c true when the endpoint was found in the
259 * configuration
260 * - @c descriptor will point inside the configuration descriptor to endpoint
261 * corresponding to given description (or NULL for not found descriptor)
262 * - @c interface will point inside the configuration descriptor to interface
263 * descriptor the endpoint @c descriptor belongs to (or NULL for not found
264 * descriptor)
265 * - @c pipe will be initialized when found, otherwise left untouched
266 * - @c description will be untouched under all circumstances
267 *
268 * @param mapping Endpoint mapping list.
269 * @param mapping_count Number of endpoint mappings in @p mapping.
270 * @param configuration_descriptor Full configuration descriptor (is expected
271 * to be in USB endianness: i.e. as-is after being retrieved from
272 * the device).
273 * @param configuration_descriptor_size Size of @p configuration_descriptor
274 * in bytes.
275 * @param connection Connection backing the endpoint pipes.
276 * @return Error code.
277 */
278int usb_pipe_initialize_from_configuration(
279 usb_endpoint_mapping_t *mapping, size_t mapping_count,
280 const uint8_t *config_descriptor, size_t config_descriptor_size,
281 usb_dev_session_t *bus_session)
282{
283
284 if (config_descriptor == NULL) {
285 return EBADMEM;
286 }
287 if (config_descriptor_size
288 < sizeof(usb_standard_configuration_descriptor_t)) {
289 return ERANGE;
290 }
291
292 /* Go through the mapping and set all endpoints to not present. */
293 for (size_t i = 0; i < mapping_count; i++) {
294 mapping[i].present = false;
295 mapping[i].descriptor = NULL;
296 mapping[i].interface = NULL;
297 }
298
299 /* Prepare the descriptor parser. */
300 const usb_dp_parser_t dp_parser = {
301 .nesting = descriptor_nesting
302 };
303 const usb_dp_parser_data_t dp_data = {
304 .data = config_descriptor,
305 .size = config_descriptor_size,
306 };
307
308 /*
309 * Iterate through all interfaces.
310 */
311 const uint8_t *interface = usb_dp_get_nested_descriptor(&dp_parser,
312 &dp_data, config_descriptor);
313 if (interface == NULL) {
314 return ENOENT;
315 }
316 do {
317 (void) process_interface(mapping, mapping_count,
318 &dp_parser, &dp_data, interface, bus_session);
319 interface = usb_dp_get_sibling_descriptor(&dp_parser, &dp_data,
320 config_descriptor, interface);
321 } while (interface != NULL);
322
323 return EOK;
324}
325
326/** Probe default control pipe for max packet size.
327 *
328 * The function tries to get the correct value of max packet size several
329 * time before giving up.
330 *
331 * The session on the pipe shall not be started.
332 *
333 * @param pipe Default control pipe.
334 * @return Error code.
335 */
336int usb_pipe_probe_default_control(usb_pipe_t *pipe)
337{
338 assert(pipe);
339 assert(DEV_DESCR_MAX_PACKET_SIZE_OFFSET < CTRL_PIPE_MIN_PACKET_SIZE);
340
341 if ((pipe->direction != USB_DIRECTION_BOTH) ||
342 (pipe->transfer_type != USB_TRANSFER_CONTROL) ||
343 (pipe->endpoint_no != 0)) {
344 return EINVAL;
345 }
346
347 uint8_t dev_descr_start[CTRL_PIPE_MIN_PACKET_SIZE];
348 size_t transferred_size;
349 int rc;
350 for (size_t attempt_var = 0; attempt_var < 3; ++attempt_var) {
351 rc = usb_request_get_descriptor(pipe, USB_REQUEST_TYPE_STANDARD,
352 USB_REQUEST_RECIPIENT_DEVICE, USB_DESCTYPE_DEVICE,
353 0, 0, dev_descr_start, CTRL_PIPE_MIN_PACKET_SIZE,
354 &transferred_size);
355 if (rc == EOK) {
356 if (transferred_size != CTRL_PIPE_MIN_PACKET_SIZE) {
357 rc = ELIMIT;
358 continue;
359 }
360 break;
361 }
362 }
363 if (rc != EOK) {
364 return rc;
365 }
366
367 pipe->max_packet_size
368 = dev_descr_start[DEV_DESCR_MAX_PACKET_SIZE_OFFSET];
369
370 return EOK;
371}
372
373/**
374 * @}
375 */
Note: See TracBrowser for help on using the repository browser.