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
RevLine 
[6105fc0]1/*
2 * Copyright (c) 2011 Vojtech Horky
[4291215]3 * Copyright (c) 2011 Jan Vesely
[6105fc0]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 */
[160b75e]29/** @addtogroup libusbdev
[6105fc0]30 * @{
31 */
32/** @file
33 * USB device driver framework.
34 */
[7d521e24]35#include <usb/dev/driver.h>
36#include <usb/dev/request.h>
[6105fc0]37#include <usb/debug.h>
[1a38701]38#include <usb/dev.h>
[6105fc0]39#include <errno.h>
[69334af]40#include <str_error.h>
[6105fc0]41#include <assert.h>
42
[69334af]43/** Count number of pipes the driver expects.
44 *
45 * @param drv USB driver.
46 * @return Number of pipes (excluding default control pipe).
47 */
[ab27e01]48static inline size_t count_other_pipes(
49 const usb_endpoint_description_t **endpoints)
[6105fc0]50{
[ab27e01]51 size_t count;
[64e3dad]52 for (count = 0; endpoints != NULL && endpoints[count] != NULL; ++count);
[6105fc0]53 return count;
54}
[a76b01b4]55
[0b4e7ca]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 *
[3f2af64]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 *
[0b4e7ca]73 * @param dev USB device.
74 * @param alternate_setting Alternate setting to choose.
75 * @param endpoints New endpoint descriptions.
76 * @return Error code.
77 */
[f4138ac]78int usb_device_select_interface(usb_device_t *usb_dev,
79 uint8_t alternate_setting, const usb_endpoint_description_t **endpoints)
[0b4e7ca]80{
[f4138ac]81 assert(usb_dev);
82
83 if (usb_dev->interface_no < 0) {
[0b4e7ca]84 return EINVAL;
85 }
86
87 /* Change the interface itself. */
[f4138ac]88 int rc = usb_request_set_interface(&usb_dev->ctrl_pipe,
89 usb_dev->interface_no, alternate_setting);
[0b4e7ca]90 if (rc != EOK) {
91 return rc;
92 }
93
[f4138ac]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
[0b4e7ca]100 /* Create new pipes. */
[f4138ac]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);
[c1b1944]106
107 return rc;
108}
109
110/** Retrieve basic descriptors from the device.
111 *
[4fa0a384]112 * @param[in] ctrl_pipe Control endpoint pipe.
[c1b1944]113 * @param[out] descriptors Where to store the descriptors.
114 * @return Error code.
115 */
[945d66c]116static int usb_device_retrieve_descriptors(usb_device_t *usb_dev)
[c1b1944]117{
[945d66c]118 assert(usb_dev);
119 assert(usb_dev->descriptors.configuration == NULL);
[c1b1944]120
[4fa0a384]121 /* It is worth to start a long transfer. */
[945d66c]122 usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe);
[4fa0a384]123
[c1b1944]124 /* Get the device descriptor. */
[945d66c]125 int rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
126 &usb_dev->descriptors.device);
[c1b1944]127 if (rc != EOK) {
[4fa0a384]128 goto leave;
[c1b1944]129 }
130
131 /* Get the full configuration descriptor. */
132 rc = usb_request_get_full_configuration_descriptor_alloc(
[945d66c]133 &usb_dev->ctrl_pipe, 0,
134 (void **) &usb_dev->descriptors.configuration,
135 &usb_dev->descriptors.configuration_size);
[c1b1944]136
[4fa0a384]137leave:
[945d66c]138 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
[4fa0a384]139
140 return rc;
[c1b1944]141}
142
[7fc260ff]143/** Cleanup structure initialized via usb_device_retrieve_descriptors.
144 *
145 * @param[in] descriptors Where to store the descriptors.
146 */
[945d66c]147static void usb_device_release_descriptors(usb_device_t *usb_dev)
[7fc260ff]148{
[945d66c]149 assert(usb_dev);
150 free(usb_dev->descriptors.configuration);
151 usb_dev->descriptors.configuration = NULL;
152 usb_dev->descriptors.configuration_size = 0;
[7fc260ff]153}
154
[c1b1944]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
[ab27e01]171 * (set to NULL if you wish to ignore the count).
[c1b1944]172 * @return Error code.
173 */
[0cfb05e]174int usb_device_create_pipes(usb_device_connection_t *wire,
[b803845]175 const usb_endpoint_description_t **endpoints,
[7c95d6f5]176 const uint8_t *config_descr, size_t config_descr_size,
[c1b1944]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
[5917859c]188 const size_t pipe_count = count_other_pipes(endpoints);
[c1b1944]189 if (pipe_count == 0) {
[ab27e01]190 if (pipes_count_ptr)
191 *pipes_count_ptr = pipe_count;
[c1b1944]192 *pipes_ptr = NULL;
193 return EOK;
194 }
195
196 usb_endpoint_mapping_t *pipes
[ab27e01]197 = calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
[c1b1944]198 if (pipes == NULL) {
199 return ENOMEM;
200 }
201
[441633f]202 /* Now initialize. */
[c1b1944]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) {
[441633f]213 free(pipes);
214 return rc;
[c1b1944]215 }
216
[441633f]217 /* Register created pipes. */
[c1b1944]218 for (i = 0; i < pipe_count; i++) {
219 if (pipes[i].present) {
[b77931d]220 rc = usb_pipe_register(&pipes[i].pipe,
[bd575647]221 pipes[i].descriptor->poll_interval);
[c1b1944]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) {
[bd575647]244 usb_pipe_unregister(&pipes[i].pipe);
[c1b1944]245 }
246 }
247
248 free(pipes);
[0b4e7ca]249 return rc;
250}
[159b91f4]251
[c1b1944]252/** Destroy pipes previously created by usb_device_create_pipes.
253 *
[2dc5a9f]254 * @param[in] usb_dev USB device.
[c1b1944]255 */
[2dc5a9f]256void usb_device_destroy_pipes(usb_device_t *usb_dev)
[c1b1944]257{
[2dc5a9f]258 assert(usb_dev);
259 assert(usb_dev->pipes || usb_dev->pipes_count == 0);
[c1b1944]260 /* Destroy the pipes. */
[2dc5a9f]261 for (size_t i = 0; i < usb_dev->pipes_count; ++i) {
[8a01a0b]262 usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
[2dc5a9f]263 i, usb_dev->pipes[i].present ? "" : "not ");
264 if (usb_dev->pipes[i].present)
265 usb_pipe_unregister(&usb_dev->pipes[i].pipe);
[c1b1944]266 }
[2dc5a9f]267 free(usb_dev->pipes);
268 usb_dev->pipes = NULL;
269 usb_dev->pipes_count = 0;
[c1b1944]270}
271
[0f4bff8]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
[8e10ef4]278int usb_device_get_iface_number(usb_device_t *usb_dev)
279{
280 assert(usb_dev);
281 return usb_dev->interface_no;
282}
283
[945d66c]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
[8e10ef4]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
[7d9cd62]307/** Initialize new instance of USB device.
[6ee6e6f]308 *
[7d9cd62]309 * @param[in] usb_dev Pointer to the new device.
[6ee6e6f]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 */
[7d9cd62]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)
[6ee6e6f]318{
[7d9cd62]319 assert(usb_dev != NULL);
[6ee6e6f]320 assert(ddf_dev != NULL);
321
[7fc260ff]322 *errstr_ptr = NULL;
323
[7d9cd62]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;
[6ee6e6f]329
[94fbf78]330 usb_dev->bus_session = usb_dev_connect_to_self(ddf_dev);
[c8c758d]331 if (!usb_dev->bus_session) {
332 *errstr_ptr = "device bus session create";
333 return ENOMEM;
334 }
335
[c24c157d]336 /* Get assigned params */
337 devman_handle_t hc_handle;
338 usb_address_t address;
339
[56fd7cf]340 int rc = usb_get_info_by_handle(ddf_dev_get_handle(ddf_dev),
[c24c157d]341 &hc_handle, &address, &usb_dev->interface_no);
342 if (rc != EOK) {
343 *errstr_ptr = "device parameters retrieval";
344 return rc;
345 }
346
[c0fdc0e]347 /* Initialize hc connection. */
[c24c157d]348 usb_hc_connection_initialize(&usb_dev->hc_conn, hc_handle);
[c0fdc0e]349
[3f2af64]350 /* Initialize backing wire and control pipe. */
[c24c157d]351 rc = usb_device_connection_initialize(
[bd575647]352 &usb_dev->wire, &usb_dev->hc_conn, address);
[3f2af64]353 if (rc != EOK) {
[7fc260ff]354 *errstr_ptr = "device connection initialization";
355 return rc;
356 }
357
358 /* This pipe was registered by the hub driver,
359 * during device initialization. */
[c0fdc0e]360 rc = usb_pipe_initialize_default_control(
361 &usb_dev->ctrl_pipe, &usb_dev->wire);
[7fc260ff]362 if (rc != EOK) {
363 *errstr_ptr = "default control pipe initialization";
[3f2af64]364 return rc;
365 }
366
[6e3c005]367 /* Open hc connection for pipe registration. */
[c0fdc0e]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
[3f2af64]374 /* Retrieve standard descriptors. */
[945d66c]375 rc = usb_device_retrieve_descriptors(usb_dev);
[6ee6e6f]376 if (rc != EOK) {
[3f2af64]377 *errstr_ptr = "descriptor retrieval";
[c0fdc0e]378 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]379 return rc;
380 }
381
[7fc260ff]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. */
[ab27e01]386 rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
[904dcc6]387 usb_dev->descriptors.configuration,
388 usb_dev->descriptors.configuration_size, usb_dev->interface_no);
[6ee6e6f]389
[66ee26a]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,
[f4138ac]394 usb_dev->interface_no, usb_dev->alternate_interfaces.current,
[66ee26a]395 &usb_dev->pipes, &usb_dev->pipes_count);
[6ee6e6f]396 if (rc != EOK) {
[c0fdc0e]397 usb_hc_connection_close(&usb_dev->hc_conn);
[7d9cd62]398 /* Full configuration descriptor is allocated. */
[945d66c]399 usb_device_release_descriptors(usb_dev);
[7d9cd62]400 /* Alternate interfaces may be allocated */
[904dcc6]401 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
[6ee6e6f]402 *errstr_ptr = "pipes initialization";
403 return rc;
404 }
405
[c0fdc0e]406 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]407 return EOK;
408}
409
[7d9cd62]410/** Clean instance of a USB device.
[70452dd4]411 *
[9c5fd7a]412 * @param dev Device to be de-initialized.
413 *
414 * Does not free/destroy supplied pointer.
[70452dd4]415 */
[9c5fd7a]416void usb_device_deinit(usb_device_t *dev)
[70452dd4]417{
[7d9cd62]418 if (dev) {
[c8c758d]419 usb_dev_session_close(dev->bus_session);
[8e3742f9]420 /* Destroy existing pipes. */
[2dc5a9f]421 usb_device_destroy_pipes(dev);
[8e3742f9]422 /* Ignore errors and hope for the best. */
423 usb_hc_connection_deinitialize(&dev->hc_conn);
[904dcc6]424 usb_alternate_interfaces_deinit(&dev->alternate_interfaces);
[945d66c]425 usb_device_release_descriptors(dev);
[7d9cd62]426 free(dev->driver_data);
[8e3742f9]427 dev->driver_data = NULL;
[70452dd4]428 }
[065064e6]429}
430
[f6b2a76b]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
[bb512b2d]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
[0f4bff8]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
[6e3c005]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 */
[065064e6]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);
[70452dd4]469
470}
471
[0f4bff8]472void * usb_device_data_get(usb_device_t *usb_dev)
473{
474 assert(usb_dev);
475 return usb_dev->driver_data;
476}
[6105fc0]477/**
478 * @}
479 */
Note: See TracBrowser for help on using the repository browser.