connectivity/usb/device_next/usb_device.html · connectivity/usb/device_next/usb_device.rst 공식 원문 ↗

Zephyr 3.7.0 · Connectivity · USB · Device Next

New USB Device Support

UDC 위에 여러 controller와 configuration을 runtime으로 조립하고 VBUS 상태에 맞춰 device를 운용합니다.

Official pathconnectivity/usb/device_next/usb_device.html
Source fileconnectivity/usb/device_next/usb_device.rst
Source versionZephyr 3.7.0
TranslationFull · reviewed

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

Part 1

요약·해설

새 USB device stack의 built-in class, sample 전환 option, usbd_context 생성, string descriptor와 speed별 configuration, class 등록, init·enable·shutdown lifecycle, message callback과 VBUS 감지를 순서대로 설명합니다.

Compose

Context · Config · Class

Lifecycle

Init · Enable · Shutdown

Notify

Messages · VBUS

Part 2

접을 수 있는 영어 원문 전체

영어 원문 전체 펼치기
원문 SHA-256 883D211936556202FAE0900FF641850809CFE311FC1B884227320B23ED40A6A5
.. _usb_device_stack_next:

New USB device support
######################

Overview
********

USB device support consists of the USB device controller (UDC) drivers
, :ref:`udc_api`, and USB device stack, :ref:`usbd_api`.
The :ref:`udc_api` provides a generic and vendor independent interface to USB
device controllers, and although, there a is clear separation between these
layers, the purpose of :ref:`udc_api` is to serve new Zephyr's USB device stack
exclusively.

The new device stack supports multiple device controllers, meaning that if a
SoC has multiple controllers, they can be used simultaneously. Full and
high-speed device controllers are supported. It also provides support for
registering multiple function or class instances to a configuration at runtime,
or changing the configuration later. It has built-in support for several USB
classes and provides an API to implement custom USB functions.

The new USB device support is considered experimental and will replace
:ref:`usb_device_stack`.

Built-in functions
==================

The USB device stack has built-in USB functions. Some can be used directly in
the user application through a special API, such as HID or Audio class devices,
while others use a general Zephyr RTOS driver API, such as MSC and CDC class
implementations. The *Identification string* identifies a class or function
instance (`n`) and is used as an argument to the :c:func:`usbd_register_class`.

+-----------------------------------+-------------------------+-------------------------+
| Class or function                 | User API (if any)       | Identification string   |
+===================================+=========================+=========================+
| USB Audio 2 class                 | :ref:`uac2_device`      | :samp:`uac2_{n}`        |
+-----------------------------------+-------------------------+-------------------------+
| USB CDC ACM class                 | :ref:`uart_api`         | :samp:`cdc_acm_{n}`     |
+-----------------------------------+-------------------------+-------------------------+
| USB CDC ECM class                 | Ethernet device         | :samp:`cdc_ecm_{n}`     |
+-----------------------------------+-------------------------+-------------------------+
| USB Mass Storage Class (MSC)      | :ref:`usbd_msc_device`  | :samp:`msc_{n}`         |
+-----------------------------------+-------------------------+-------------------------+
| USB Human Interface Devices (HID) | :ref:`usbd_hid_device`  | :samp:`hid_{n}`         |
+-----------------------------------+-------------------------+-------------------------+
| Bluetooth HCI USB transport layer | :ref:`bt_hci_raw`       | :samp:`bt_hci_{n}`      |
+-----------------------------------+-------------------------+-------------------------+

Samples
=======

* :zephyr:code-sample:`usb-hid-keyboard`

* :zephyr:code-sample:`uac2-explicit-feedback`

Samples ported to new USB device support
----------------------------------------

To build a sample that supports both the old and new USB device stack, set the
configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via
``west``.

* :ref:`bluetooth-hci-usb-sample`

* :zephyr:code-sample:`usb-cdc-acm`

* :zephyr:code-sample:`usb-cdc-acm-console`

* :zephyr:code-sample:`usb-mass`

* :zephyr:code-sample:`usb-hid-mouse`

* :zephyr:code-sample:`zperf` To build the sample for the new device support,
  set the configuration overlay file
  ``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file
  ``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``.

How to configure and enable USB device support
**********************************************

For the USB device support samples in the Zephyr project repository, we have a
common file for instantiation, configuration and initialization,
:zephyr_file:`samples/subsys/usb/common/sample_usbd_init.c`. The following code
snippets from this file are used as examples. USB Samples Kconfig options used
in the USB samples and prefixed with ``SAMPLE_USBD_`` have default values
specific to the Zephyr project and the scope is limited to the project samples.
In the examples below, you will need to replace these Kconfig options and other
defaults with values appropriate for your application or hardware.

