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

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

libusbdev: Make pipes_destroy a member function.

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