blob: 2cc150b19869336f771662863299722e4c8dbf78 [file] [log] [blame] [view]
<!--
(C) Copyright 2019 The Fuchsia Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
# Lifecycle of a USB request
Caution: This page may contain information that is specific to the legacy
version of the driver framework (DFv1).
## Glossary
* HCI -- Host Controller Interface: A host controller interface driver is
responsible for queueing USB requests to hardware, and managing the state
of connected devices while operating as a USB host.
* DCI -- Device controller interface: A device controller interface is
responsible for queueing USB requests to a USB host that the device is
connected to.
## Allocation
The first step in a USB request's lifecycle is allocation. USB requests contain
data from all of the drivers in the request stack in a single allocation. Each
driver that is upstream of a USB device driver should provide a
[GetRequestSize](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#96) method --
which returns the size it needs to contain its local request context. When a
USB device driver allocates a request, it should invoke this method to
determine the size of the parent's request context.
Note: It is important to ensure that the lifetime of a request does not exceed
the lifetime of your driver. If your driver is released with outstanding
requests, you will encounter a use-after-free scenario when the parent tries to
send the request back to you by invoking your callback. Drivers should not
reply to unbind until all outstanding requests have been returned.
### C example {#c-example}
```c
size_t parent_req_size = usb_get_request_size(&usb);
usb_request_t* request;
usb_request_alloc(&request, transfer_length, endpoint_addr,
parent_req_size+sizeof(your_context_struct_t));
usb_request_complete_callback_t complete = {
.callback = usb_request_complete,
.ctx = your_context_pointer,
};
your_context_pointer.completion = complete;
usb_request_queue(&usb, request, &complete);
...
void usb_request_complete(void* cookie, usb_request_t* request) {
your_context_struct_t data;
// memcpy is needed to ensure alignment
memcpy(&data, cookie, sizeof(data));
// Do something here to process the response
// ...
// Requeue the request
usb_request_queue(&data.usb, request, &data.completion);
}
```
### C++ example
```c++
parent_req_size = usb.GetRequestSize();
std::optional<usb::Request<void>> req;
status = usb::Request<void>::Alloc(&req, transfer_length,
endpoint_addr, parent_req_size);
usb_request_complete_callback_t complete = {
.callback =
[](void* ctx, usb_request_t* request) {
static_cast<YourDeviceClass*>(ctx)->YourHandlerFunction(request);
},
.ctx = this,
};
usb.RequestQueue(req->take(), &complete);
```
### C++ example (with lambdas)
```c++
size_t parent_size = usb_.GetRequestSize();
using Request = usb::CallbackRequest<sizeof(std::max_align_t) * 4>;
std::optional<Request> request;
Request::Alloc(&request, max_packet_size, endpoint_address,
parent_size, [=](Request request) {
// Do some processing here.
// ...
// Re-queue the request
Request::Queue(std::move(request), usb_client_);
});
```
## Submission
You can submit requests using the
[RequestQueue](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#22) method,
or -- in the case of CallbackRequests (as seen [here](#c-example)), using
`Request::Queue` or simply `request.Queue(client)`. In all cases, ownership of
the USB request is transferred to the parent driver (usually `usb-device`).
The typical lifecycle of a USB request (from a device driver to either a host
controller or device controller is as follows):
* The USB device driver queues the request
* The `usb-device` core driver receives the request, and now owns the request
object.
* The `usb-device` core driver injects its own callback (if the direct flag
is not set), or passes through the request (if the direct flag is set) to
the HCI or DCI driver.
* The HCI or DCI driver now owns the request. The HCI or DCI driver submits
this request to hardware.
* The request completes. When this happens, if the direct flag was set, the
callback in the device driver is invoked, and the device driver now owns
the request. If the direct flag is not set, the usb-device (core) driver
now owns the request.
* If the core driver owns the request; it is added to a queue for dispatch by
another thread.
* The core driver eventually invokes the callback, and the request is now
owned by the device driver. The device driver can now re-submit the
request.
## Cancellation
Requests may be cancelled by invoking
[CancelAll](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#89). When
[CancelAll](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#89) completes, all
requests are owned by the caller. Drivers implementing a
[CancelAll](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#89) function (such
as the usb-device core driver and any HCI/DCI drivers) are responsible for
transferring ownership to their children with a `ZX_ERR_CANCELLED` status code.
## Implementation notes for writers of HCI, DCI, or filter drivers
### Implementing [GetRequestSize](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#96)
The value returned by
[GetRequestSize](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#96) should
equal the value of your parent's
[GetRequestSize](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#96) + the size
of your request context, including any padding that would be necessary to
ensure proper alignment of your data structures (if applicable). If you are
implementing an HCI or DCI driver, you must include `sizeof(usb_request_t)` in
your size calculation in addition to any other data structures that you are
storing. `usb_request_t` has no special alignment requirements, so it is not
necessary to add padding for that structure.
### Implementing [RequestQueue](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#22)
Implementors of
[RequestQueue](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#22) temporarily
assumes ownership of the USB request from its client driver. As an implementor
of [RequestQueue](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#22), you are
allowed to access all fields of the `usb_request_t`, as well as any private
data that you have appended to the `usb_request_t` structure (by requesting
additional space through
[GetRequestSize](/sdk/banjo/fuchsia.hardware.usb/usb.fidl#96)), but you
are not allowed to modify any data outside of your private area, which starts
at `parent_req_size` bytes (past the end of `usb_request_t`).
## Example USB request stack (HCI)
xHCI (host controller) -> `usb-bus` -> `usb-device` (core USB device driver) ->
`usb-mass-storage`
## Example USB request stack (DCI)
`dwc2` (device-side controller) -> `usb-peripheral` (peripheral core driver) ->
`usb-function` (core function driver) -> `cdc-eth-function` (ethernet
peripheral mode driver)