The USB device stack requires a context structure to manage its properties and
runtime data. The preferred way to define a device context is to use the
:c:macro:`USBD_DEVICE_DEFINE` macro. This creates a static
:c:struct:`usbd_context` variable with a given name. Any number of contexts may
be instantiated. A USB controller device can be assigned to multiple contexts,
but only one context can be initialized and used at a time. Context properties
must not be directly accessed or manipulated by the application.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc device instantiation start
   :end-before: doc device instantiation end

Your USB device may have manufacturer, product, and serial number string
descriptors. To instantiate these string descriptors, the application should
use the appropriate :c:macro:`USBD_DESC_MANUFACTURER_DEFINE`,
:c:macro:`USBD_DESC_PRODUCT_DEFINE`, and
:c:macro:`USBD_DESC_SERIAL_NUMBER_DEFINE` macros. String descriptors also
require a single instantiation of the language descriptor using the
:c:macro:`USBD_DESC_LANG_DEFINE` macro.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc string instantiation start
   :end-before: doc string instantiation end

String descriptors must be added to the device context at runtime before
initializing the USB device with :c:func:`usbd_add_descriptor`.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc add string descriptor start
   :end-before: doc add string descriptor end

USB device requires at least one configuration instance per supported speed.
The application should use :c:macro:`USBD_CONFIGURATION_DEFINE` to instantiate
a configuration. Later, USB device functions are assigned to a configuration.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc configuration instantiation start
   :end-before: doc configuration instantiation end

Each configuration instance for a specific speed must be added to the device
context at runtime before the USB device is initialized using
:c:func:`usbd_add_configuration`. Note :c:enumerator:`USBD_SPEED_FS` and
:c:enumerator:`USBD_SPEED_HS`. The first full-speed or high-speed
configuration will get ``bConfigurationValue`` one, and then further upward.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc configuration register start
   :end-before: doc configuration register end


Although we have already done a lot, this USB device has no function. A device
can have multiple configurations with different set of functions at different
speeds. A function or class can be registered on a USB device before
it is initialized using :c:func:`usbd_register_class`. The desired
configuration is specified using :c:enumerator:`USBD_SPEED_FS` or
:c:enumerator:`USBD_SPEED_HS` and the configuration number.  For simple cases,
:c:func:`usbd_register_all_classes` can be used to register all available
instances.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc functions register start
   :end-before: doc functions register end

The last step in the preparation is to initialize the device with
:c:func:`usbd_init`. After this, the configuration of the device cannot be
changed. A device can be deinitialized with :c:func:`usbd_shutdown` and all
instances can be reused, but the previous steps must be repeated. So it is
possible to shutdown a device, register another type of configuration or
function, and initialize it again.  At the USB controller level,
:c:func:`usbd_init` does only what is necessary to detect VBUS changes. There
are controller types where the next step is only possible if a VBUS signal is
present.

A function or class implementation may require its own specific configuration
steps, which should be performed prior to initializing the USB device.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc device init start
   :end-before: doc device init end

The final step to enable the USB device is :c:func:`usbd_enable`, after that,
if the USB device is connected to a USB host controller, the host can start
enumerating the device. The application can disable the USB device using
:c:func:`usbd_disable`.

.. literalinclude:: ../../../../../../samples/subsys/usb/hid-keyboard/src/main.c
   :language: c
   :dedent:
   :start-after: doc device enable start
   :end-before: doc device enable end

USB Message notifications
=========================

The application can register a callback using :c:func:`usbd_msg_register_cb` to
receive message notification from the USB device support subsystem. The
messages are mostly about the common device state changes, and a few specific
types from the USB CDC ACM implementation.

.. literalinclude:: ../../../../../../samples/subsys/usb/common/sample_usbd_init.c
   :language: c
   :dedent:
   :start-after: doc device init-and-msg start
   :end-before: doc device init-and-msg end

The helper function :c:func:`usbd_msg_type_string()` can be used to convert
:c:enumerator:`usbd_msg_type` to a human readable form for logging.

If the controller supports VBUS state change detection, the battery-powered
application may want to enable the USB device only when it is connected to a
host. A generic application should use :c:func:`usbd_can_detect_vbus` to check
for this capability.

.. literalinclude:: ../../../../../../samples/subsys/usb/hid-keyboard/src/main.c
   :language: c
   :dedent:
   :start-after: doc device msg-cb start
   :end-before: doc device msg-cb end

Part 3

한국어 전문 번역

새 USB device 지원

개요

USB device 지원은 USB device controller(UDC) driver와 UDC API, USB device stack과 USBD API로 구성됩니다. UDC API는 USB device controller에 대한 범용 vendor 독립 interface를 제공합니다. 두 layer의 경계는 분명하지만 UDC API의 목적은 Zephyr의 새 USB device stack만을 지원하는 것입니다.

