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

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

libusbdev: Make ddf driver parts to separate file.

  • Property mode set to 100644
File size: 13.6 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/** Destroy existing pipes of a USB device.
57 *
58 * @param dev Device where to destroy the pipes.
59 */
60static void destroy_current_pipes(usb_device_t *dev)
61{
62 usb_device_destroy_pipes(dev->pipes, dev->pipes_count);
63 dev->pipes = NULL;
64 dev->pipes_count = 0;
65}
66
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 *
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 *
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,
90 const usb_endpoint_description_t **endpoints)
91{
92 if (dev->interface_no < 0) {
93 return EINVAL;
94 }
95
96 /* Destroy existing pipes. */
97 destroy_current_pipes(dev);
98
99 /* Change the interface itself. */
100 int rc = usb_request_set_interface(&dev->ctrl_pipe, dev->interface_no,
101 alternate_setting);
102 if (rc != EOK) {
103 return rc;
104 }
105
106 /* Create new pipes. */
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);
111
112 return rc;
113}
114
115/** Retrieve basic descriptors from the device.
116 *
117 * @param[in] ctrl_pipe Control endpoint pipe.
118 * @param[out] descriptors Where to store the descriptors.
119 * @return Error code.
120 */
121static int usb_device_retrieve_descriptors(usb_device_t *usb_dev)
122{
123 assert(usb_dev);
124 assert(usb_dev->descriptors.configuration == NULL);
125
126 /* It is worth to start a long transfer. */
127 usb_pipe_start_long_transfer(&usb_dev->ctrl_pipe);
128
129 /* Get the device descriptor. */
130 int rc = usb_request_get_device_descriptor(&usb_dev->ctrl_pipe,
131 &usb_dev->descriptors.device);
132 if (rc != EOK) {
133 goto leave;
134 }
135
136 /* Get the full configuration descriptor. */
137 rc = usb_request_get_full_configuration_descriptor_alloc(
138 &usb_dev->ctrl_pipe, 0,
139 (void **) &usb_dev->descriptors.configuration,
140 &usb_dev->descriptors.configuration_size);
141
142leave:
143 usb_pipe_end_long_transfer(&usb_dev->ctrl_pipe);
144
145 return rc;
146}
147
148/** Cleanup structure initialized via usb_device_retrieve_descriptors.
149 *
150 * @param[in] descriptors Where to store the descriptors.
151 */
152static void usb_device_release_descriptors(usb_device_t *usb_dev)
153{
154 assert(usb_dev);
155 free(usb_dev->descriptors.configuration);
156 usb_dev->descriptors.configuration = NULL;
157 usb_dev->descriptors.configuration_size = 0;
158}
159
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
176 * (set to NULL if you wish to ignore the count).
177 * @return Error code.
178 */
179int usb_device_create_pipes(usb_device_connection_t *wire,
180 const usb_endpoint_description_t **endpoints,
181 const uint8_t *config_descr, size_t config_descr_size,
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
193 const size_t pipe_count = count_other_pipes(endpoints);
194 if (pipe_count == 0) {
195 if (pipes_count_ptr)
196 *pipes_count_ptr = pipe_count;
197 *pipes_ptr = NULL;
198 return EOK;
199 }
200
201 usb_endpoint_mapping_t *pipes
202 = calloc(pipe_count, sizeof(usb_endpoint_mapping_t));
203 if (pipes == NULL) {
204 return ENOMEM;
205 }
206
207 /* Now initialize. */
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) {
218 free(pipes);
219 return rc;
220 }
221
222 /* Register created pipes. */
223 for (i = 0; i < pipe_count; i++) {
224 if (pipes[i].present) {
225 rc = usb_pipe_register(&pipes[i].pipe,
226 pipes[i].descriptor->poll_interval);
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) {
249 usb_pipe_unregister(&pipes[i].pipe);
250 }
251 }
252
253 free(pipes);
254 return rc;
255}
256
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 */
262void usb_device_destroy_pipes(usb_endpoint_mapping_t *pipes, size_t pipes_count)
263{
264 /* Destroy the pipes. */
265 for (size_t i = 0; i < pipes_count; ++i) {
266 assert(pipes);
267 usb_log_debug2("Unregistering pipe %zu: %spresent.\n",
268 i, pipes[i].present ? "" : "not ");
269 if (pipes[i].present)
270 usb_pipe_unregister(&pipes[i].pipe);
271 }
272 free(pipes);
273}
274
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
281const usb_standard_device_descriptor_t *
282usb_device_get_device_descriptor(usb_device_t *usb_dev)
283{
284 assert(usb_dev);
285 return &usb_dev->descriptors.device;
286}
287
288const void * usb_device_get_configuration_descriptor(
289 usb_device_t *usb_dev, size_t *size)
290{
291 assert(usb_dev);
292 if (size)
293 *size = usb_dev->descriptors.configuration_size;
294 return usb_dev->descriptors.configuration;
295}
296
297/** Initialize new instance of USB device.
298 *
299 * @param[in] usb_dev Pointer to the new device.
300 * @param[in] ddf_dev Generic DDF device backing the USB one.
301 * @param[in] endpoints NULL terminated array of endpoints (NULL for none).
302 * @param[out] errstr_ptr Where to store description of context
303 * (in case error occurs).
304 * @return Error code.
305 */
306int usb_device_init(usb_device_t *usb_dev, ddf_dev_t *ddf_dev,
307 const usb_endpoint_description_t **endpoints, const char **errstr_ptr)
308{
309 assert(usb_dev != NULL);
310 assert(ddf_dev != NULL);
311
312 *errstr_ptr = NULL;
313
314 usb_dev->ddf_dev = ddf_dev;
315 usb_dev->driver_data = NULL;
316 usb_dev->descriptors.configuration = NULL;
317 usb_dev->pipes_count = 0;
318 usb_dev->pipes = NULL;
319
320 usb_dev->bus_session = usb_dev_connect_to_self(ddf_dev);
321 if (!usb_dev->bus_session) {
322 *errstr_ptr = "device bus session create";
323 return ENOMEM;
324 }
325
326 /* Get assigned params */
327 devman_handle_t hc_handle;
328 usb_address_t address;
329
330 int rc = usb_get_info_by_handle(ddf_dev_get_handle(ddf_dev),
331 &hc_handle, &address, &usb_dev->interface_no);
332 if (rc != EOK) {
333 *errstr_ptr = "device parameters retrieval";
334 return rc;
335 }
336
337 /* Initialize hc connection. */
338 usb_hc_connection_initialize(&usb_dev->hc_conn, hc_handle);
339
340 /* Initialize backing wire and control pipe. */
341 rc = usb_device_connection_initialize(
342 &usb_dev->wire, &usb_dev->hc_conn, address);
343 if (rc != EOK) {
344 *errstr_ptr = "device connection initialization";
345 return rc;
346 }
347
348 /* This pipe was registered by the hub driver,
349 * during device initialization. */
350 rc = usb_pipe_initialize_default_control(
351 &usb_dev->ctrl_pipe, &usb_dev->wire);
352 if (rc != EOK) {
353 *errstr_ptr = "default control pipe initialization";
354 return rc;
355 }
356
357 /* Open hc connection for pipe registration. */
358 rc = usb_hc_connection_open(&usb_dev->hc_conn);
359 if (rc != EOK) {
360 *errstr_ptr = "hc connection open";
361 return rc;
362 }
363
364 /* Retrieve standard descriptors. */
365 rc = usb_device_retrieve_descriptors(usb_dev);
366 if (rc != EOK) {
367 *errstr_ptr = "descriptor retrieval";
368 usb_hc_connection_close(&usb_dev->hc_conn);
369 return rc;
370 }
371
372 /* Create alternate interfaces. We will silently ignore failure.
373 * We might either control one interface or an entire device,
374 * it makes no sense to speak about alternate interfaces when
375 * controlling a device. */
376 rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
377 usb_dev->descriptors.configuration,
378 usb_dev->descriptors.configuration_size, usb_dev->interface_no);
379 const int alternate_iface =
380 (rc == EOK) ? usb_dev->alternate_interfaces.current : 0;
381
382 /* Create and register other pipes than default control (EP 0) */
383 rc = usb_device_create_pipes(&usb_dev->wire, endpoints,
384 usb_dev->descriptors.configuration,
385 usb_dev->descriptors.configuration_size,
386 usb_dev->interface_no, (int)alternate_iface,
387 &usb_dev->pipes, &usb_dev->pipes_count);
388 if (rc != EOK) {
389 usb_hc_connection_close(&usb_dev->hc_conn);
390 /* Full configuration descriptor is allocated. */
391 usb_device_release_descriptors(usb_dev);
392 /* Alternate interfaces may be allocated */
393 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
394 *errstr_ptr = "pipes initialization";
395 return rc;
396 }
397
398 usb_hc_connection_close(&usb_dev->hc_conn);
399 return EOK;
400}
401
402/** Clean instance of a USB device.
403 *
404 * @param dev Device to be de-initialized.
405 *
406 * Does not free/destroy supplied pointer.
407 */
408void usb_device_deinit(usb_device_t *dev)
409{
410 if (dev) {
411 usb_dev_session_close(dev->bus_session);
412 /* Destroy existing pipes. */
413 destroy_current_pipes(dev);
414 /* Ignore errors and hope for the best. */
415 usb_hc_connection_deinitialize(&dev->hc_conn);
416 usb_alternate_interfaces_deinit(&dev->alternate_interfaces);
417 usb_device_release_descriptors(dev);
418 free(dev->driver_data);
419 dev->driver_data = NULL;
420 }
421}
422
423const char *usb_device_get_name(usb_device_t *usb_dev)
424{
425 assert(usb_dev);
426 assert(usb_dev->ddf_dev);
427 //TODO Handle case without ddf_dev
428 return ddf_dev_get_name(usb_dev->ddf_dev);
429}
430
431ddf_fun_t *usb_device_ddf_fun_create(usb_device_t *usb_dev, fun_type_t ftype,
432 const char* name)
433{
434 assert(usb_dev);
435 if (usb_dev->ddf_dev)
436 return ddf_fun_create(usb_dev->ddf_dev, ftype, name);
437 return NULL;
438}
439
440async_exch_t * usb_device_bus_exchange_begin(usb_device_t *usb_dev)
441{
442 assert(usb_dev);
443 return async_exchange_begin(usb_dev->bus_session);
444}
445
446void usb_device_bus_exchange_end(async_exch_t *exch)
447{
448 async_exchange_end(exch);
449}
450
451/** Allocate driver specific data.
452 * @param usb_dev usb_device structure.
453 * @param size requested data size.
454 * @return Pointer to the newly allocated space, NULL on failure.
455 */
456void * usb_device_data_alloc(usb_device_t *usb_dev, size_t size)
457{
458 assert(usb_dev);
459 assert(usb_dev->driver_data == NULL);
460 return usb_dev->driver_data = calloc(1, size);
461
462}
463
464void * usb_device_data_get(usb_device_t *usb_dev)
465{
466 assert(usb_dev);
467 return usb_dev->driver_data;
468}
469/**
470 * @}
471 */
Note: See TracBrowser for help on using the repository browser.