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

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

libusbdev: Interface number and alternate interfaces wrappers.

  • Property mode set to 100644
File size: 13.9 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/** Destroy existing pipes of a USB device.
57 *
58 * @param dev Device where to destroy the pipes.
59 */
[6883abfa]60static void destroy_current_pipes(usb_device_t *dev)
[0b4e7ca]61{
[6883abfa]62 usb_device_destroy_pipes(dev->pipes, dev->pipes_count);
[0b4e7ca]63 dev->pipes = NULL;
64 dev->pipes_count = 0;
65}
[a76b01b4]66
[0b4e7ca]67/** Change interface setting of a device.
68 * This function selects new alternate setting of an interface by issuing
69 * proper USB command to the device and also creates new USB pipes
70 * under @c dev->pipes.
71 *
72 * @warning This function is intended for drivers working at interface level.
73 * For drivers controlling the whole device, you need to change interface
74 * manually using usb_request_set_interface() and creating new pipes
75 * with usb_pipe_initialize_from_configuration().
76 *
[3f2af64]77 * @warning This is a wrapper function that does several operations that
78 * can fail and that cannot be rollbacked easily. That means that a failure
79 * during the SET_INTERFACE request would result in having a device with
80 * no pipes at all (except the default control one). That is because the old
81 * pipes needs to be unregistered at HC first and the new ones could not
82 * be created.
83 *
[0b4e7ca]84 * @param dev USB device.
85 * @param alternate_setting Alternate setting to choose.
86 * @param endpoints New endpoint descriptions.
87 * @return Error code.
88 */
89int usb_device_select_interface(usb_device_t *dev, uint8_t alternate_setting,
[b803845]90 const usb_endpoint_description_t **endpoints)
[0b4e7ca]91{
92 if (dev->interface_no < 0) {
93 return EINVAL;
94 }
95
96 /* Destroy existing pipes. */
[6883abfa]97 destroy_current_pipes(dev);
[0b4e7ca]98
99 /* Change the interface itself. */
[441633f]100 int rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
[0b4e7ca]101 alternate_setting);
102 if (rc != EOK) {
103 return rc;
104 }
105
106 /* Create new pipes. */
[66ee26a]107 rc = usb_device_create_pipes(&dev->wire, endpoints,
108 dev->descriptors.configuration, dev->descriptors.configuration_size,
109 dev->interface_no, (int)alternate_setting,
110 &dev->pipes, &dev->pipes_count);
[c1b1944]111
112 return rc;
113}
114
115/** Retrieve basic descriptors from the device.
116 *
[4fa0a384]117 * @param[in] ctrl_pipe Control endpoint pipe.
[c1b1944]118 * @param[out] descriptors Where to store the descriptors.
119 * @return Error code.
120 */
[945d66c]121static int usb_device_retrieve_descriptors(usb_device_t *usb_dev)
[c1b1944]122{
[945d66c]123 assert(usb_dev);
124 assert(usb_dev->descriptors.configuration == NULL);
[c1b1944]125
[4fa0a384]126 /* It is worth to start a long transfer. */
[945d66c]127 usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe);
[4fa0a384]128
[c1b1944]129 /* Get the device descriptor. */
[945d66c]130 int rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
131 &usb_dev->descriptors.device);
[c1b1944]132 if (rc != EOK) {
[4fa0a384]133 goto leave;
[c1b1944]134 }
135
136 /* Get the full configuration descriptor. */
137 rc = usb_request_get_full_configuration_descriptor_alloc(
[945d66c]138 &usb_dev->ctrl_pipe, 0,
139 (void **) &usb_dev->descriptors.configuration,
140 &usb_dev->descriptors.configuration_size);
[c1b1944]141
[4fa0a384]142leave:
[945d66c]143 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
[4fa0a384]144
145 return rc;
[c1b1944]146}
147
[7fc260ff]148/** Cleanup structure initialized via usb_device_retrieve_descriptors.
149 *
150 * @param[in] descriptors Where to store the descriptors.
151 */
[945d66c]152static void usb_device_release_descriptors(usb_device_t *usb_dev)
[7fc260ff]153{
[945d66c]154 assert(usb_dev);
155 free(usb_dev->descriptors.configuration);
156 usb_dev->descriptors.configuration = NULL;
157 usb_dev->descriptors.configuration_size = 0;
[7fc260ff]158}
159
[c1b1944]160/** Create pipes for a device.
161 *
162 * This is more or less a wrapper that does following actions:
163 * - allocate and initialize pipes
164 * - map endpoints to the pipes based on the descriptions
165 * - registers endpoints with the host controller
166 *
167 * @param[in] wire Initialized backing connection to the host controller.
168 * @param[in] endpoints Endpoints description, NULL terminated.
169 * @param[in] config_descr Configuration descriptor of active configuration.
170 * @param[in] config_descr_size Size of @p config_descr in bytes.
171 * @param[in] interface_no Interface to map from.
172 * @param[in] interface_setting Interface setting (default is usually 0).
173 * @param[out] pipes_ptr Where to store array of created pipes
174 * (not NULL terminated).
175 * @param[out] pipes_count_ptr Where to store number of pipes
[ab27e01]176 * (set to NULL if you wish to ignore the count).
[c1b1944]177 * @return Error code.
178 */
[0cfb05e]179int usb_device_create_pipes(usb_device_connection_t *wire,
[b803845]180 const usb_endpoint_description_t **endpoints,
[7c95d6f5]181 const uint8_t *config_descr, size_t config_descr_size,
[c1b1944]182 int interface_no, int interface_setting,
183 usb_endpoint_mapping_t **pipes_ptr, size_t *pipes_count_ptr)
184{
185 assert(wire != NULL);
186 assert(config_descr != NULL);
187 assert(config_descr_size > 0);
188 assert(pipes_ptr != NULL);
189
190 size_t i;
191 int rc;
192
[5917859c]193 const size_t pipe_count = count_other_pipes(endpoints);
[c1b1944]194 if (pipe_count == 0) {
[ab27e01]195 if (pipes_count_ptr)
196 *pipes_count_ptr = pipe_count;
[c1b1944]197 *pipes_ptr = NULL;
198 return EOK;
199 }
200
201 usb_endpoint_mapping_t *pipes
[ab27e01]202 = calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
[c1b1944]203 if (pipes == NULL) {
204 return ENOMEM;
205 }
206
[441633f]207 /* Now initialize. */
[c1b1944]208 for (i = 0; i < pipe_count; i++) {
209 pipes[i].description = endpoints[i];
210 pipes[i].interface_no = interface_no;
211 pipes[i].interface_setting = interface_setting;
212 }
213
214 /* Find the mapping from configuration descriptor. */
215 rc = usb_pipe_initialize_from_configuration(pipes, pipe_count,
216 config_descr, config_descr_size, wire);
217 if (rc != EOK) {
[441633f]218 free(pipes);
219 return rc;
[c1b1944]220 }
221
[441633f]222 /* Register created pipes. */
[c1b1944]223 for (i = 0; i < pipe_count; i++) {
224 if (pipes[i].present) {
[b77931d]225 rc = usb_pipe_register(&pipes[i].pipe,
[bd575647]226 pipes[i].descriptor->poll_interval);
[c1b1944]227 if (rc != EOK) {
228 goto rollback_unregister_endpoints;
229 }
230 }
231 }
232
233 *pipes_ptr = pipes;
234 if (pipes_count_ptr != NULL) {
235 *pipes_count_ptr = pipe_count;
236 }
237
238 return EOK;
239
240 /*
241 * Jump here if something went wrong after endpoints have
242 * been registered.
243 * This is also the target when the registration of
244 * endpoints fails.
245 */
246rollback_unregister_endpoints:
247 for (i = 0; i < pipe_count; i++) {
248 if (pipes[i].present) {
[bd575647]249 usb_pipe_unregister(&pipes[i].pipe);
[c1b1944]250 }
251 }
252
253 free(pipes);
[0b4e7ca]254 return rc;
255}
[159b91f4]256
[c1b1944]257/** Destroy pipes previously created by usb_device_create_pipes.
258 *
259 * @param[in] pipes Endpoint mapping to be destroyed.
260 * @param[in] pipes_count Number of endpoints.
261 */
[6883abfa]262void usb_device_destroy_pipes(usb_endpoint_mapping_t *pipes, size_t pipes_count)
[c1b1944]263{
264 /* Destroy the pipes. */
[8a01a0b]265 for (size_t i = 0; i < pipes_count; ++i) {
[6883abfa]266 assert(pipes);
[8a01a0b]267 usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
[1526c174]268 i, pipes[i].present ? "" : "not ");
269 if (pipes[i].present)
[bd575647]270 usb_pipe_unregister(&pipes[i].pipe);
[c1b1944]271 }
272 free(pipes);
273}
274
[0f4bff8]275usb_pipe_t *usb_device_get_default_pipe(usb_device_t *usb_dev)
276{
277 assert(usb_dev);
278 return &usb_dev->ctrl_pipe;
279}
280
[8e10ef4]281int usb_device_get_iface_number(usb_device_t *usb_dev)
282{
283 assert(usb_dev);
284 return usb_dev->interface_no;
285}
286
[945d66c]287const usb_standard_device_descriptor_t *
288usb_device_get_device_descriptor(usb_device_t *usb_dev)
289{
290 assert(usb_dev);
291 return &usb_dev->descriptors.device;
292}
293
294const void * usb_device_get_configuration_descriptor(
295 usb_device_t *usb_dev, size_t *size)
296{
297 assert(usb_dev);
298 if (size)
299 *size = usb_dev->descriptors.configuration_size;
300 return usb_dev->descriptors.configuration;
301}
302
[8e10ef4]303const usb_alternate_interfaces_t * usb_device_get_alternative_ifaces(
304 usb_device_t *usb_dev)
305{
306 assert(usb_dev);
307 return &usb_dev->alternate_interfaces;
308}
309
[7d9cd62]310/** Initialize new instance of USB device.
[6ee6e6f]311 *
[7d9cd62]312 * @param[in] usb_dev Pointer to the new device.
[6ee6e6f]313 * @param[in] ddf_dev Generic DDF device backing the USB one.
314 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
315 * @param[out] errstr_ptr Where to store description of context
316 * (in case error occurs).
317 * @return Error code.
318 */
[7d9cd62]319int usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
320 const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
[6ee6e6f]321{
[7d9cd62]322 assert(usb_dev != NULL);
[6ee6e6f]323 assert(ddf_dev != NULL);
324
[7fc260ff]325 *errstr_ptr = NULL;
326
[7d9cd62]327 usb_dev->ddf_dev = ddf_dev;
328 usb_dev->driver_data = NULL;
329 usb_dev->descriptors.configuration = NULL;
330 usb_dev->pipes_count = 0;
331 usb_dev->pipes = NULL;
[6ee6e6f]332
[94fbf78]333 usb_dev->bus_session = usb_dev_connect_to_self(ddf_dev);
[c8c758d]334 if (!usb_dev->bus_session) {
335 *errstr_ptr = "device bus session create";
336 return ENOMEM;
337 }
338
[c24c157d]339 /* Get assigned params */
340 devman_handle_t hc_handle;
341 usb_address_t address;
342
[56fd7cf]343 int rc = usb_get_info_by_handle(ddf_dev_get_handle(ddf_dev),
[c24c157d]344 &hc_handle, &address, &usb_dev->interface_no);
345 if (rc != EOK) {
346 *errstr_ptr = "device parameters retrieval";
347 return rc;
348 }
349
[c0fdc0e]350 /* Initialize hc connection. */
[c24c157d]351 usb_hc_connection_initialize(&usb_dev->hc_conn, hc_handle);
[c0fdc0e]352
[3f2af64]353 /* Initialize backing wire and control pipe. */
[c24c157d]354 rc = usb_device_connection_initialize(
[bd575647]355 &usb_dev->wire, &usb_dev->hc_conn, address);
[3f2af64]356 if (rc != EOK) {
[7fc260ff]357 *errstr_ptr = "device connection initialization";
358 return rc;
359 }
360
361 /* This pipe was registered by the hub driver,
362 * during device initialization. */
[c0fdc0e]363 rc = usb_pipe_initialize_default_control(
364 &usb_dev->ctrl_pipe, &usb_dev->wire);
[7fc260ff]365 if (rc != EOK) {
366 *errstr_ptr = "default control pipe initialization";
[3f2af64]367 return rc;
368 }
369
[6e3c005]370 /* Open hc connection for pipe registration. */
[c0fdc0e]371 rc = usb_hc_connection_open(&usb_dev->hc_conn);
372 if (rc != EOK) {
373 *errstr_ptr = "hc connection open";
374 return rc;
375 }
376
[3f2af64]377 /* Retrieve standard descriptors. */
[945d66c]378 rc = usb_device_retrieve_descriptors(usb_dev);
[6ee6e6f]379 if (rc != EOK) {
[3f2af64]380 *errstr_ptr = "descriptor retrieval";
[c0fdc0e]381 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]382 return rc;
383 }
384
[7fc260ff]385 /* Create alternate interfaces. We will silently ignore failure.
386 * We might either control one interface or an entire device,
387 * it makes no sense to speak about alternate interfaces when
388 * controlling a device. */
[ab27e01]389 rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
[904dcc6]390 usb_dev->descriptors.configuration,
391 usb_dev->descriptors.configuration_size, usb_dev->interface_no);
[ab27e01]392 const int alternate_iface =
393 (rc == EOK) ? usb_dev->alternate_interfaces.current : 0;
[6ee6e6f]394
[66ee26a]395 /* Create and register other pipes than default control (EP 0) */
396 rc = usb_device_create_pipes(&usb_dev->wire, endpoints,
397 usb_dev->descriptors.configuration,
398 usb_dev->descriptors.configuration_size,
399 usb_dev->interface_no, (int)alternate_iface,
400 &usb_dev->pipes, &usb_dev->pipes_count);
[6ee6e6f]401 if (rc != EOK) {
[c0fdc0e]402 usb_hc_connection_close(&usb_dev->hc_conn);
[7d9cd62]403 /* Full configuration descriptor is allocated. */
[945d66c]404 usb_device_release_descriptors(usb_dev);
[7d9cd62]405 /* Alternate interfaces may be allocated */
[904dcc6]406 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
[6ee6e6f]407 *errstr_ptr = "pipes initialization";
408 return rc;
409 }
410
[c0fdc0e]411 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]412 return EOK;
413}
414
[7d9cd62]415/** Clean instance of a USB device.
[70452dd4]416 *
[9c5fd7a]417 * @param dev Device to be de-initialized.
418 *
419 * Does not free/destroy supplied pointer.
[70452dd4]420 */
[9c5fd7a]421void usb_device_deinit(usb_device_t *dev)
[70452dd4]422{
[7d9cd62]423 if (dev) {
[c8c758d]424 usb_dev_session_close(dev->bus_session);
[8e3742f9]425 /* Destroy existing pipes. */
[7d9cd62]426 destroy_current_pipes(dev);
[8e3742f9]427 /* Ignore errors and hope for the best. */
428 usb_hc_connection_deinitialize(&dev->hc_conn);
[904dcc6]429 usb_alternate_interfaces_deinit(&dev->alternate_interfaces);
[945d66c]430 usb_device_release_descriptors(dev);
[7d9cd62]431 free(dev->driver_data);
[8e3742f9]432 dev->driver_data = NULL;
[70452dd4]433 }
[065064e6]434}
435
[f6b2a76b]436const char *usb_device_get_name(usb_device_t *usb_dev)
437{
438 assert(usb_dev);
439 assert(usb_dev->ddf_dev);
440 //TODO Handle case without ddf_dev
441 return ddf_dev_get_name(usb_dev->ddf_dev);
442}
443
[bb512b2d]444ddf_fun_t *usb_device_ddf_fun_create(usb_device_t *usb_dev, fun_type_t ftype,
445 const char* name)
446{
447 assert(usb_dev);
448 if (usb_dev->ddf_dev)
449 return ddf_fun_create(usb_dev->ddf_dev, ftype, name);
450 return NULL;
451}
452
[0f4bff8]453async_exch_t * usb_device_bus_exchange_begin(usb_device_t *usb_dev)
454{
455 assert(usb_dev);
456 return async_exchange_begin(usb_dev->bus_session);
457}
458
459void usb_device_bus_exchange_end(async_exch_t *exch)
460{
461 async_exchange_end(exch);
462}
463
[6e3c005]464/** Allocate driver specific data.
465 * @param usb_dev usb_device structure.
466 * @param size requested data size.
467 * @return Pointer to the newly allocated space, NULL on failure.
468 */
[065064e6]469void * usb_device_data_alloc(usb_device_t *usb_dev, size_t size)
470{
471 assert(usb_dev);
472 assert(usb_dev->driver_data == NULL);
473 return usb_dev->driver_data = calloc(1, size);
[70452dd4]474
475}
476
[0f4bff8]477void * usb_device_data_get(usb_device_t *usb_dev)
478{
479 assert(usb_dev);
480 return usb_dev->driver_data;
481}
[6105fc0]482/**
483 * @}
484 */
Note: See TracBrowser for help on using the repository browser.