새 device stack은 여러 device controller를 지원합니다. SoC에 controller가 여러 개 있으면 동시에 사용할 수 있습니다. Full-speed와 high-speed device controller를 모두 지원합니다.

Runtime에 configuration 하나에 function 또는 class instance 여러 개를 등록하거나 나중에 configuration을 바꿀 수 있습니다. 여러 USB class를 built-in으로 지원하고 custom USB function을 구현하는 API도 제공합니다.

새 USB device 지원은 experimental 상태이며 기존 USB device stack을 대체할 예정입니다.

Built-in function

USB device stack에는 built-in USB function이 있습니다. HID 또는 Audio class device처럼 전용 API로 user application에서 직접 사용하는 function도 있고, MSC와 CDC class처럼 Zephyr RTOS의 일반 driver API를 사용하는 구현도 있습니다. Identification string은 class 또는 function instance의 번호 n을 식별하며 usbd_register_class argument로 전달합니다.

Class 또는 functionUser APIIdentification string
USB Audio 2 classuac2_deviceuac2_{n}
USB CDC ACM classUART APIcdc_acm_{n}
USB CDC ECM classEthernet devicecdc_ecm_{n}
USB Mass Storage Class(MSC)usbd_msc_devicemsc_{n}
USB Human Interface Devices(HID)usbd_hid_devicehid_{n}
Bluetooth HCI USB transport layerBluetooth HCI rawbt_hci_{n}

Sample

  • usb-hid-keyboard
  • uac2-explicit-feedback

새 USB device 지원으로 port된 sample

기존 stack과 새 stack을 모두 지원하는 sample을 build할 때는 직접 또는 west를 통해 -DCONF_FILE=usbd_next_prj.conf configuration을 설정합니다.

  • bluetooth-hci-usb-sample
  • usb-cdc-acm
  • usb-cdc-acm-console
  • usb-mass
  • usb-hid-mouse
  • zperf: 새 device 지원용으로 build하려면 직접 또는 west를 통해 configuration overlay -DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf와 devicetree overlay -DDTC_OVERLAY_FILE="usbd_next_ecm.overlay를 지정합니다.

USB device 지원 구성과 활성화 방법

Zephyr project repository의 USB device 지원 sample은 instance 생성, configuration과 initialization을 위한 공통 file samples/subsys/usb/common/sample_usbd_init.c을 사용합니다. 아래 code snippet은 이 file에서 가져왔습니다.

SAMPLE_USBD_ prefix를 사용하는 USB Sample Kconfig option의 기본값은 Zephyr project sample 전용이며 범위도 project sample에 한정됩니다. 실제 application이나 hardware에 적용할 때는 아래 example의 Kconfig option과 다른 기본값을 알맞은 값으로 바꿔야 합니다.

USB device stack은 property와 runtime data를 관리하는 context structure가 필요합니다. 권장 방식은 USBD_DEVICE_DEFINE macro로 device context를 정의하는 것입니다. 주어진 이름의 static usbd_context variable을 만들며 context는 필요한 만큼 생성할 수 있습니다.

USB controller device 하나를 여러 context에 할당할 수 있지만, 한 시점에는 context 하나만 initialize해 사용할 수 있습니다. Application이 context property를 직접 읽거나 조작해서는 안 됩니다.

/*
 * Instantiate a context named sample_usbd using the default USB device
 * controller, the Zephyr project vendor ID, and the sample product ID.
 * Zephyr project vendor ID must not be used outside of Zephyr samples.
 */
USBD_DEVICE_DEFINE(sample_usbd,
		   DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
		   ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID);

USB device에는 manufacturer, product와 serial number string descriptor가 있을 수 있습니다. USBD_DESC_MANUFACTURER_DEFINE, USBD_DESC_PRODUCT_DEFINE, USBD_DESC_SERIAL_NUMBER_DEFINE macro로 각각 생성합니다. String descriptor에는 USBD_DESC_LANG_DEFINE macro로 language descriptor instance 하나도 만들어야 합니다.

USBD_DESC_LANG_DEFINE(sample_lang);
USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER);
USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT);
USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn);

USB device를 initialize하기 전에 runtime에서 usbd_add_descriptor를 호출해 string descriptor를 device context에 추가해야 합니다.

err = usbd_add_descriptor(&sample_usbd, &sample_lang);
if (err) {
	LOG_ERR("Failed to initialize language descriptor (%d)", err);
	return NULL;
}

err = usbd_add_descriptor(&sample_usbd, &sample_mfr);
if (err) {
	LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err);
	return NULL;
}

err = usbd_add_descriptor(&sample_usbd, &sample_product);
if (err) {
	LOG_ERR("Failed to initialize product descriptor (%d)", err);
	return NULL;
}

