diff --git a/docs/en/api-reference/peripherals/usb_host.rst b/docs/en/api-reference/peripherals/usb_host.rst index c4a10d46218..2f0756b43ff 100644 --- a/docs/en/api-reference/peripherals/usb_host.rst +++ b/docs/en/api-reference/peripherals/usb_host.rst @@ -1,6 +1,8 @@ USB Host ======== +:link_to_translation:`zh_CN:[中文]` + The document provides information regarding the USB Host Library. This document is split into the following sections: .. contents:: Sections @@ -11,29 +13,30 @@ The document provides information regarding the USB Host Library. This document Overview -------- -The USB Host Library (hereinafter referred to as the Host Library) is the lowest public facing API layer of the ESP-IDF USB Host Stack. In most cases, applications that require USB Host functionality will not need to interface with the Host Library directly. Instead, most applications will use the API provided by a host class driver that is implemented on top of the Host Library. +The USB Host Library (hereinafter referred to as the Host Library) is the lowest layer of the USB Host stack that exposes a public facing API. In most cases, applications that require USB Host functionality do not need to interface with the Host Library directly. Instead, most applications use the API provided by a host class driver that is implemented on top of the Host Library. -However, users may want to use the Host Library directly for some of (but not limited to) the following reasons: +However, you may want to use the Host Library directly for some of (but not limited to) the following reasons: -- The user needs to implement a custom host class driver such as a vendor specific class driver -- The user has a requirement for a lower level of abstraction due to resource/latency requirements +- Implementation of a custom host class driver +- Usage of lower level USB Host API Features & Limitations ^^^^^^^^^^^^^^^^^^^^^^ The Host Library has the following features: -- Supports Full Speed (FS) and Low Speed (LS) Devices -- Supports all four transfer types (Control, Bulk, Interrupt, and Isochronous) -- Allows multiple class drivers to run simultaneously (i.e., multiple clients of the Host Library) -- A single device can be used by multiple clients simultaneously (e.g., composite devices) -- The Host Library itself (and the underlying Host Stack) does not internally instantiate any OS tasks. The number of tasks are entirely controlled by how the Host Library interface is used. However, a general rule of thumb regarding the number of tasks is ``(the number of host class drivers running + 1)``. +- Supports Full Speed (FS) and Low Speed (LS) Devices. +- Supports all four transfer types, i.e., Control, Bulk, Interrupt, and Isochronous. +- Allows multiple class drivers to run simultaneously, i.e., multiple clients of the Host Library. +- A single device can be used by multiple clients simultaneously, e.g., composite devices. +- The Host Library itself and the underlying Host Stack does not internally instantiate any OS tasks. The number of tasks is entirely controlled by how the Host Library interface is used. However, a general rule of thumb regarding the number of tasks is ``(the number of host class drivers running + 1)``. -Currently, the Host Library (and the underlying Host Stack) has the following limitations: +Currently, the Host Library and the underlying Host Stack has the following limitations: - Only supports a single device, but the Host Library's API is designed for multiple device support. -- Only supports Asynchronous transfers -- Transfer timeouts are not supported yet +- Only supports Asynchronous transfers. +- Only supports using the first configuration found. Changing to other configurations is not supported yet. +- Transfer timeouts are not supported yet. .. -------------------------------------------------- Architecture ----------------------------------------------------- @@ -58,43 +61,43 @@ The diagram above shows the key entities that are involved when implementing USB Host Library ^^^^^^^^^^^^ -The Host Library is the a lowest public facing layer of the USB Host Stack. Any other IDF component (such as a class driver or a user component) that needs to communicate with a connected USB device can only do so using the Host Library API either directly or indirectly. +The Host Library is the lowest public-facing API layer of the ESP-IDF USB Host Stack. Any other ESP-IDF component (such as a class driver or a user component) that needs to communicate with a connected USB device can only do so using the Host Library API either directly or indirectly. The Host Library's API is split into two sub-sets, namely the **Library API** and **Client API**. - The Client API handles the communication between a client of the Host Library and one or more USB devices. The Client API should only be called by registered clients of the Host Library. -- The Library API handles all of the Host Library processing that is not specific to a single client (e.g., device enumeration). Usually, the library API is called by a Host Library Daemon Task. +- The Library API handles all of the Host Library processing that is not specific to a single client, such as device enumeration. Usually, the library API is called by a Host Library Daemon Task. Clients ^^^^^^^ -A client of the Host Library is a software component (such as a host class driver or user component) that uses the Host Library to communicate with a USB device. Generally each client has a one-to-one relation with a task, meaning that for a particular client, all of its Client API calls should be done from the context of the same task. +A client of the Host Library is a software component, such as a host class driver or user component, which utilizes the Host Library to establish communication with a USB device. Generally, each client has a one-to-one relation with a task. This implies that all Client API calls pertaining to a specific client must originate from the context of the same task. -By organizing the software components that use the Host Library's into clients, the Host Library can delegate the handling of all client events (i.e., the events specific to that client) to the client's task. In other words, each client task is responsible for all the required processing and event handling associated with the USB communication that the client initiates. +By organizing the software components that use the Host Library's into clients, the Host Library can delegate the handling of all events specific to that client to the client's task. In other words, each client task is responsible for all the required processing and event handling associated with the USB communication that the client initiates. Daemon Task ^^^^^^^^^^^ -Although the Host Library delegates the handling of client events to the clients themselves, there are still Library events (i.e., events that are not specific to a client) that need to be handled. Library event handling can include things such as: +Although the Host Library delegates the handling of client events to the clients themselves, there are still Library events – events that are not specific to any particular client – that need to be handled. Library event handling can include things such as: - Handling USB device connection, enumeration, and disconnection - Rerouting control transfers to/from clients - Forwarding events to clients -Therefore, in addition to the client tasks, the Host Library also requires a task (usually the Host Library Daemon Task) to handle all of the library events. +Therefore, in addition to the client tasks, the Host Library also requires a task, which is usually the Host Library Daemon Task, to handle all of the library events. Devices ^^^^^^^ -The Host Library hides the details of device handling (such as connection, memory allocation, and enumeration) from the clients. The clients are provided only with a list of already connected and enumerated devices to choose from. During enumeration, each device is automatically configured to use the first configuration found (i.e., the first configuration descriptor returned on a Get Configuration Descriptor request). For most standard devices, the first configuration will have a ``bConfigurationValue`` of ``1``. +The Host Library shields clients from the details of device handling, encompassing details such as connection, memory allocation, and enumeration. The clients are provided only with a list of already connected and enumerated devices to choose from. During enumeration, each device is automatically configured to use the first configuration found, namely, the first configuration descriptor returned on a Get Configuration Descriptor request. For most standard devices, the first configuration will have a ``bConfigurationValue`` of ``1``. -It is possible for a two or more clients to simultaneously communicate with the same device as long as they are not communicating to the same interface. However, multiple clients can simultaneously communicate with the same device's default endpoint (EP0), which will result in their control transfers being serialized. +It is possible for two or more clients to simultaneously communicate with the same device as long as they are not communicating to the same interface. However, multiple clients can simultaneously communicate with the same device's default endpoint (i.e., EP0), which will result in their control transfers being serialized. For a client to communicate with a device, the client must: #. Open the device using the device's address. This lets the Host Library know that the client is using that device. #. Claim the interface(s) that will be used for communication. This prevents other clients from claiming the same interface(s). -#. Send transfers to the endpoints in the claimed interface. The client's task is responsible for handling its own processing and events. +#. Send transfers to the endpoints of claimed interfaces. The client's task is responsible for handling its own processing and events related to USB device communication. .. ------------------------------------------------------ Usage -------------------------------------------------------- @@ -102,7 +105,7 @@ For a client to communicate with a device, the client must: Usage ----- -The Host Library (and the underlying Host Stack) will not create any tasks. All tasks (i.e., the client tasks and the Daemon Task) will need to be created by the class drivers or the user. Instead, the Host Library provides two event handler functions that will handle all of the required Host Library processing, thus these functions should be called repeatedly from the client tasks and the Daemon Task. Therefore, the implementation of client tasks and the Daemon Task will be the largely centered around the invocation of these event handler functions. +The Host Library and the underlying Host Stack will not create any tasks. All tasks, namely the client tasks and the Daemon Task, need to be created by the class drivers or the user. Instead, the Host Library provides two event handler functions that handle all of the required Host Library processing, thus these functions should be called repeatedly from the client tasks and the Daemon Task. Therefore, the implementation of client tasks and the Daemon Task will be largely centered around the invocation of these event handler functions. Host Library & Daemon Task ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,9 +113,9 @@ Host Library & Daemon Task Basic Usage """"""""""" -The Host Library API provides :cpp:func:`usb_host_lib_handle_events` to handle library events. This function should be called repeatedly, typically from the daemon task. Some notable features regarding :cpp:func:`usb_host_lib_handle_events` are: +The Host Library API provides :cpp:func:`usb_host_lib_handle_events` to handle library events. This function should be called repeatedly, typically from the Daemon Task. Some notable features regarding :cpp:func:`usb_host_lib_handle_events` are: -- The function can block until a library event needs handling +- The function can block until a library event needs handling. - Event flags are returned on each invocation. These event flags are useful for knowing when the Host Library can be uninstalled. A bare-bones Daemon Task would resemble something like the following code snippet: @@ -140,7 +143,8 @@ A bare-bones Daemon Task would resemble something like the following code snippe } .. note:: - See the :example:`peripherals/usb/host/usb_host_lib` example for a full implementation of the Daemon Task + + See the :example:`peripherals/usb/host/usb_host_lib` example for full implementation of the Daemon Task. Lifecycle """"""""" @@ -152,38 +156,38 @@ Lifecycle Graph of Typical USB Host Library Lifecycle -The graph above illustrates the typical lifecycle of the Host Library with multiple clients and devices. Specifically, the example involves... +The graph above illustrates the typical lifecycle of the Host Library with multiple clients and devices. Specifically, the example involves: -- two registered clients (Client 1 and Client 2) +- two registered clients (Client 1 and Client 2). - two connected devices (Device 1 and Device 2), where Client 1 communicates with Device 1 and Client 2 communicates with Device 2. -With reference the graph above, the typical lifecycle involves the following key stages. +With reference to the graph above, the typical lifecycle involves the following key stages. 1. The Host Library is installed by calling :cpp:func:`usb_host_install`. - Installation must be done before any other Host Library API is called. - - Where :cpp:func:`usb_host_install` is called (e.g., from the Daemon Task or another task) will depend on the synchronization logic between the Daemon Task, client tasks, and the rest of the system. + - Where :cpp:func:`usb_host_install` is called (e.g., from the Daemon Task or another task) depends on the synchronization logic between the Daemon Task, client tasks, and the rest of the system. 2. Once the Host Library is installed, the clients can be registered by calling :cpp:func:`usb_host_client_register`. - - This is typically called from the client task (where the client task waits for a signal from the Daemon Task). + - This is typically called from the client task, where the client task waits for a signal from the Daemon Task. - This can be called elsewhere if necessary as long it is called after :cpp:func:`usb_host_install`. 3. Device 1 connects and is then enumerated. - - Each registered client (in this case Client 1 and Client 2) are notified of the new device by way of the :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event. + - Each registered client (in this case Client 1 and Client 2) is notified of the new device by way of the :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event. - Client 1 opens Device 1 and begins communication with it. 4. Similarly Device 2 connects and is enumerated. - - Client 1 and 2 are notified of a new device (via a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event). + - Client 1 and 2 are notified of a new device via a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` event. - Client 2 opens Device 2 and begins communication with it. 5. Device 1 suddenly disconnects. - Client 1 is notified by way of :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` and begins its cleanup. - Client 2 is not notified as it has not opened Device 1. -6. Client 1 completes its clean up and deregisters by calling :cpp:func:`usb_host_client_deregister`. +6. Client 1 completes its cleanup and deregisters by calling :cpp:func:`usb_host_client_deregister`. - This is typically called from the client task before the task exits. - - This can be called elsewhere if necessary as long as Client 1 has already completed its clean up. + - This can be called elsewhere if necessary as long as Client 1 has already completed its cleanup. 7. Client 2 completes its communication with Device 2. Client 2 then closes Device 2 and deregisters itself. - The Daemon Task is notified of the deregistration of all clients by way the :c:macro:`USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS` event flag as Client 2 is the last client to deregister. - - Device 2 is still allocated (i.e., not freed) as it is still connected albeit not currently opened by any client. -8. The Daemon Task decides to cleanup as there are no more clients. + - Device 2 is still allocated (i.e., not freed), as it is still connected albeit not currently opened by any client. +8. The Daemon Task decides to clean up as there are no more clients. - The Daemon Task must free Device 2 first by calling :cpp:func:`usb_host_device_free_all`. - If :cpp:func:`usb_host_device_free_all` was able to free all devices, the function will return `ESP_OK` indicating that all devices have been freed. - - If :cpp:func:`usb_host_device_free_all` was unable to free all devices (e.g., because the device is still opened by a client), the function will return `ESP_ERR_NOT_FINISHED`. + - If :cpp:func:`usb_host_device_free_all` was unable to free all devices for reasons like the device is still opened by a client, the function will return `ESP_ERR_NOT_FINISHED`. - The Daemon Task must wait for :cpp:func:`usb_host_lib_handle_events` to return the :c:macro:`USB_HOST_LIB_EVENT_FLAGS_ALL_FREE` event flag in order to know when all devices have been freed. 9. Once the Daemon Task has verified that all clients have deregistered and all devices have been freed, it can now uninstall the Host Library by calling :cpp:func:`usb_host_uninstall`. @@ -195,15 +199,16 @@ Basic Usage The Host Library API provides :cpp:func:`usb_host_client_handle_events` to handle a particular client's events. This function should be called repeatedly, typically from the client's task. Some notable features regarding :cpp:func:`usb_host_client_handle_events` are: -- The function can block until a client event needs handling +- The function can block until a client event needs handling. - The function's primary purpose is to call the various event handling callbacks when a client event occurs. The following callbacks are called from within :cpp:func:`usb_host_client_handle_events` thus allowing the client task to be notified of events. -- The client event callback of type :cpp:type:`usb_host_client_event_cb_t` which delivers client event messages to the client. Client event messages indicate events such as the addition or removal of a device. -- The USB transfer completion callback of type :cpp:type:`usb_transfer_cb_t` which indicates that a particular USB transfer previously submitted by the client has completed. +- The client event callback of type :cpp:type:`usb_host_client_event_cb_t` delivers client event messages to the client. Client event messages indicate events such as the addition or removal of a device. +- The USB transfer completion callback of type :cpp:type:`usb_transfer_cb_t` indicates that a particular USB transfer previously submitted by the client has been completed. .. note:: + Given that the callbacks are called from within :cpp:func:`usb_host_client_handle_events`, users should avoid blocking from within the callbacks as this will result in :cpp:func:`usb_host_client_handle_events` being blocked as well, thus preventing other pending client events from being handled. The following code snippet demonstrates a bare-bones host class driver and its client task. The code snippet contains: @@ -230,7 +235,7 @@ The following code snippet demonstrates a bare-bones host class driver and its c static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) { - //This is function is called from within usb_host_client_handle_events(). Don't block and try to keep it short + // This function is called from within usb_host_client_handle_events(). Do not block and try to keep it short struct class_driver_control *class_driver_obj = (struct class_driver_control *)arg; switch (event_msg->event) { case USB_HOST_CLIENT_EVENT_NEW_DEV: @@ -247,7 +252,7 @@ The following code snippet demonstrates a bare-bones host class driver and its c static void transfer_cb(usb_transfer_t *transfer) { - //This is function is called from within usb_host_client_handle_events(). Don't block and try to keep it short + // This function is called from within usb_host_client_handle_events(). Do not block and try to keep it short struct class_driver_control *class_driver_obj = (struct class_driver_control *)transfer->context; printf("Transfer status %d, actual number of bytes transferred %d\n", transfer->status, transfer->actual_num_bytes); class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV; @@ -255,10 +260,10 @@ The following code snippet demonstrates a bare-bones host class driver and its c void client_task(void *arg) { - ... //Wait until Host Library is installed - //Initialize class driver objects + ... // Wait until Host Library is installed + // Initialize class driver objects struct class_driver_control class_driver_obj = {0}; - //Register the client + // Register the client usb_host_client_config_t client_config = { .is_synchronous = false, .max_num_event_msg = 5, @@ -275,16 +280,16 @@ The following code snippet demonstrates a bare-bones host class driver and its c //Event handling loop bool exit = false; while (!exit) { - //Call the client event handler function + // Call the client event handler function usb_host_client_handle_events(class_driver_obj.client_hdl, portMAX_DELAY); - //Execute pending class driver actions + // Execute pending class driver actions if (class_driver_obj.actions & CLASS_DRIVER_ACTION_OPEN_DEV) { - //Open the device and claim interface 1 + // Open the device and claim interface 1 usb_host_device_open(class_driver_obj.client_hdl, class_driver_obj.dev_addr, &class_driver_obj.dev_hdl); usb_host_interface_claim(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1, 0); } if (class_driver_obj.actions & CLASS_DRIVER_ACTION_TRANSFER) { - //Send an OUT transfer to EP1 + // Send an OUT transfer to EP1 memset(transfer->data_buffer, 0xAA, 1024); transfer->num_bytes = 1024; transfer->device_handle = class_driver_obj.dev_hdl; @@ -294,22 +299,23 @@ The following code snippet demonstrates a bare-bones host class driver and its c usb_host_transfer_submit(transfer); } if (class_driver_obj.actions & CLASS_DRIVER_ACTION_CLOSE_DEV) { - //Release the interface and close the device + // Release the interface and close the device usb_host_interface_release(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1); usb_host_device_close(class_driver_obj.client_hdl, class_driver_obj.dev_hdl); exit = true; } - ... //Handle any other actions required by the class driver + ... // Handle any other actions required by the class driver } - //Cleanup class driver + // Cleanup class driver usb_host_transfer_free(transfer); usb_host_client_deregister(class_driver_obj.client_hdl); - ... //Delete the task and any other signal Daemon Task if required + ... // Delete the client task. Signal the Daemon Task if necessary. } .. note:: - An actual host class driver will likely supported many more features, thus will have a much more complex state machine. A host class driver will likely also need to: + + An actual host class driver is likely to support many more features, thus will have a much more complex state machine. A host class driver is also likely to need to: - Be able to open multiple devices - Parse an opened device's descriptors to identify if the device is of the target class @@ -323,7 +329,7 @@ Lifecycle The typical life cycle of a client task and class driver will go through the following stages: #. Wait for some signal regarding the Host Library being installed. -#. Register the client via :cpp:func:`usb_host_client_register` and allocate any other class driver resources (e.g., allocating transfers using :cpp:func:`usb_host_transfer_alloc`). +#. Register the client via :cpp:func:`usb_host_client_register` and allocate any other class driver resources, such as allocating transfers using :cpp:func:`usb_host_transfer_alloc`. #. For each new device that the class driver needs to communicate with: a. Check if the device is already connected via :cpp:func:`usb_host_device_addr_list_fill`. @@ -333,7 +339,7 @@ The typical life cycle of a client task and class driver will go through the fol e. Claim the necessary interfaces of the device via :cpp:func:`usb_host_interface_claim`. #. Submit transfers to the device via :cpp:func:`usb_host_transfer_submit` or :cpp:func:`usb_host_transfer_submit_control`. -#. Once an opened device is no longer needed by the class driver, or has disconnected (as indicated by a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` event): +#. Once an opened device is no longer needed by the class driver, or has disconnected, as indicated by a :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` event: a. Stop any previously submitted transfers to the device's endpoints by calling :cpp:func:`usb_host_endpoint_halt` and :cpp:func:`usb_host_endpoint_flush` on those endpoints. b. Release all previously claimed interfaces via :cpp:func:`usb_host_interface_release`. @@ -351,38 +357,50 @@ Examples Host Library Examples ^^^^^^^^^^^^^^^^^^^^^ -The :example:`peripherals/usb/host/usb_host_lib` demonstrates basic usage of the USB Host Library's API to implement a pseudo class driver. +The :example:`peripherals/usb/host/usb_host_lib` demonstrates basic usage of the USB Host Library's API to implement a pseudo-class driver. Class Driver Examples ^^^^^^^^^^^^^^^^^^^^^ -The USB Host Stack provides a number examples that implement host class drivers using the Host Library's API. +The USB Host Stack provides a number of examples that implement host class drivers using the Host Library's API. CDC-ACM """"""" -* A host class driver for the Communication Device Class (Abstract Control Model) is deployed to `IDF component registry `__. -* The :example:`peripherals/usb/host/cdc/cdc_acm_host` example uses the CDC-ACM host driver component to communicate with CDC-ACM devices +* A host class driver for the Communication Device Class (Abstract Control Model) is distributed as a managed component via the `ESP-IDF Component Registry `__. +* The :example:`peripherals/usb/host/cdc/cdc_acm_host` example uses the CDC-ACM host driver component to communicate with CDC-ACM devices. * The :example:`peripherals/usb/host/cdc/cdc_acm_vcp` example shows how can you extend the CDC-ACM host driver to interface Virtual COM Port devices. * The CDC-ACM driver is also used in `esp_modem examples `__, where it is used for communication with cellular modems. MSC """ -* A host class driver for the Mass Storage Class (Bulk-Only Transport) is deployed to `IDF component registry `__. You can find its example in :example:`peripherals/usb/host/msc`. +* A host class driver for the Mass Storage Class (Bulk-Only Transport) is deployed to `ESP-IDF Component Registry `__. +* The :example:`peripherals/usb/host/msc` example demonstrates the usage of the MSC host driver to read and write to a USB flash drive. + +HID +""" + +* A host class driver for the HID (Human interface device) is distributed as a managed component via the `ESP-IDF Component Registry `__. +* The :example:`peripherals/usb/host/hid` example demonstrates the possibility to receive reports from a USB HID device with several interfaces. +UVC +""" + +* A host class driver for the USB Video Device Class is distributed as a managed component via the `ESP-IDF Component Registry `__. +* The :example:`peripherals/usb/host/uvc` example demonstrates the usage of the UVC host driver to receive a video stream from a USB camera and optionally forward that stream over Wi-Fi. .. -------------------------------------------------- API Reference ---------------------------------------------------- API Reference ------------- -The API of the USB Host Library is separated into the following header files. However, it is sufficient for applications to only ``#include "usb/usb_host.h"`` and all of USB Host Library headers will also be included. +The API of the USB Host Library is separated into the following header files. However, it is sufficient for applications to only ``#include "usb/usb_host.h"`` and all USB Host Library headers will also be included. -- :component_file:`usb/include/usb/usb_host.h` contains the functions and types of the USB Host Library +- :component_file:`usb/include/usb/usb_host.h` contains the functions and types of the USB Host Library. - :component_file:`usb/include/usb/usb_helpers.h` contains various helper functions that are related to the USB protocol such as descriptor parsing. -- :component_file:`usb/include/usb/usb_types_stack.h` contains types that are are used across multiple layers of the USB Host stack. -- :component_file:`usb/include/usb/usb_types_ch9.h` contains types and macros related to Chapter 9 of the USB2.0 specification (i.e., descriptors and standard requests). +- :component_file:`usb/include/usb/usb_types_stack.h` contains types that are used across multiple layers of the USB Host stack. +- :component_file:`usb/include/usb/usb_types_ch9.h` contains types and macros related to Chapter 9 of the USB2.0 specification, i.e., descriptors and standard requests. .. include-build-file:: inc/usb_host.inc @@ -399,6 +417,7 @@ Maintainers Notes ----------------- .. note:: + For more details regarding the internal implementation details of the USB Host stack, please refer to :doc:`/api-reference/peripherals/usb_host/usb_host_notes_index`. .. toctree:: diff --git a/docs/zh_CN/api-reference/peripherals/usb_host.rst b/docs/zh_CN/api-reference/peripherals/usb_host.rst index 64ba8042f5c..8cc03b085b1 100644 --- a/docs/zh_CN/api-reference/peripherals/usb_host.rst +++ b/docs/zh_CN/api-reference/peripherals/usb_host.rst @@ -1 +1,427 @@ -.. include:: ../../../en/api-reference/peripherals/usb_host.rst \ No newline at end of file +USB 主机 +======== + +:link_to_translation:`en:[English]` + +本文档提供了 USB 主机库的相关信息,按以下章节展开: + +.. contents:: 章节 + :depth: 2 + +.. ---------------------------------------------------- Overview ------------------------------------------------------- + +概述 +-------- + +USB 主机库(以下简称主机库)是 USB 主机栈的最底层,提供面向公众开放的 API。应用程序使用 USB 主机功能时,通常无需与主机库直接交互,而是使用某个主机 Class 驱动提供的 API,这些主机 Class 驱动构建在主机库之上。 + +然而,由于以下的某些原因(但不仅限于此),有时你可能需要直接使用主机库: + +- 需要实现自定义主机 Class 驱动程序 +- 需要更低级别的抽象 + +特性和限制 +^^^^^^^^^^^^^^^^^^^^^^ + +主机库具有以下特性: + +- 支持全速 (FS) 和低速 (LS) 设备。 +- 支持四种传输类型,即控制传输、块传输、中断传输和同步传输。 +- 支持多个 Class 驱动程序同时运行,即主机的多个客户端同时运行。 +- 单个设备可以由多个客户端同时使用,如复合设备。 +- 主机库及其底层主机栈不会在内部自动创建操作系统任务,任务数量完全由主机库接口的使用方式决定。一般来说,任务数量为 ``(运行中的主机 Class 驱动程序数量 + 1)``。 + +目前,主机库及其底层主机栈存在以下限制: + +- 仅支持单个设备,而主机库的 API 支持多设备。 +- 仅支持异步传输。 +- 仅支持使用发现的首个配置,尚不支持变更为其他配置。 +- 尚不支持传输超时。 + + +.. -------------------------------------------------- Architecture ----------------------------------------------------- + +架构 +------------ + +.. figure:: ../../../_static/usb_host_lib_entities.png + :align: center + :alt: USB 主机功能的关键实体 + :figclass: align-center + + USB 主机功能涉及的关键实体 + +上图展示了使用 USB 主机功能时涉及的关键实体,包括: + +- **主机库** +- 主机库的 **客户端** +- **设备** +- 主机库的 **守护进程任务** + +主机库 +^^^^^^^^^^^^ + +主机库是 ESP-IDF USB 主机栈中面向公众开放的最底层的 API 层。任何其他 ESP-IDF 组件(如 Class 驱动程序或用户组件),如果需要与连接的 USB 设备通信,只能直接或间接使用主机库 API。 + +主机库 API 分为两类,即 **库 API** 和 **客户端 API**。 + +- 客户端 API 负责主机库的客户端与一或多个 USB 设备间的通信,该 API 只能由主机库的注册客户端调用。 +- 库 API 负责主机库处理的通信中不特定于单个客户端的通信,如设备枚举。该 API 通常由主机库的守护进程任务调用。 + +客户端 +^^^^^^^ + +主机库的客户端指使用主机库与 USB 设备通信的软件组件,如主机 Class 驱动程序或用户组件。每个客户端通常与任务间存在一对一关系,这表明,对特定客户端而言,其所有客户端 API 都应该在同一任务的上下文中调用。 + +通过将使用主机库的软件组件进行分类,划分为独立的客户端,主机库可以将所有客户端特定事件的处理委托给客户端对应的任务。换句话说,每个客户端任务负责管理与其对应的客户端之间的所有 USB 通信操作和事件处理,无需关心其他客户端的事件。 + +守护进程任务 +^^^^^^^^^^^^ + +尽管主机库将客户端事件的处理委托给客户端本身,但仍然需要处理主机库事件,即不特定于客户端的事件。主机库事件处理可能涉及以下内容: + +- 处理 USB 设备的连接、枚举和断连 +- 将控制传输从/向客户端进行重定向 +- 将事件转发给客户端 + +因此,除客户端任务外,主机库也需要一个任务来处理所有的库事件,这个任务通常是主机库守护进程任务。 + +设备 +^^^^^^^ + +主机库隔离了客户端与设备处理的细节,包括连接、内存分配和枚举等,客户端只需提供已连接且已枚举的设备列表供选择。在枚举过程中,每个设备都会自动配置为使用找到的第一个配置,即通过获取配置描述符请求返回的第一个配置描述符。对于大多数标准设备,通常将第一个配置的 ``bConfigurationValue`` 设置为 ``1``。 + +只要不与相同接口通信,两个及以上的客户端可以同时与同一设备通信。然而,多个客户端同时与相同设备的默认端点(即 EP0)通信,将导致它们的控制传输序列化。 + +要与设备通信,客户端必须满足以下条件: + +#. 使用设备地址打开设备,告知主机库,客户端正在使用该设备。 +#. 获取将用于通信的接口,防止其他客户端获取相同的接口。 +#. 通过已经获取了使用权的设备接口的端点通信通道发送数据传输。客户端的任务负责处理其与 USB 设备通信相关的操作和事件。 + + +.. ------------------------------------------------------ Usage -------------------------------------------------------- + +用法 +----- + +主机库及底层主机栈不会创建任何任务,客户端任务和守护进程任务等均需由 Class 驱动程序或用户自行创建。然而,主机库提供了两个事件处理函数,可以处理所有必要的主机库操作,这些函数应从客户端任务和守护进程任务中重复调用。因此,客户端任务和守护进程任务的使用将主要集中于调用这些事件处理函数。 + +主机库与守护进程任务 +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +基本用法 +""""""""""" + +主机库 API 提供了 :cpp:func:`usb_host_lib_handle_events`,可以处理库事件。该函数需要反复调用,通常是从守护进程任务中调用。函数 :cpp:func:`usb_host_lib_handle_events` 有以下特点: + +- 该函数会持续阻塞,直至需要处理库事件。 +- 每次调用该函数都会返回事件标志,有助于了解卸载主机库的时机。 + +最基础的守护进程任务通常类似以下代码片段: + +.. code-block:: c + + #include "usb/usb_host.h" + + void daemon_task(void *arg) + { + ... + bool exit = false; + while (!exit) { + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ... + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ... + } + ... + } + ... + } + +.. note:: + + 了解守护进程任务的完整示例,请前往 :example:`peripherals/usb/host/usb_host_lib`。 + +生命周期 +""""""""" + +.. figure:: ../../../_static/usb_host_lib_lifecycle.png + :align: center + :alt: USB 主机库典型生命周期 + :figclass: align-center + + USB 主机库典型生命周期 + +上图展示了 USB 主机库的典型生命周期,其中涉及多个客户端和设备。具体而言,示例涉及以下内容: + +- 两个已注册的客户端(客户端 1 和客户端 2)。 +- 两个已连接的设备(设备 1 和设备 2),其中客户端 1 与设备 1 通信,客户端 2 与设备 2 通信。 + +参考上图可知,典型 USB 主机库生命周期包括以下关键阶段: + +1. 调用 :cpp:func:`usb_host_install`,安装主机库。 + - 调用任意主机库 API 前,请确保已完成主机库安装。 + - 调用 :cpp:func:`usb_host_install` 的位置(如从守护进程任务或其他任务中调用)取决于守护系统任务、客户端任务和系统其余部分间的同步逻辑。 +2. 安装完主机库后,调用 :cpp:func:`usb_host_client_register` 注册客户端。 + - 该注册函数通常从客户端任务调用,而客户端任务需等待来自守护进程任务的信号。 + - 调用过 :cpp:func:`usb_host_install` 后,如有需要,也可以在其他地方调用该注册函数。 +3. 设备 1 连接,并进行枚举。 + - 每个已注册的客户端(本案例中为客户端 1 和客户端 2)都会通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件得到新设备的通知。 + - 客户端 1 开启设备 1,并与之通信。 +4. 设备 2 连接,并进行枚举。 + - 客户端 1 和 2 通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件得到新设备的通知。 + - 客户端 2 开启设备 2,并与之通信。 +5. 设备 1 突然断开连接。 + - 客户端 1 通过 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` 得到通知,并开始清理,关闭和释放客户端 1 与设备 1 之间的关联资源。 + - 客户端 2 不会收到通知,因为它并未开启设备 1。 +6. 客户端 1 完成清理,调用 :cpp:func:`usb_host_client_deregister` 注销客户端。 + - 该注销函数通常在任务退出前,从客户端任务中调用。 + - 如有需要,只要客户端 1 已完成清理,也可以在其他地方调用该注销函数。 +7. 客户端 2 完成与设备 2 的通信,随后关闭设备 2,并自行注销。 + - 由于客户端 2 是最后一个注销的客户端,通过 :c:macro:`USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS` 事件标志,守护进程任务可以得知,所有客户端已注销。 + - 设备 2 未释放,仍会进行分配,因为虽然没有任何客户端打开设备 2,但它仍处于连接状态。 +8. 所有客户端注销后,守护进程任务开始清理。 + - 守护进程任务需要先调用 :cpp:func:`usb_host_device_free_all`,释放设备 2。 + - 如果 :cpp:func:`usb_host_device_free_all` 能够成功释放所有设备,函数将返回 `ESP_OK`,表明已释放所有设备。 + - 如果 :cpp:func:`usb_host_device_free_all` 无法成功释放所有设备,例如因为设备仍由某个客户端开启,函数将返回 `ESP_ERR_NOT_FINISHED`。 + - 守护进程任务必须等待 :cpp:func:`usb_host_lib_handle_events` 返回事件标志 :c:macro:`USB_HOST_LIB_EVENT_FLAGS_ALL_FREE`,方知何时所有设备均已释放。 +9. 一旦守护进程任务确认所有客户端均已注销,且所有设备均已释放,便可调用 :cpp:func:`usb_host_uninstall`,卸载主机库。 + +客户端与 Class 驱动程序 +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +基本用法 +""""""""""" + +主机库 API 提供函数 :cpp:func:`usb_host_client_handle_events`,可以处理特定客户端事件。该函数需要反复调用,通常是从客户端任务中调用。函数 :cpp:func:`usb_host_client_handle_events` 有以下特点: + +- 该函数可以持续阻塞,直至需要处理客户端事件。 +- 该函数的主要目的是发生客户端事件时,调用多个事件处理回调函数。 + +以下回调函数均从 :cpp:func:`usb_host_client_handle_events` 中调用,因此客户端任务能够获取事件通知。 + +- 类型为 :cpp:type:`usb_host_client_event_cb_t` 的客户端事件回调函数会将客户端事件消息传递给客户端,提示添加、移除设备等事件。 +- 类型为 :cpp:type:`usb_transfer_cb_t` 的 USB 传输完成回调函数表明,先前由客户端提交的特定 USB 传输已完成。 + +.. note:: + + 考虑到上述回调函数从 :cpp:func:`usb_host_client_handle_events` 中调用,应避免在回调函数内部阻塞,否则将导致 :cpp:func:`usb_host_client_handle_events` 阻塞,阻止其他待处理的客户端事件得到处理。 + +以下代码片段展示了一个基础的主机 Class 驱动程序及其客户端任务,代码片段中包括: + +- 一个简单的客户端任务函数 ``client_task``,它会在循环中调用 :cpp:func:`usb_host_client_handle_events`。 +- 使用客户端事件回调函数和传输完成回调函数。 +- 一个用于 Class 驱动程序的简单状态机。该 Class 驱动程序仅支持打开设备,发送 OUT 传输到 EP1,然后关闭设备。 + +.. code-block:: c + + #include + #include "usb/usb_host.h" + + #define CLASS_DRIVER_ACTION_OPEN_DEV 0x01 + #define CLASS_DRIVER_ACTION_TRANSFER 0x02 + #define CLASS_DRIVER_ACTION_CLOSE_DEV 0x03 + + struct class_driver_control { + uint32_t actions; + uint8_t dev_addr; + usb_host_client_handle_t client_hdl; + usb_device_handle_t dev_hdl; + }; + + static void client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) + { + //该函数从 usb_host_client_handle_events() 中调用,请勿在此阻塞,并尽量保持简洁 + struct class_driver_control *class_driver_obj = (struct class_driver_control *)arg; + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: + class_driver_obj->actions |= CLASS_DRIVER_ACTION_OPEN_DEV; + class_driver_obj->dev_addr = event_msg->new_dev.address; //存储新设备的地址 + break; + case USB_HOST_CLIENT_EVENT_DEV_GONE: + class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV; + break; + default: + break; + } + } + + static void transfer_cb(usb_transfer_t *transfer) + { + //该函数从 usb_host_client_handle_events() 中调用,请勿在此阻塞,并尽量保持简洁 + struct class_driver_control *class_driver_obj = (struct class_driver_control *)transfer->context; + printf("Transfer status %d, actual number of bytes transferred %d\n", transfer->status, transfer->actual_num_bytes); + class_driver_obj->actions |= CLASS_DRIVER_ACTION_CLOSE_DEV; + } + + void client_task(void *arg) + { + ... //等待主机库安装 + //初始化 Class 驱动程序对象 + struct class_driver_control class_driver_obj = {0}; + //注册客户端 + usb_host_client_config_t client_config = { + .is_synchronous = false, + .max_num_event_msg = 5, + .async = { + .client_event_callback = client_event_cb, + .callback_arg = &class_driver_obj, + } + }; + usb_host_client_register(&client_config, &class_driver_obj.client_hdl); + //分配一个 USB 传输 + usb_transfer_t *transfer; + usb_host_transfer_alloc(1024, 0, &transfer); + + //事件处理循环 + bool exit = false; + while (!exit) { + //调用客户端事件处理函数 + usb_host_client_handle_events(class_driver_obj.client_hdl, portMAX_DELAY); + //执行待处理的 Class 驱动程序操作 + if (class_driver_obj.actions & CLASS_DRIVER_ACTION_OPEN_DEV) { + //开启设备,声明接口 1 + usb_host_device_open(class_driver_obj.client_hdl, class_driver_obj.dev_addr, &class_driver_obj.dev_hdl); + usb_host_interface_claim(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1, 0); + } + if (class_driver_obj.actions & CLASS_DRIVER_ACTION_TRANSFER) { + //发送一个 OUT 传输到 EP1 + memset(transfer->data_buffer, 0xAA, 1024); + transfer->num_bytes = 1024; + transfer->device_handle = class_driver_obj.dev_hdl; + transfer->bEndpointAddress = 0x01; + transfer->callback = transfer_cb; + transfer->context = (void *)&class_driver_obj; + usb_host_transfer_submit(transfer); + } + if (class_driver_obj.actions & CLASS_DRIVER_ACTION_CLOSE_DEV) { + //释放接口,关闭设备 + usb_host_interface_release(class_driver_obj.client_hdl, class_driver_obj.dev_hdl, 1); + usb_host_device_close(class_driver_obj.client_hdl, class_driver_obj.dev_hdl); + exit = true; + } + ... //处理其他 Class 驱动程序要求的行为 + } + + //清理 Class 驱动程序 + usb_host_transfer_free(transfer); + usb_host_client_deregister(class_driver_obj.client_hdl); + ... //删除客户端任务。如有需要,向守护进程任务发送信号。 + } + +.. note:: + + 在实际应用中,主机 Class 驱动程序还能支持更多功能,因此也存在更复杂的状态机。主机 Class 驱动程序可能需要: + + - 能够开启多个设备 + - 解析已开启设备的描述符,确定设备是否是目标 Class + - 按特定顺序,与接口的多个端点通信 + - 声明设备的多个接口 + - 处理各种错误情况 + +生命周期 +""""""""" + +客户端任务与 Class 驱动程序的典型生命周期包括以下关键阶段: + +#. 等待与主机库有关的信号完成安装。 +#. 通过 :cpp:func:`usb_host_client_register` 注册客户端,并分配其他的 Class 驱动程序资源,如使用 :cpp:func:`usb_host_transfer_alloc` 分配传输。 +#. 对于 Class 驱动程序需要与之通信的各个新设备: + + a. 调用 :cpp:func:`usb_host_device_addr_list_fill`,检查设备是否已连接。 + b. 如果设备尚未连接,则等待客户端事件回调函数的 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_NEW_DEV` 事件。 + c. 调用 :cpp:func:`usb_host_device_open` 开启设备。 + d. 分别调用 :cpp:func:`usb_host_get_device_descriptor` 和 :cpp:func:`usb_host_get_active_config_descriptor`,解析设备和配置描述符。 + e. 调用 :cpp:func:`usb_host_interface_claim`,声明设备的必要接口。 + +#. 调用 :cpp:func:`usb_host_transfer_submit` 或 :cpp:func:`usb_host_transfer_submit_control`,向设备提交传输。 +#. 一旦 :cpp:enumerator:`USB_HOST_CLIENT_EVENT_DEV_GONE` 事件表示,Class 驱动程序不再需要已打开的设备,或者设备断开连接: + + a. 在这些端点上调用 :cpp:func:`usb_host_endpoint_halt` 和 :cpp:func:`usb_host_endpoint_flush`,停止先前提交的传输。 + b. 调用 :cpp:func:`usb_host_interface_release`,释放先前声明的所有接口。 + c. 调用 :cpp:func:`usb_host_device_close`,关闭设备。 + +#. 调用 :cpp:func:`usb_host_client_deregister` 注销客户端,并释放其他 Class 驱动程序资源。 +#. 删除客户端任务。如有需要,向守护进程任务发送信号。 + + +.. ---------------------------------------------------- Examples ------------------------------------------------------- + +示例 +-------- + +主机库示例 +^^^^^^^^^^^^^^^^^^^^^ + +:example:`peripherals/usb/host/usb_host_lib` 展示了 USB 主机库 API 的基本用法,用于创建伪 Class 驱动程序。 + +Class 驱动程序示例 +^^^^^^^^^^^^^^^^^^^^^^^^ + +USB 主机栈提供了大量示例,展示了如何通过使用主机库 API 创建主机 Class 驱动程序。 + +CDC-ACM +""""""" + +* 通信设备 Class(抽象控制模型)的主机 Class 驱动程序通过 `ESP-IDF 组件注册器 `__ 作为受管理的组件分发。 +* 示例 :example:`peripherals/usb/host/cdc/cdc_acm_host` 使用 CDC-ACM 主机驱动程序组件,与 CDC-ACM 设备通信。 +* 示例 :example:`peripherals/usb/host/cdc/cdc_acm_vcp` 展示了如何扩展 CDC-ACM 主机驱动程序,与虚拟串口设备交互。 +* 示例 `esp_modem `__ 中也使用了 CDC-ACM 驱动程序,该程序在这些示例中与蜂窝模块通信。 + +MSC +""" + +* 大容量存储 Class(仅支持批量传输)的主机 Class 驱动程序已部署到 `ESP-IDF 组件注册器 `__。 +* 示例 :example:`peripherals/usb/host/msc` 展示了如何使用 MSC 主机驱动程序读写 USB flash 驱动。 + +HID +""" + +* HID(人机接口设备)的主机 class 驱动作为托管组件通过 `ESP-IDF 组件注册器 `__ 分发。 +* 示例 :example:`peripherals/usb/host/hid` 展示了从具有多个接口的 USB HID 设备接收报告的可能性。 + +UVC +""" + +* USB 视频设备 Class 的主机 Class 驱动程序作为托管组件通过 `ESP-IDF 组件注册器 `__ 分发。 +* 示例 :example:`peripherals/usb/host/uvc` 展示了如何使用 UVC 主机驱动程序接收来自 USB 摄像头的视频流,并可选择将该流通过 Wi-Fi 转发。 + +.. -------------------------------------------------- API Reference ---------------------------------------------------- + +API 参考 +------------- + +USB 主机库的 API 包含以下头文件,但应用程序调用该 API 时只需 ``#include "usb/usb_host.h"``,该头文件包含了所有 USB 主机库的头文件。 + +- :component_file:`usb/include/usb/usb_host.h` 包含 USB 主机库的函数和类型。 +- :component_file:`usb/include/usb/usb_helpers.h` 包含与 USB 协议相关的各种辅助函数,如描述符解析等。 +- :component_file:`usb/include/usb/usb_types_stack.h` 包含在 USB 主机栈的多个层次中使用的类型。 +- :component_file:`usb/include/usb/usb_types_ch9.h` 包含了与 USB 2.0 规范中第 9 章相关的类型和宏,即描述符和标准请求。 + + +.. include-build-file:: inc/usb_host.inc + +.. include-build-file:: inc/usb_helpers.inc + +.. include-build-file:: inc/usb_types_stack.inc + +.. include-build-file:: inc/usb_types_ch9.inc + +.. ------------------------------------------------ Maintainers Notes -------------------------------------------------- + +维护注意事项 +----------------- + +.. note:: + + 有关 USB 主机栈内部实现的更多细节,请参阅 :doc:`/api-reference/peripherals/usb_host/usb_host_notes_index`。 + +.. toctree:: + :hidden: + :maxdepth: 0 + + usb_host/usb_host_notes_index