2 * Copyright (c) 2013 Jan Vesely
3 * Copyright (c) 2018 Ondrej Hlavaty, Michal Staruch, Petr Manek
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
30 /** @addtogroup libusbhost
34 * Helpers to work with the DDF interface.
40 #include <ddf/driver.h>
41 #include <ddf/interrupt.h>
42 #include <device/hw_res_parsed.h>
44 #include <str_error.h>
45 #include <usb/classes/classes.h>
46 #include <usb/debug.h>
47 #include <usb/descriptor.h>
49 #include <usb/dma_buffer.h>
50 #include <usb_iface.h>
51 #include <usbhc_iface.h>
56 #include "ddf_helpers.h"
59 * DDF usbhc_iface callback. Passes the endpoint descriptors, fills the pipe
60 * descriptor according to the contents of the endpoint.
62 * @param[in] fun DDF function of the device in question.
63 * @param[out] pipe_desc The pipe descriptor to be filled.
64 * @param[in] endpoint_desc Endpoint descriptors from the device.
67 static errno_t
register_endpoint(ddf_fun_t
*fun
, usb_pipe_desc_t
*pipe_desc
,
68 const usb_endpoint_descriptors_t
*ep_desc
)
71 hc_device_t
*hcd
= dev_to_hcd(ddf_fun_get_dev(fun
));
72 device_t
*dev
= ddf_fun_data_get(fun
);
78 const int err
= bus_endpoint_add(dev
, ep_desc
, &ep
);
83 pipe_desc
->endpoint_no
= ep
->endpoint
;
84 pipe_desc
->direction
= ep
->direction
;
85 pipe_desc
->transfer_type
= ep
->transfer_type
;
86 pipe_desc
->max_transfer_size
= ep
->max_transfer_size
;
87 pipe_desc
->transfer_buffer_policy
= ep
->transfer_buffer_policy
;
95 * DDF usbhc_iface callback. Unregister endpoint that makes the other end of
98 * @param fun DDF function of the device in question.
99 * @param pipe_desc Pipe description.
100 * @return Error code.
102 static errno_t
unregister_endpoint(ddf_fun_t
*fun
, const usb_pipe_desc_t
*pipe_desc
)
105 hc_device_t
*hcd
= dev_to_hcd(ddf_fun_get_dev(fun
));
106 device_t
*dev
= ddf_fun_data_get(fun
);
111 endpoint_t
*ep
= bus_find_endpoint(dev
, pipe_desc
->endpoint_no
, pipe_desc
->direction
);
115 const errno_t err
= bus_endpoint_remove(ep
);
117 endpoint_del_ref(ep
);
122 * DDF usbhc_iface callback. Calls the respective bus operation directly.
124 * @param fun DDF function of the device (hub) requesting the address.
126 static errno_t
default_address_reservation(ddf_fun_t
*fun
, bool reserve
)
129 hc_device_t
*hcd
= dev_to_hcd(ddf_fun_get_dev(fun
));
130 device_t
*dev
= ddf_fun_data_get(fun
);
135 usb_log_debug("Device %d %s default address", dev
->address
, reserve
? "requested" : "releasing");
137 return bus_reserve_default_address(hcd
->bus
, dev
);
139 bus_release_default_address(hcd
->bus
, dev
);
145 * DDF usbhc_iface callback. Calls the bus operation directly.
147 * @param fun DDF function of the device (hub) requesting the address.
148 * @param speed USB speed of the new device
150 static errno_t
device_enumerate(ddf_fun_t
*fun
, unsigned port
, usb_speed_t speed
)
153 ddf_dev_t
*hc
= ddf_fun_get_dev(fun
);
155 hc_device_t
*hcd
= dev_to_hcd(hc
);
157 device_t
*hub
= ddf_fun_data_get(fun
);
162 if (!usb_speed_is_valid(speed
))
165 usb_log_debug("Hub %d reported a new %s speed device on port: %u",
166 hub
->address
, usb_str_speed(speed
), port
);
168 device_t
*dev
= hcd_ddf_fun_create(hcd
, speed
);
170 usb_log_error("Failed to create USB device function.");
175 dev
->tier
= hub
->tier
+ 1;
179 if ((err
= bus_device_enumerate(dev
))) {
180 usb_log_error("Failed to initialize USB dev memory structures.");
185 * If the driver didn't name the dev when enumerating,
186 * do it in some generic way.
188 if (!ddf_fun_get_name(dev
->fun
)) {
189 bus_device_set_default_name(dev
);
192 if ((err
= ddf_fun_bind(dev
->fun
))) {
193 usb_log_error("Device(%d): Failed to register: %s.", dev
->address
, str_error(err
));
200 hcd_ddf_fun_destroy(dev
);
204 static errno_t
device_remove(ddf_fun_t
*fun
, unsigned port
)
207 device_t
*hub
= ddf_fun_data_get(fun
);
209 usb_log_debug("Hub `%s' reported removal of device on port %u",
210 ddf_fun_get_name(fun
), port
);
212 device_t
*victim
= NULL
;
214 fibril_mutex_lock(&hub
->guard
);
215 list_foreach(hub
->devices
, link
, device_t
, it
) {
216 if (it
->port
== port
) {
221 fibril_mutex_unlock(&hub
->guard
);
224 usb_log_warning("Hub '%s' tried to remove non-existent"
225 " device.", ddf_fun_get_name(fun
));
230 assert(victim
->port
== port
);
231 assert(victim
->hub
== hub
);
233 bus_device_gone(victim
);
238 * Gets description of the device that is calling.
240 * @param[in] fun Device function.
241 * @param[out] desc Device descriptor to be filled.
242 * @return Error code.
244 static errno_t
get_device_description(ddf_fun_t
*fun
, usb_device_desc_t
*desc
)
247 device_t
*dev
= ddf_fun_data_get(fun
);
253 *desc
= (usb_device_desc_t
) {
254 .address
= dev
->address
,
257 .handle
= ddf_fun_get_handle(fun
),
264 * Transfer issuing interface function.
266 * @param fun DDF function.
267 * @param target Communication target.
268 * @param dir Communication direction.
269 * @param setup_data Data to use in setup stage (control transfers).
270 * @param data Pointer to data buffer.
271 * @param size Size of the data buffer.
272 * @param callback Function to call on communication end.
273 * @param arg Argument passed to the callback function.
274 * @return Error code.
276 static errno_t
transfer(ddf_fun_t
*fun
,
277 const usbhc_iface_transfer_request_t
*ifreq
,
278 usbhc_iface_transfer_callback_t callback
, void *arg
)
281 device_t
*dev
= ddf_fun_data_get(fun
);
284 const usb_target_t target
= {
286 .address
= dev
->address
,
287 .endpoint
= ifreq
->endpoint
,
288 .stream
= ifreq
->stream
,
292 if (!usb_target_is_valid(&target
))
295 if (ifreq
->offset
> 0 && ifreq
->size
== 0)
298 if (ifreq
->size
> 0 && !dma_buffer_is_set(&ifreq
->buffer
))
301 if (!callback
&& arg
)
304 const transfer_request_t request
= {
307 .buffer
= ifreq
->buffer
,
308 .offset
= ifreq
->offset
,
310 .setup
= ifreq
->setup
,
311 .on_complete
= callback
,
313 .name
= (ifreq
->dir
== USB_DIRECTION_IN
) ? "READ" : "WRITE",
316 return bus_issue_transfer(dev
, &request
);
319 /** USB device interface */
320 static usb_iface_t usb_iface
= {
321 .get_my_description
= get_device_description
,
324 /** USB host controller interface */
325 static usbhc_iface_t usbhc_iface
= {
326 .default_address_reservation
= default_address_reservation
,
328 .device_enumerate
= device_enumerate
,
329 .device_remove
= device_remove
,
331 .register_endpoint
= register_endpoint
,
332 .unregister_endpoint
= unregister_endpoint
,
334 .transfer
= transfer
,
337 /** Standard USB device interface) */
338 static ddf_dev_ops_t usb_ops
= {
339 .interfaces
[USB_DEV_IFACE
] = &usb_iface
,
340 .interfaces
[USBHC_DEV_IFACE
] = &usbhc_iface
,
345 #define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
347 match_id_t *mid = malloc(sizeof(match_id_t)); \
349 clean_match_ids(list); \
353 int ret = asprintf(&id, str, ##__VA_ARGS__); \
355 clean_match_ids(list); \
361 add_match_id(list, mid); \
364 /* This is a copy of lib/usbdev/src/recognise.c */
365 static errno_t
create_match_ids(match_id_list_t
*l
,
366 usb_standard_device_descriptor_t
*d
)
371 if (d
->vendor_id
!= 0) {
372 /* First, with release number. */
373 ADD_MATCHID_OR_RETURN(l
, 100,
374 "usb&vendor=%#06x&product=%#06x&release=%x.%x",
375 d
->vendor_id
, d
->product_id
, (d
->device_version
>> 8),
376 (d
->device_version
& 0xff));
378 /* Next, without release number. */
379 ADD_MATCHID_OR_RETURN(l
, 90, "usb&vendor=%#06x&product=%#06x",
380 d
->vendor_id
, d
->product_id
);
384 ADD_MATCHID_OR_RETURN(l
, 50, "usb&class=%s",
385 usb_str_class(d
->device_class
));
387 /* As a last resort, try fallback driver. */
388 ADD_MATCHID_OR_RETURN(l
, 10, "usb&fallback");
393 device_t
*hcd_ddf_fun_create(hc_device_t
*hc
, usb_speed_t speed
)
395 /* Create DDF function for the new device */
396 ddf_fun_t
*fun
= ddf_fun_create(hc
->ddf_dev
, fun_inner
, NULL
);
400 ddf_fun_set_ops(fun
, &usb_ops
);
402 /* Create USB device node for the new device */
403 device_t
*dev
= ddf_fun_data_alloc(fun
, hc
->bus
->device_size
);
405 ddf_fun_destroy(fun
);
409 bus_device_init(dev
, hc
->bus
);
415 void hcd_ddf_fun_destroy(device_t
*dev
)
419 ddf_fun_destroy(dev
->fun
);
422 errno_t
hcd_ddf_setup_match_ids(device_t
*device
, usb_standard_device_descriptor_t
*desc
)
425 match_id_list_t mids
;
427 init_match_ids(&mids
);
429 /* Create match ids from the device descriptor */
430 usb_log_debug("Device(%d): Creating match IDs.", device
->address
);
431 if ((err
= create_match_ids(&mids
, desc
))) {
435 list_foreach(mids
.ids
, link
, const match_id_t
, mid
) {
436 ddf_fun_add_match_id(device
->fun
, mid
->id
, mid
->score
);
442 /** Initialize hc structures.
444 * @param[in] device DDF instance of the device to use.
445 * @param[in] max_speed Maximum supported USB speed.
446 * @param[in] bw available bandwidth.
447 * @param[in] bw_count Function to compute required ep bandwidth.
449 * @return Error code.
450 * This function does all the ddf work for hc driver.
452 errno_t
hcd_ddf_setup_hc(ddf_dev_t
*device
, size_t size
)
456 hc_device_t
*instance
= ddf_dev_data_alloc(device
, size
);
457 if (instance
== NULL
) {
458 usb_log_error("Failed to allocate HCD ddf structure.");
461 instance
->ddf_dev
= device
;
463 errno_t ret
= ENOMEM
;
464 instance
->ctl_fun
= ddf_fun_create(device
, fun_exposed
, "ctl");
465 if (!instance
->ctl_fun
) {
466 usb_log_error("Failed to create HCD ddf fun.");
467 goto err_destroy_fun
;
470 ret
= ddf_fun_bind(instance
->ctl_fun
);
472 usb_log_error("Failed to bind ctl_fun: %s.", str_error(ret
));
473 goto err_destroy_fun
;
476 ret
= ddf_fun_add_to_category(instance
->ctl_fun
, USB_HC_CATEGORY
);
478 usb_log_error("Failed to add fun to category: %s.",
480 ddf_fun_unbind(instance
->ctl_fun
);
481 goto err_destroy_fun
;
484 /* HC should be ok at this point (except it can't do anything) */
488 ddf_fun_destroy(instance
->ctl_fun
);
489 instance
->ctl_fun
= NULL
;
493 void hcd_ddf_clean_hc(hc_device_t
*hcd
)
495 if (ddf_fun_unbind(hcd
->ctl_fun
) == EOK
)
496 ddf_fun_destroy(hcd
->ctl_fun
);
499 /** Call the parent driver with a request to enable interrupt
501 * @param[in] device Device asking for interrupts
502 * @param[in] inum Interrupt number
503 * @return Error code.
505 errno_t
hcd_ddf_enable_interrupt(hc_device_t
*hcd
, int inum
)
507 async_sess_t
*parent_sess
= ddf_dev_parent_sess_get(hcd
->ddf_dev
);
508 if (parent_sess
== NULL
)
511 return hw_res_enable_interrupt(parent_sess
, inum
);
514 errno_t
hcd_ddf_get_registers(hc_device_t
*hcd
, hw_res_list_parsed_t
*hw_res
)
516 async_sess_t
*parent_sess
= ddf_dev_parent_sess_get(hcd
->ddf_dev
);
517 if (parent_sess
== NULL
)
520 hw_res_list_parsed_init(hw_res
);
521 const errno_t ret
= hw_res_get_list_parsed(parent_sess
, hw_res
, 0);
523 hw_res_list_parsed_clean(hw_res
);