source: mainline/uspace/lib/usbhost/src/ddf_helpers.c@ 5514cf7

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

libusbhost: remove implementations of the old HC handle based functions

  • Property mode set to 100644
File size: 17.0 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 *
34 */
35
36#include <usb_iface.h>
37#include <usb/classes/classes.h>
38#include <usb/debug.h>
39#include <usb/descriptor.h>
40#include <usb/request.h>
41#include <errno.h>
42#include <str_error.h>
43
44#include "ddf_helpers.h"
45
46#define CTRL_PIPE_MIN_PACKET_SIZE 8
47
48extern usbhc_iface_t hcd_iface;
49
50typedef struct hc_dev {
51 ddf_fun_t *hc_fun;
52 list_t devices;
53 fibril_mutex_t guard;
54} hc_dev_t;
55
56static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
57{
58 return ddf_dev_data_get(dev);
59}
60
61hcd_t *dev_to_hcd(ddf_dev_t *dev)
62{
63 hc_dev_t *hc_dev = dev_to_hc_dev(dev);
64 if (!hc_dev || !hc_dev->hc_fun) {
65 usb_log_error("Invalid HCD device.\n");
66 return NULL;
67 }
68 return ddf_fun_data_get(hc_dev->hc_fun);
69}
70
71typedef struct usb_dev {
72 link_t link;
73 ddf_fun_t *fun;
74 usb_address_t address;
75 usb_speed_t speed;
76} usb_dev_t;
77
78/** Register endpoint interface function.
79 * @param fun DDF function.
80 * @param address USB address of the device.
81 * @param endpoint USB endpoint number to be registered.
82 * @param transfer_type Endpoint's transfer type.
83 * @param direction USB communication direction the endpoint is capable of.
84 * @param max_packet_size Maximu size of packets the endpoint accepts.
85 * @param interval Preferred timeout between communication.
86 * @return Error code.
87 */
88static int register_endpoint(
89 ddf_fun_t *fun, usb_endpoint_t endpoint,
90 usb_transfer_type_t transfer_type, usb_direction_t direction,
91 size_t max_packet_size, unsigned interval)
92{
93 assert(fun);
94 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
95 usb_dev_t *dev = ddf_fun_data_get(fun);
96 assert(hcd);
97 assert(dev);
98 const size_t size = max_packet_size;
99 const usb_target_t target =
100 {{.address = dev->address, .endpoint = endpoint}};
101
102 usb_log_debug("Register endpoint %d:%d %s-%s %zuB %ums.\n",
103 dev->address, endpoint, usb_str_transfer_type(transfer_type),
104 usb_str_direction(direction), max_packet_size, interval);
105
106 return hcd_add_ep(hcd, target, direction, transfer_type,
107 max_packet_size, size);
108}
109
110/** Unregister endpoint interface function.
111 * @param fun DDF function.
112 * @param address USB address of the endpoint.
113 * @param endpoint USB endpoint number.
114 * @param direction Communication direction of the enpdoint to unregister.
115 * @return Error code.
116 */
117static int unregister_endpoint(
118 ddf_fun_t *fun, usb_endpoint_t endpoint, usb_direction_t direction)
119{
120 assert(fun);
121 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
122 usb_dev_t *dev = ddf_fun_data_get(fun);
123 assert(hcd);
124 assert(dev);
125 const usb_target_t target =
126 {{.address = dev->address, .endpoint = endpoint}};
127 usb_log_debug("Unregister endpoint %d:%d %s.\n",
128 dev->address, endpoint, usb_str_direction(direction));
129 return hcd_remove_ep(hcd, target, direction);
130}
131
132static int reserve_default_address(ddf_fun_t *fun, usb_speed_t speed)
133{
134 assert(fun);
135 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
136 usb_dev_t *dev = ddf_fun_data_get(fun);
137 assert(hcd);
138 assert(dev);
139
140 usb_log_debug("Device %d requested default address at %s speed\n",
141 dev->address, usb_str_speed(speed));
142 return hcd_reserve_default_address(hcd, speed);
143}
144
145static int release_default_address(ddf_fun_t *fun)
146{
147 assert(fun);
148 hcd_t *hcd = dev_to_hcd(ddf_fun_get_dev(fun));
149 usb_dev_t *dev = ddf_fun_data_get(fun);
150 assert(hcd);
151 assert(dev);
152
153 usb_log_debug("Device %d released default address\n", dev->address);
154 return hcd_release_default_address(hcd);
155}
156
157static int device_enumerate(ddf_fun_t *fun, usb_device_handle_t *handle)
158{
159 assert(fun);
160 ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
161 usb_dev_t *dev = ddf_fun_data_get(fun);
162 assert(ddf_dev);
163 assert(dev);
164 usb_address_t address = 0;
165 usb_log_debug("Device %d reported a new USB device\n", dev->address);
166 const int ret = hcd_ddf_new_device(ddf_dev, &address);
167 if (ret == EOK && handle)
168 *handle = address;
169 return ret;
170}
171
172static int device_remove(ddf_fun_t *fun, usb_device_handle_t handle)
173{
174 assert(fun);
175 ddf_dev_t *ddf_dev = ddf_fun_get_dev(fun);
176 usb_dev_t *dev = ddf_fun_data_get(fun);
177 assert(ddf_dev);
178 assert(dev);
179 usb_log_debug("Device %d reported removal of device %d\n",
180 dev->address, (int)handle);
181 return hcd_ddf_remove_device(ddf_dev, (usb_address_t)handle);
182}
183
184/** Gets handle of the respective hc (this device, hc function).
185 *
186 * @param[in] root_hub_fun Root hub function seeking hc handle.
187 * @param[out] handle Place to write the handle.
188 * @return Error code.
189 */
190static int get_device_handle(ddf_fun_t *fun, devman_handle_t *handle)
191{
192 assert(fun);
193 if (handle)
194 *handle = ddf_fun_get_handle(fun);
195 return EOK;
196}
197
198/** Inbound communication interface function.
199 * @param fun DDF function.
200 * @param target Communication target.
201 * @param setup_data Data to use in setup stage (control transfers).
202 * @param data Pointer to data buffer.
203 * @param size Size of the data buffer.
204 * @param callback Function to call on communication end.
205 * @param arg Argument passed to the callback function.
206 * @return Error code.
207 */
208static int dev_read(ddf_fun_t *fun, usb_endpoint_t endpoint,
209 uint64_t setup_data, uint8_t *data, size_t size,
210 usbhc_iface_transfer_in_callback_t callback, void *arg)
211{
212 assert(fun);
213 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
214 assert(usb_dev);
215 const usb_target_t target = {{
216 .address = usb_dev->address,
217 .endpoint = endpoint,
218 }};
219 return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)), target,
220 USB_DIRECTION_IN, data, size, setup_data, callback, NULL, arg,
221 "READ");
222}
223
224/** Outbound communication interface function.
225 * @param fun DDF function.
226 * @param target Communication target.
227 * @param setup_data Data to use in setup stage (control transfers).
228 * @param data Pointer to data buffer.
229 * @param size Size of the data buffer.
230 * @param callback Function to call on communication end.
231 * @param arg Argument passed to the callback function.
232 * @return Error code.
233 */
234static int dev_write(ddf_fun_t *fun, usb_endpoint_t endpoint,
235 uint64_t setup_data, const uint8_t *data, size_t size,
236 usbhc_iface_transfer_out_callback_t callback, void *arg)
237{
238 assert(fun);
239 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
240 assert(usb_dev);
241 const usb_target_t target = {{
242 .address = usb_dev->address,
243 .endpoint = endpoint,
244 }};
245 return hcd_send_batch(dev_to_hcd(ddf_fun_get_dev(fun)),
246 target, USB_DIRECTION_OUT, (uint8_t*)data, size, setup_data, NULL,
247 callback, arg, "WRITE");
248}
249
250/** Root hub USB interface */
251static usb_iface_t usb_iface = {
252 .get_device_handle = get_device_handle,
253
254 .reserve_default_address = reserve_default_address,
255 .release_default_address = release_default_address,
256
257 .device_enumerate = device_enumerate,
258 .device_remove = device_remove,
259
260 .register_endpoint = register_endpoint,
261 .unregister_endpoint = unregister_endpoint,
262
263 .read = dev_read,
264 .write = dev_write,
265};
266
267/** Standard USB RH options (device interface) */
268static ddf_dev_ops_t usb_ops = {
269 .interfaces[USB_DEV_IFACE] = &usb_iface,
270};
271
272#define GET_DEVICE_DESC(size) \
273{ \
274 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
275 | (USB_REQUEST_TYPE_STANDARD << 5) \
276 | USB_REQUEST_RECIPIENT_DEVICE, \
277 .request = USB_DEVREQ_GET_DESCRIPTOR, \
278 .value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
279 .index = uint16_host2usb(0), \
280 .length = uint16_host2usb(size), \
281};
282
283#define SET_ADDRESS(address) \
284{ \
285 .request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
286 | (USB_REQUEST_TYPE_STANDARD << 5) \
287 | USB_REQUEST_RECIPIENT_DEVICE, \
288 .request = USB_DEVREQ_SET_ADDRESS, \
289 .value = uint16_host2usb(address), \
290 .index = uint16_host2usb(0), \
291 .length = uint16_host2usb(0), \
292};
293
294int hcd_ddf_add_usb_device(ddf_dev_t *parent,
295 usb_address_t address, usb_speed_t speed, const char *name,
296 const match_id_list_t *mids)
297{
298 assert(parent);
299 hc_dev_t *hc_dev = dev_to_hc_dev(parent);
300
301 char default_name[10] = { 0 }; /* usbxyz-ss */
302 if (!name) {
303 snprintf(default_name, sizeof(default_name) - 1,
304 "usb%u-%cs", address, usb_str_speed(speed)[0]);
305 name = default_name;
306 }
307
308 //TODO more checks
309 ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
310 if (!fun)
311 return ENOMEM;
312 usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
313 if (!info) {
314 ddf_fun_destroy(fun);
315 return ENOMEM;
316 }
317 info->address = address;
318 info->speed = speed;
319 info->fun = fun;
320 link_initialize(&info->link);
321
322 ddf_fun_set_ops(fun, &usb_ops);
323 list_foreach(mids->ids, iter) {
324 match_id_t *mid = list_get_instance(iter, match_id_t, link);
325 ddf_fun_add_match_id(fun, mid->id, mid->score);
326 }
327
328 int ret = ddf_fun_bind(fun);
329 if (ret != EOK) {
330 ddf_fun_destroy(fun);
331 return ret;
332 }
333
334 list_append(&info->link, &hc_dev->devices);
335 return EOK;
336}
337
338#define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
339do { \
340 match_id_t *mid = malloc(sizeof(match_id_t)); \
341 if (!mid) { \
342 clean_match_ids(list); \
343 return ENOMEM; \
344 } \
345 char *id = NULL; \
346 int ret = asprintf(&id, str, ##__VA_ARGS__); \
347 if (ret < 0) { \
348 clean_match_ids(list); \
349 free(mid); \
350 return ENOMEM; \
351 } \
352 mid->score = sc; \
353 mid->id = id; \
354 add_match_id(list, mid); \
355} while (0)
356
357
358/* This is a copy of lib/usbdev/src/recognise.c */
359static int create_match_ids(match_id_list_t *l,
360 usb_standard_device_descriptor_t *d)
361{
362 assert(l);
363 assert(d);
364
365 if (d->vendor_id != 0) {
366 /* First, with release number. */
367 ADD_MATCHID_OR_RETURN(l, 100,
368 "usb&vendor=%#04x&product=%#04x&release=%x.%x",
369 d->vendor_id, d->product_id, (d->device_version >> 8),
370 (d->device_version & 0xff));
371
372 /* Next, without release number. */
373 ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
374 d->vendor_id, d->product_id);
375 }
376
377 /* Class match id */
378 ADD_MATCHID_OR_RETURN(l, 50, "usb&class=%s",
379 usb_str_class(d->device_class));
380
381 /* As a last resort, try fallback driver. */
382 ADD_MATCHID_OR_RETURN(l, 10, "usb&fallback");
383
384 return EOK;
385
386}
387
388int hcd_ddf_remove_device(ddf_dev_t *device, usb_address_t id)
389{
390 assert(device);
391
392 hcd_t *hcd = dev_to_hcd(device);
393 assert(hcd);
394
395 hc_dev_t *hc_dev = dev_to_hc_dev(device);
396 assert(hc_dev);
397
398 fibril_mutex_lock(&hc_dev->guard);
399
400 usb_dev_t *victim = NULL;
401
402 list_foreach(hc_dev->devices, it) {
403 victim = list_get_instance(it, usb_dev_t, link);
404 if (victim->address == id)
405 break;
406 }
407 if (victim && victim->address == id) {
408 list_remove(&victim->link);
409 fibril_mutex_unlock(&hc_dev->guard);
410 const int ret = ddf_fun_unbind(victim->fun);
411 if (ret == EOK) {
412 ddf_fun_destroy(victim->fun);
413 hcd_release_address(hcd, id);
414 } else {
415 usb_log_warning("Failed to unbind device %d: %s\n",
416 id, str_error(ret));
417 }
418 return EOK;
419 }
420 return ENOENT;
421}
422
423int hcd_ddf_new_device(ddf_dev_t *device, usb_address_t *id)
424{
425 assert(device);
426
427 hcd_t *hcd = dev_to_hcd(device);
428 assert(hcd);
429
430 usb_speed_t speed = USB_SPEED_MAX;
431
432 /* This checks whether the default address is reserved and gets speed */
433 int ret = usb_endpoint_manager_get_info_by_address(&hcd->ep_manager,
434 USB_ADDRESS_DEFAULT, &speed);
435 if (ret != EOK) {
436 return ret;
437 }
438
439 static const usb_target_t default_target = {{
440 .address = USB_ADDRESS_DEFAULT,
441 .endpoint = 0,
442 }};
443
444 const usb_address_t address = hcd_request_address(hcd, speed);
445 if (address < 0)
446 return address;
447
448 const usb_target_t target = {{
449 .address = address,
450 .endpoint = 0,
451 }};
452
453 /* Add default pipe on default address */
454 ret = hcd_add_ep(hcd,
455 default_target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
456 CTRL_PIPE_MIN_PACKET_SIZE, CTRL_PIPE_MIN_PACKET_SIZE);
457
458 if (ret != EOK) {
459 hcd_release_address(hcd, address);
460 return ret;
461 }
462
463 /* Get max packet size for default pipe */
464 usb_standard_device_descriptor_t desc = { 0 };
465 static const usb_device_request_setup_packet_t get_device_desc_8 =
466 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
467
468 // TODO CALLBACKS
469 ssize_t got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
470 &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
471 "read first 8 bytes of dev descriptor");
472
473 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
474 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
475 hcd_release_address(hcd, address);
476 return got < 0 ? got : EOVERFLOW;
477 }
478
479 /* Register EP on the new address */
480 ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
481 desc.max_packet_size, desc.max_packet_size);
482 if (ret != EOK) {
483 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
484 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
485 hcd_release_address(hcd, address);
486 return ret;
487 }
488
489 /* Set new address */
490 const usb_device_request_setup_packet_t set_address =
491 SET_ADDRESS(target.address);
492
493 got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
494 NULL, 0, *(uint64_t *)&set_address, "set address");
495
496 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
497
498 if (got != 0) {
499 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
500 hcd_release_address(hcd, address);
501 return got;
502 }
503
504 /* Get std device descriptor */
505 static const usb_device_request_setup_packet_t get_device_desc =
506 GET_DEVICE_DESC(sizeof(desc));
507
508 got = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
509 &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
510 "read device descriptor");
511 if (ret != EOK) {
512 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
513 hcd_release_address(hcd, target.address);
514 return got < 0 ? got : EOVERFLOW;
515 }
516
517 /* Create match ids from the device descriptor */
518 match_id_list_t mids;
519 init_match_ids(&mids);
520
521 ret = create_match_ids(&mids, &desc);
522 if (ret != EOK) {
523 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
524 hcd_release_address(hcd, target.address);
525 return ret;
526 }
527
528 /* Register device */
529 ret = hcd_ddf_add_usb_device(device, address, speed, NULL, &mids);
530 clean_match_ids(&mids);
531 if (ret != EOK) {
532 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
533 hcd_release_address(hcd, target.address);
534 return ret;
535 }
536 if (ret == EOK && id)
537 *id = target.address;
538
539 return ret;
540}
541
542/** Announce root hub to the DDF
543 *
544 * @param[in] device Host controller ddf device
545 * @return Error code
546 */
547int hcd_ddf_setup_root_hub(ddf_dev_t *device)
548{
549 assert(device);
550 hcd_t *hcd = dev_to_hcd(device);
551 assert(hcd);
552
553 const usb_speed_t speed = hcd->ep_manager.max_speed;
554
555 hcd_reserve_default_address(hcd, speed);
556 const int ret = hcd_ddf_new_device(device, NULL);
557 hcd_release_default_address(hcd);
558 return ret;
559}
560
561/** Initialize hc structures.
562 *
563 * @param[in] device DDF instance of the device to use.
564 *
565 * This function does all the ddf work for hc driver.
566 */
567int hcd_ddf_setup_device(ddf_dev_t *device, ddf_fun_t **hc_fun,
568 usb_speed_t max_speed, size_t bw, bw_count_func_t bw_count)
569{
570 if (!device)
571 return EBADMEM;
572
573 int ret = ENOMEM;
574 hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
575 if (instance == NULL) {
576 usb_log_error("Failed to allocate HCD ddf structure.\n");
577 return ENOMEM;
578 }
579 list_initialize(&instance->devices);
580 fibril_mutex_initialize(&instance->guard);
581
582 instance->hc_fun = ddf_fun_create(device, fun_exposed, "hc");
583 if (!instance->hc_fun) {
584 usb_log_error("Failed to create HCD ddf fun.\n");
585 goto err_destroy_fun;
586 }
587
588 hcd_t *hcd = ddf_fun_data_alloc(instance->hc_fun, sizeof(hcd_t));
589 if (!instance->hc_fun) {
590 usb_log_error("Failed to allocate HCD ddf fun data.\n");
591 goto err_destroy_fun;
592 }
593
594 hcd_init(hcd, max_speed, bw, bw_count);
595
596 ret = ddf_fun_bind(instance->hc_fun);
597 if (ret != EOK) {
598 usb_log_error("Failed to bind hc_fun: %s.\n", str_error(ret));
599 goto err_destroy_fun;
600 }
601
602 ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
603 if (ret != EOK) {
604 usb_log_error("Failed to add fun to category: %s.\n",
605 str_error(ret));
606 ddf_fun_unbind(instance->hc_fun);
607 goto err_destroy_fun;
608 }
609
610 /* HC should be ok at this point (except it can't do anything) */
611 if (hc_fun)
612 *hc_fun = instance->hc_fun;
613 return EOK;
614
615err_destroy_fun:
616 ddf_fun_destroy(instance->hc_fun);
617 instance->hc_fun = NULL;
618 return ret;
619}
620
621/**
622 * @}
623 */
Note: See TracBrowser for help on using the repository browser.