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
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
[945d66c]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
[7d9cd62]297/** Initialize new instance of USB device.
[6ee6e6f]298 *
[7d9cd62]299 * @param[in] usb_dev Pointer to the new device.
[6ee6e6f]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 */
[7d9cd62]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)
[6ee6e6f]308{
[7d9cd62]309 assert(usb_dev != NULL);
[6ee6e6f]310 assert(ddf_dev != NULL);
311
[7fc260ff]312 *errstr_ptr = NULL;
313
[7d9cd62]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;
[6ee6e6f]319
[94fbf78]320 usb_dev->bus_session = usb_dev_connect_to_self(ddf_dev);
[c8c758d]321 if (!usb_dev->bus_session) {
322 *errstr_ptr = "device bus session create";
323 return ENOMEM;
324 }
325
[c24c157d]326 /* Get assigned params */
327 devman_handle_t hc_handle;
328 usb_address_t address;
329
[56fd7cf]330 int rc = usb_get_info_by_handle(ddf_dev_get_handle(ddf_dev),
[c24c157d]331 &hc_handle, &address, &usb_dev->interface_no);
332 if (rc != EOK) {
333 *errstr_ptr = "device parameters retrieval";
334 return rc;
335 }
336
[c0fdc0e]337 /* Initialize hc connection. */
[c24c157d]338 usb_hc_connection_initialize(&usb_dev->hc_conn, hc_handle);
[c0fdc0e]339
[3f2af64]340 /* Initialize backing wire and control pipe. */
[c24c157d]341 rc = usb_device_connection_initialize(
[bd575647]342 &usb_dev->wire, &usb_dev->hc_conn, address);
[3f2af64]343 if (rc != EOK) {
[7fc260ff]344 *errstr_ptr = "device connection initialization";
345 return rc;
346 }
347
348 /* This pipe was registered by the hub driver,
349 * during device initialization. */
[c0fdc0e]350 rc = usb_pipe_initialize_default_control(
351 &usb_dev->ctrl_pipe, &usb_dev->wire);
[7fc260ff]352 if (rc != EOK) {
353 *errstr_ptr = "default control pipe initialization";
[3f2af64]354 return rc;
355 }
356
[6e3c005]357 /* Open hc connection for pipe registration. */
[c0fdc0e]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
[3f2af64]364 /* Retrieve standard descriptors. */
[945d66c]365 rc = usb_device_retrieve_descriptors(usb_dev);
[6ee6e6f]366 if (rc != EOK) {
[3f2af64]367 *errstr_ptr = "descriptor retrieval";
[c0fdc0e]368 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]369 return rc;
370 }
371
[7fc260ff]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. */
[ab27e01]376 rc = usb_alternate_interfaces_init(&usb_dev->alternate_interfaces,
[904dcc6]377 usb_dev->descriptors.configuration,
378 usb_dev->descriptors.configuration_size, usb_dev->interface_no);
[ab27e01]379 const int alternate_iface =
380 (rc == EOK) ? usb_dev->alternate_interfaces.current : 0;
[6ee6e6f]381
[66ee26a]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);
[6ee6e6f]388 if (rc != EOK) {
[c0fdc0e]389 usb_hc_connection_close(&usb_dev->hc_conn);
[7d9cd62]390 /* Full configuration descriptor is allocated. */
[945d66c]391 usb_device_release_descriptors(usb_dev);
[7d9cd62]392 /* Alternate interfaces may be allocated */
[904dcc6]393 usb_alternate_interfaces_deinit(&usb_dev->alternate_interfaces);
[6ee6e6f]394 *errstr_ptr = "pipes initialization";
395 return rc;
396 }
397
[c0fdc0e]398 usb_hc_connection_close(&usb_dev->hc_conn);
[6ee6e6f]399 return EOK;
400}
401
[7d9cd62]402/** Clean instance of a USB device.
[70452dd4]403 *
[9c5fd7a]404 * @param dev Device to be de-initialized.
405 *
406 * Does not free/destroy supplied pointer.
[70452dd4]407 */
[9c5fd7a]408void usb_device_deinit(usb_device_t *dev)
[70452dd4]409{
[7d9cd62]410 if (dev) {
[c8c758d]411 usb_dev_session_close(dev->bus_session);
[8e3742f9]412 /* Destroy existing pipes. */
[7d9cd62]413 destroy_current_pipes(dev);
[8e3742f9]414 /* Ignore errors and hope for the best. */
415 usb_hc_connection_deinitialize(&dev->hc_conn);
[904dcc6]416 usb_alternate_interfaces_deinit(&dev->alternate_interfaces);
[945d66c]417 usb_device_release_descriptors(dev);
[7d9cd62]418 free(dev->driver_data);
[8e3742f9]419 dev->driver_data = NULL;
[70452dd4]420 }
[065064e6]421}
422
[f6b2a76b]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
[bb512b2d]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
[0f4bff8]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
[6e3c005]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 */
[065064e6]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);
[70452dd4]461
462}
463
[0f4bff8]464void * usb_device_data_get(usb_device_t *usb_dev)
465{
466 assert(usb_dev);
467 return usb_dev->driver_data;
468}
[6105fc0]469/**
470 * @}
471 */
Note: See TracBrowser for help on using the repository browser.