source: mainline/uspace/lib/usbdev/src/devdrv.c@ f4138ac

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

libusbdev: Use alternate_interfaces.current.

It's initialized to zero if there are no alternatives.

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*
2 * Copyright (c) 2011 Vojtech Horky
3 * Copyright (c) 2011 Jan Vesely
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup libusbdev
30 * @{
31 */
32/** @file
33 * USB device driver framework.
34 */
35#include <usb/dev/driver.h>
36#include <usb/dev/request.h>
37#include <usb/debug.h>
38#include <usb/dev.h>
39#include <errno.h>
40#include <str_error.h>
41#include <assert.h>
42
43/** Count number of pipes the driver expects.
44 *
45 * @param drv USB driver.
46 * @return Number of pipes (excluding default control pipe).
47 */
48static inline size_t count_other_pipes(
49 const usb_endpoint_description_t **endpoints)
50{
51 size_t count;
52 for (count = 0; endpoints != NULL && endpoints[count] != NULL; ++count);
53 return count;
54}
55
56/** Change interface setting of a device.
57 * This function selects new alternate setting of an interface by issuing
58 * proper USB command to the device and also creates new USB pipes
59 * under @c dev->pipes.
60 *
61 * @warning This function is intended for drivers working at interface level.
62 * For drivers controlling the whole device, you need to change interface
63 * manually using usb_request_set_interface() and creating new pipes
64 * with usb_pipe_initialize_from_configuration().
65 *
66 * @warning This is a wrapper function that does several operations that
67 * can fail and that cannot be rollbacked easily. That means that a failure
68 * during the SET_INTERFACE request would result in having a device with
69 * no pipes at all (except the default control one). That is because the old
70 * pipes needs to be unregistered at HC first and the new ones could not
71 * be created.
72 *
73 * @param dev USB device.
74 * @param alternate_setting Alternate setting to choose.
75 * @param endpoints New endpoint descriptions.
76 * @return Error code.
77 */
78int usb_device_select_interface(usb_device_t *usb_dev,
79 uint8_t alternate_setting, const usb_endpoint_description_t **endpoints)
80{
81 assert(usb_dev);
82
83 if (usb_dev->interface_no < 0) {
84 return EINVAL;
85 }
86
87 /* Change the interface itself. */
88 int rc = usb_request_set_interface(&usb_dev->ctrl_pipe,
89 usb_dev->interface_no, alternate_setting);
90 if (rc != EOK) {
91 return rc;
92 }
93
94 /* Change current alternative */
95 usb_dev->alternate_interfaces.current = alternate_setting;
96
97 /* Destroy existing pipes. */
98 usb_device_destroy_pipes(usb_dev);
99
100 /* Create new pipes. */
101 rc = usb_device_create_pipes(&usb_dev->wire, endpoints,
102 usb_dev->descriptors.configuration,
103 usb_dev->descriptors.configuration_size,
104 usb_dev->interface_no, usb_dev->alternate_interfaces.current,
105 &usb_dev->pipes, &usb_dev->pipes_count);
106
107 return rc;
108}
109
110/** Retrieve basic descriptors from the device.
111 *
112 * @param[in] ctrl_pipe Control endpoint pipe.
113 * @param[out] descriptors Where to store the descriptors.
114 * @return Error code.
115 */
116static int usb_device_retrieve_descriptors(usb_device_t *usb_dev)
117{
118 assert(usb_dev);
119 assert(usb_dev->descriptors.configuration == NULL);
120
121 /* It is worth to start a long transfer. */
122 usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe);
123
124 /* Get the device descriptor. */
125 int rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
126 &usb_dev->descriptors.device);
127 if (rc != EOK) {
128 goto leave;
129 }
130
131 /* Get the full configuration descriptor. */
132 rc = usb_request_get_full_configuration_descriptor_alloc(
133 &usb_dev->ctrl_pipe, 0,
134 (void **) &usb_dev->descriptors.configuration,
135 &usb_dev->descriptors.configuration_size);
136
137leave:
138 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
139
140 return rc;
141}
142
143/** Cleanup structure initialized via usb_device_retrieve_descriptors.
144 *
145 * @param[in] descriptors Where to store the descriptors.
146 */
147static void usb_device_release_descriptors(usb_device_t *usb_dev)
148{
149 assert(usb_dev);
150 free(usb_dev->descriptors.configuration);
151 usb_dev->descriptors.configuration = NULL;
152 usb_dev->descriptors.configuration_size = 0;
153}
154
155/** Create pipes for a device.
156 *
157 * This is more or less a wrapper that does following actions:
158 * - allocate and initialize pipes
159 * - map endpoints to the pipes based on the descriptions
160 * - registers endpoints with the host controller
161 *
162 * @param[in] wire Initialized backing connection to the host controller.
163 * @param[in] endpoints Endpoints description, NULL terminated.
164 * @param[in] config_descr Configuration descriptor of active configuration.
165 * @param[in] config_descr_size Size of @p config_descr in bytes.
166 * @param[in] interface_no Interface to map from.
167 * @param[in] interface_setting Interface setting (default is usually 0).
168 * @param[out] pipes_ptr Where to store array of created pipes
169 * (not NULL terminated).
170 * @param[out] pipes_count_ptr Where to store number of pipes
171 * (set to NULL if you wish to ignore the count).
172 * @return Error code.
173 */
174int usb_device_create_pipes(usb_device_connection_t *wire,
175 const usb_endpoint_description_t **endpoints,
176 const uint8_t *config_descr, size_t config_descr_size,
177 int interface_no, int interface_setting,
178 usb_endpoint_mapping_t **pipes_ptr, size_t *pipes_count_ptr)
179{
180 assert(wire != NULL);
181 assert(config_descr != NULL);
182 assert(config_descr_size > 0);
183 assert(pipes_ptr != NULL);
184
185 size_t i;
186 int rc;
187
188 const size_t pipe_count = count_other_pipes(endpoints);
189 if (pipe_count == 0) {
190 if (pipes_count_ptr)
191 *pipes_count_ptr = pipe_count;
192 *pipes_ptr = NULL;
193 return EOK;
194 }
195
196 usb_endpoint_mapping_t *pipes
197 = calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
198 if (pipes == NULL) {
199 return ENOMEM;
200 }
201
202 /* Now initialize. */
203 for (i = 0; i < pipe_count; i++) {
204 pipes[i].description = endpoints[i];
205 pipes[i].interface_no = interface_no;
206 pipes[i].interface_setting = interface_setting;
207 }
208
209 /* Find the mapping from configuration descriptor. */
210 rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
211 config_descr, config_descr_size, wire);
212 if (rc != EOK) {
213 free(pipes);
214 return rc;
215 }
216
217 /* Register created pipes. */
218 for (i = 0; i < pipe_count; i++) {
219 if (pipes[i].present) {
220 rc = usb_pipe_register(&pipes[i].pipe,
221 pipes[i].descriptor->poll_interval);
222 if (rc != EOK) {
223 goto rollback_unregister_endpoints;
224 }
225 }
226 }
227
228 *pipes_ptr = pipes;
229 if (pipes_count_ptr != NULL) {
230 *pipes_count_ptr = pipe_count;
231 }
232
233 return EOK;
234
235 /*
236 * Jump here if something went wrong after endpoints have
237 * been registered.
238 * This is also the target when the registration of
239 * endpoints fails.
240 */
241rollback_unregister_endpoints:
242 for (i = 0; i < pipe_count; i++) {
243 if (pipes[i].present) {
244 usb_pipe_unregister(&pipes[i].pipe);
245 }
246 }
247
248 free(pipes);
249 return rc;
250}
251
252/** Destroy pipes previously created by usb_device_create_pipes.
253 *
254 * @param[in] usb_dev USB device.
255 */
256void usb_device_destroy_pipes(usb_device_t *usb_dev)
257{
258 assert(usb_dev);
259 assert(usb_dev->pipes || usb_dev->pipes_count == 0);
260 /* Destroy the pipes. */
261 for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
262 usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
263 i, usb_dev->pipes[i].present ? "" : "not ");
264 if (usb_dev->pipes[i].present)
265 usb_pipe_unregister(&usb_dev->pipes[i].pipe);
266 }
267 free(usb_dev->pipes);
268 usb_dev->pipes = NULL;
269 usb_dev->pipes_count = 0;
270}
271
272usb_pipe_t *usb_device_get_default_pipe(usb_device_t *usb_dev)
273{
274 assert(usb_dev);
275 return &usb_dev->ctrl_pipe;
276}
277
278int usb_device_get_iface_number(usb_device_t *usb_dev)
279{
280 assert(usb_dev);
281 return usb_dev->interface_no;
282}
283
284const usb_standard_device_descriptor_t *
285usb_device_get_device_descriptor(usb_device_t *usb_dev)
286{
287 assert(usb_dev);
288 return &usb_dev->descriptors.device;
289}
290
291const void * usb_device_get_configuration_descriptor(
292 usb_device_t *usb_dev, size_t *size)
293{
294 assert(usb_dev);
295 if (size)
296 *size = usb_dev->descriptors.configuration_size;
297 return usb_dev->descriptors.configuration;
298}
299
300const usb_alternate_interfaces_t * usb_device_get_alternative_ifaces(
301 usb_device_t *usb_dev)
302{
303 assert(usb_dev);
304 return &usb_dev->alternate_interfaces;
305}
306
307/** Initialize new instance of USB device.
308 *
309 * @param[in] usb_dev Pointer to the new device.
310 * @param[in] ddf_dev Generic DDF device backing the USB one.
311 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
312 * @param[out] errstr_ptr Where to store description of context
313 * (in case error occurs).
314 * @return Error code.
315 */
316int usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
317 const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
318{
319 assert(usb_dev != NULL);
320 assert(ddf_dev != NULL);
321
322 *errstr_ptr = NULL;
323
324 usb_dev->ddf_dev = ddf_dev;
325 usb_dev->driver_data = NULL;
326 usb_dev->descriptors.configuration = NULL;
327 usb_dev->pipes_count = 0;
328 usb_dev->pipes = NULL;
329
330 usb_dev->bus_session = usb_dev_connect_to_self(ddf_dev);
331 if (!usb_dev->bus_session) {
332 *errstr_ptr = "device bus session create";
333 return ENOMEM;
334 }
335
336 /* Get assigned params */
337 devman_handle_t hc_handle;
338 usb_address_t address;
339
340 int rc = usb_get_info_by_handle(ddf_dev_get_handle(ddf_dev),
341 &hc_handle, &address, &usb_dev->interface_no);
342 if (rc != EOK) {
343 *errstr_ptr = "device parameters retrieval";
344 return rc;
345 }
346
347 /* Initialize hc connection. */
348 usb_hc_connection_initialize(&usb_dev->hc_conn, hc_handle);
349
350 /* Initialize backing wire and control pipe. */
351 rc = usb_device_connection_initialize(
352 &usb_dev->wire, &usb_dev->hc_conn, address);
353 if (rc != EOK) {
354 *errstr_ptr = "device connection initialization";
355 return rc;
356 }
357
358 /* This pipe was registered by the hub driver,
359 * during device initialization. */
360 rc = usb_pipe_initialize_default_control(
361 &usb_dev->ctrl_pipe, &usb_dev->wire);
362 if (rc != EOK) {
363 *errstr_ptr = "default control pipe initialization";
364 return rc;
365 }
366
367 /* Open hc connection for pipe registration. */
368 rc = usb_hc_connection_open(&usb_dev->hc_conn);
369 if (rc != EOK) {
370 *errstr_ptr = "hc connection open";
371 return rc;
372 }
373
374 /* Retrieve standard descriptors. */
375 rc = usb_device_retrieve_descriptors(usb_dev);
376 if (rc != EOK) {
377 *errstr_ptr = "descriptor retrieval";
378 usb_hc_connection_close(&usb_dev->hc_conn);
379 return rc;
380 }
381
382 /* Create alternate interfaces. We will silently ignore failure.
383 * We might either control one interface or an entire device,
384 * it makes no sense to speak about alternate interfaces when
385 * controlling a device. */
386 rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
387 usb_dev->descriptors.configuration,
388 usb_dev->descriptors.configuration_size, usb_dev->interface_no);
389
390 /* Create and register other pipes than default control (EP 0) */
391 rc = usb_device_create_pipes(&usb_dev->wire, endpoints,
392 usb_dev->descriptors.configuration,
393 usb_dev->descriptors.configuration_size,
394 usb_dev->interface_no, usb_dev->alternate_interfaces.current,
395 &usb_dev->pipes, &usb_dev->pipes_count);
396 if (rc != EOK) {
397 usb_hc_connection_close(&usb_dev->hc_conn);
398 /* Full configuration descriptor is allocated. */
399 usb_device_release_descriptors(usb_dev);
400 /* Alternate interfaces may be allocated */
401 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
402 *errstr_ptr = "pipes initialization";
403 return rc;
404 }
405
406 usb_hc_connection_close(&usb_dev->hc_conn);
407 return EOK;
408}
409
410/** Clean instance of a USB device.
411 *
412 * @param dev Device to be de-initialized.
413 *
414 * Does not free/destroy supplied pointer.
415 */
416void usb_device_deinit(usb_device_t *dev)
417{
418 if (dev) {
419 usb_dev_session_close(dev->bus_session);
420 /* Destroy existing pipes. */
421 usb_device_destroy_pipes(dev);
422 /* Ignore errors and hope for the best. */
423 usb_hc_connection_deinitialize(&dev->hc_conn);
424 usb_alternate_interfaces_deinit(&dev->alternate_interfaces);
425 usb_device_release_descriptors(dev);
426 free(dev->driver_data);
427 dev->driver_data = NULL;
428 }
429}
430
431const char *usb_device_get_name(usb_device_t *usb_dev)
432{
433 assert(usb_dev);
434 assert(usb_dev->ddf_dev);
435 //TODO Handle case without ddf_dev
436 return ddf_dev_get_name(usb_dev->ddf_dev);
437}
438
439ddf_fun_t *usb_device_ddf_fun_create(usb_device_t *usb_dev, fun_type_t ftype,
440 const char* name)
441{
442 assert(usb_dev);
443 if (usb_dev->ddf_dev)
444 return ddf_fun_create(usb_dev->ddf_dev, ftype, name);
445 return NULL;
446}
447
448async_exch_t * usb_device_bus_exchange_begin(usb_device_t *usb_dev)
449{
450 assert(usb_dev);
451 return async_exchange_begin(usb_dev->bus_session);
452}
453
454void usb_device_bus_exchange_end(async_exch_t *exch)
455{
456 async_exchange_end(exch);
457}
458
459/** Allocate driver specific data.
460 * @param usb_dev usb_device structure.
461 * @param size requested data size.
462 * @return Pointer to the newly allocated space, NULL on failure.
463 */
464void * usb_device_data_alloc(usb_device_t *usb_dev, size_t size)
465{
466 assert(usb_dev);
467 assert(usb_dev->driver_data == NULL);
468 return usb_dev->driver_data = calloc(1, size);
469
470}
471
472void * usb_device_data_get(usb_device_t *usb_dev)
473{
474 assert(usb_dev);
475 return usb_dev->driver_data;
476}
477/**
478 * @}
479 */
Note: See TracBrowser for help on using the repository browser.