err = usbd_add_descriptor(&sample_usbd, &sample_sn);
if (err) {
	LOG_ERR("Failed to initialize SN descriptor (%d)", err);
	return NULL;
}

USB device에는 지원 speed마다 configuration instance가 적어도 하나 필요합니다. USBD_CONFIGURATION_DEFINE으로 configuration을 생성하고 이후 USB device function을 해당 configuration에 할당합니다.

static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
				   USB_SCD_SELF_POWERED : 0) |
				  (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
				   USB_SCD_REMOTE_WAKEUP : 0);

/* Full speed configuration */
USBD_CONFIGURATION_DEFINE(sample_fs_config,
			  attributes,
			  CONFIG_SAMPLE_USBD_MAX_POWER);

/* High speed configuration */
USBD_CONFIGURATION_DEFINE(sample_hs_config,
			  attributes,
			  CONFIG_SAMPLE_USBD_MAX_POWER);

특정 speed의 각 configuration instance는 USB device를 initialize하기 전에 usbd_add_configuration으로 runtime에 device context에 추가해야 합니다. Speed는 USBD_SPEED_FS와 USBD_SPEED_HS로 지정합니다. 첫 full-speed 또는 high-speed configuration은 bConfigurationValue 1을 받고 이후 configuration은 순서대로 증가합니다.

err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
			     &sample_fs_config);
if (err) {
	LOG_ERR("Failed to add Full-Speed configuration");
	return NULL;
}

여기까지 구성해도 USB device에는 아직 function이 없습니다. Device는 speed마다 서로 다른 function set을 가진 configuration을 여러 개 둘 수 있습니다.

usbd_register_class를 사용하면 USB device를 initialize하기 전에 function 또는 class를 등록할 수 있습니다. 원하는 configuration은 USBD_SPEED_FS 또는 USBD_SPEED_HS와 configuration number로 지정합니다. 단순한 경우에는 usbd_register_all_classes로 사용 가능한 모든 instance를 등록할 수 있습니다.

err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1);
if (err) {
	LOG_ERR("Failed to add register classes");
	return NULL;
}

usbd_init로 device를 initialize하는 것이 준비의 마지막 단계입니다. 이후에는 device configuration을 변경할 수 없습니다. usbd_shutdown으로 device를 deinitialize하면 모든 instance를 재사용할 수 있지만 앞선 절차를 다시 수행해야 합니다. 따라서 device를 shutdown하고 다른 configuration이나 function을 등록한 뒤 다시 initialize할 수 있습니다.

usbd_init은 USB controller level에서 VBUS 변화를 감지하는 데 필요한 작업만 수행합니다. Controller 종류에 따라 다음 단계는 VBUS signal이 있어야만 가능할 수 있습니다.

Function 또는 class 구현에 고유한 configuration 단계가 필요하다면 USB device를 initialize하기 전에 수행해야 합니다.

err = usbd_init(&sample_usbd);
if (err) {
	LOG_ERR("Failed to initialize device support");
	return NULL;
}

usbd_enable이 USB device를 활성화하는 마지막 단계입니다. Device가 USB host controller에 연결되어 있다면 host가 enumeration을 시작할 수 있습니다. usbd_disable로 USB device를 비활성화할 수 있습니다.

ret = usbd_enable(sample_usbd);
if (ret) {
	LOG_ERR("Failed to enable device support");
	return ret;
}

USB message notification

usbd_msg_register_cb로 callback을 등록하면 USB device 지원 subsystem의 message notification을 받을 수 있습니다. Message는 대부분 공통 device state 변화이며 일부는 USB CDC ACM 구현에 특화된 type입니다.

err = usbd_msg_register_cb(&sample_usbd, msg_cb);
if (err) {
	LOG_ERR("Failed to register message callback");
	return NULL;
}

usbd_msg_type_string() helper function은 usbd_msg_type을 logging에 적합한 사람이 읽을 수 있는 문자열로 변환합니다.

Controller가 VBUS state change detection을 지원한다면 battery-powered application은 host에 연결됐을 때만 USB device를 활성화할 수 있습니다. 범용 application은 usbd_can_detect_vbus로 이 capability를 확인해야 합니다.

static void msg_cb(struct usbd_context *const usbd_ctx,
		   const struct usbd_msg *const msg)
{
	LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));

	if (usbd_can_detect_vbus(usbd_ctx)) {
		if (msg->type == USBD_MSG_VBUS_READY) {
			if (usbd_enable(usbd_ctx)) {
				LOG_ERR("Failed to enable device support");
			}
		}

		if (msg->type == USBD_MSG_VBUS_REMOVED) {
			if (usbd_disable(usbd_ctx)) {
				LOG_ERR("Failed to disable device support");
			}
		}
	}
}

Source

출처

영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.