术语库
- HCI - 主机控制器接口:主机控制器接口驱动程序负责将发送到硬件的 USB 请求加入队列,以及在作为 USB 主机运行时管理已连接设备的状态。
- DCI - 设备控制器接口:设备控制器接口负责将发送到设备所连接的 USB 主机的 USB 请求加入队列。
分配
USB 请求生命周期的第一步是分配。USB 请求中包含来自单次分配中请求堆栈中所有驱动程序的数据。USB 设备驱动程序上游的每个驱动程序都应提供 GetRequestSize 方法,该方法会返回包含本地请求上下文所需的大小。当 USB 设备驱动程序分配请求时,应调用此方法来确定父级请求上下文的大小。
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++ 示例
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++ 示例(使用 lambda)
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_);
});
提交
您可以使用 RequestQueue 方法提交请求,如果是 CallbackRequests(如此处所示),则可以使用 Request::Queue
或直接使用 request.Queue(client)
。在任何情况下,USB 请求的所有权都会转移给父级驱动程序(通常为 usb-device
)。
USB 请求(从设备驱动程序到主机控制器或设备控制器)的典型生命周期如下:
- USB 设备驱动程序将请求加入队列
usb-device
核心驱动程序会收到请求,并且现在拥有请求对象。usb-device
核心驱动程序会注入自己的回调(如果未设置直接标志),或将请求(如果设置了直接标志)传递给 HCI 或 DCI 驱动程序。- HCI 或 DCI 驱动程序现在拥有请求。HCI 或 DCI 驱动程序会将此请求提交到硬件。
- 请求完成。发生这种情况时,如果已设置直接标记,系统会调用设备驱动程序中的回调,相应请求便会由设备驱动程序所有。如果未设置直接标记,则 USB 设备(核心)驱动程序现在会拥有请求。
- 如果核心驱动程序拥有请求,则会将其添加到队列中,以由其他线程分派。
- 核心驱动程序最终调用回调,请求现在由设备驱动程序拥有。设备驱动程序现在可以重新提交请求。
取消
通过调用 CancelAll 可以取消请求。CancelAll 完成后,所有请求都将归调用方所有。实现 CancelAll 函数的驱动程序(例如 USB 设备核心驱动程序和任何 HCI/DCI 驱动程序)负责通过 ZX_ERR_CANCELLED
状态代码将所有权转移给其子项。
HCI、DCI 或过滤器驱动程序编写人员的实现注意事项
实现 GetRequestSize
GetRequestSize 返回的值应等于父级的 GetRequestSize 的值 + 请求上下文的大小,包括确保数据结构正确对齐所需的任何内边距(如果适用)。如果您要实现 HCI 或 DCI 驱动程序,那么除了要存储的任何其他数据结构之外,还必须在计算大小时添加 sizeof(usb_request_t)
。usb_request_t
没有特殊的对齐要求,因此不必为该结构添加内边距。
实现 RequestQueue
RequestQueue 的实现人员会暂时获得来自其客户端驱动程序的 USB 请求的所有权。作为 RequestQueue 的实现者,您可以访问 usb_request_t
的所有字段,以及已附加到 usb_request_t
结构的任何私有数据(通过通过 GetRequestSize 请求额外空间),但不能修改专用区域以外的任何数据,该区域从 parent_req_size
字节开始(usb_request_t
结束为止)。
USB 请求堆栈 (HCI) 示例
xHCI(主机控制器)-> usb-bus
-> usb-device
(核心 USB 设备驱动程序)-> usb-mass-storage
USB 请求堆栈 (DCI) 示例
dwc2
(设备端控制器)-> usb-peripheral
(外设核心驱动程序)-> usb-function
(核心功能驱动程序)-> cdc-eth-function
(以太网外设模式驱动程序)