doc: usb: add initial USB device configuraiton howto
Add initial documentation how to configure and enable new USB device support. Use literalinclude to pull code snippets from the samples. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
This commit is contained in:
parent
cf2113e437
commit
8739efe0fc
3 changed files with 134 additions and 0 deletions
|
|
@ -76,3 +76,112 @@ configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via
|
||||||
set the configuration overlay file
|
set the configuration overlay file
|
||||||
``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree 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``.
|
``-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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
: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
|
||||||
|
:start-after: doc device enable start
|
||||||
|
:end-before: doc device enable end
|
||||||
|
|
|
||||||
|
|
@ -15,27 +15,40 @@ LOG_MODULE_REGISTER(usbd_sample_config);
|
||||||
|
|
||||||
#define ZEPHYR_PROJECT_USB_VID 0x2fe3
|
#define ZEPHYR_PROJECT_USB_VID 0x2fe3
|
||||||
|
|
||||||
|
/* doc device instantiation start */
|
||||||
|
/*
|
||||||
|
* 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,
|
USBD_DEVICE_DEFINE(sample_usbd,
|
||||||
DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
|
DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
|
||||||
ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID);
|
ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID);
|
||||||
|
/* doc device instantiation end */
|
||||||
|
|
||||||
|
/* doc string instantiation start */
|
||||||
USBD_DESC_LANG_DEFINE(sample_lang);
|
USBD_DESC_LANG_DEFINE(sample_lang);
|
||||||
USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER);
|
USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER);
|
||||||
USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT);
|
USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT);
|
||||||
USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn);
|
USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn);
|
||||||
|
/* doc string instantiation end */
|
||||||
|
|
||||||
|
/* doc configuration instantiation start */
|
||||||
static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
|
static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ?
|
||||||
USB_SCD_SELF_POWERED : 0) |
|
USB_SCD_SELF_POWERED : 0) |
|
||||||
(IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
|
(IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ?
|
||||||
USB_SCD_REMOTE_WAKEUP : 0);
|
USB_SCD_REMOTE_WAKEUP : 0);
|
||||||
|
|
||||||
|
/* Full speed configuration */
|
||||||
USBD_CONFIGURATION_DEFINE(sample_fs_config,
|
USBD_CONFIGURATION_DEFINE(sample_fs_config,
|
||||||
attributes,
|
attributes,
|
||||||
CONFIG_SAMPLE_USBD_MAX_POWER);
|
CONFIG_SAMPLE_USBD_MAX_POWER);
|
||||||
|
|
||||||
|
/* High speed configuration */
|
||||||
USBD_CONFIGURATION_DEFINE(sample_hs_config,
|
USBD_CONFIGURATION_DEFINE(sample_hs_config,
|
||||||
attributes,
|
attributes,
|
||||||
CONFIG_SAMPLE_USBD_MAX_POWER);
|
CONFIG_SAMPLE_USBD_MAX_POWER);
|
||||||
|
/* doc configuration instantiation end */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This does not yet provide valuable information, but rather serves as an
|
* This does not yet provide valuable information, but rather serves as an
|
||||||
|
|
@ -73,6 +86,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
/* doc add string descriptor start */
|
||||||
err = usbd_add_descriptor(&sample_usbd, &sample_lang);
|
err = usbd_add_descriptor(&sample_usbd, &sample_lang);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Failed to initialize language descriptor (%d)", err);
|
LOG_ERR("Failed to initialize language descriptor (%d)", err);
|
||||||
|
|
@ -96,6 +110,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
||||||
LOG_ERR("Failed to initialize SN descriptor (%d)", err);
|
LOG_ERR("Failed to initialize SN descriptor (%d)", err);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* doc add string descriptor end */
|
||||||
|
|
||||||
if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
|
if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) {
|
||||||
err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS,
|
err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS,
|
||||||
|
|
@ -114,21 +129,26 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
||||||
sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS);
|
sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* doc configuration register start */
|
||||||
err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
|
err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
|
||||||
&sample_fs_config);
|
&sample_fs_config);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Failed to add Full-Speed configuration");
|
LOG_ERR("Failed to add Full-Speed configuration");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* doc configuration register end */
|
||||||
|
|
||||||
|
/* doc functions register start */
|
||||||
err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1);
|
err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Failed to add register classes");
|
LOG_ERR("Failed to add register classes");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* doc functions register end */
|
||||||
|
|
||||||
sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS);
|
sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS);
|
||||||
|
|
||||||
|
/* doc message callback register start */
|
||||||
if (msg_cb != NULL) {
|
if (msg_cb != NULL) {
|
||||||
err = usbd_msg_register_cb(&sample_usbd, msg_cb);
|
err = usbd_msg_register_cb(&sample_usbd, msg_cb);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
@ -136,6 +156,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* doc message callback register end */
|
||||||
|
|
||||||
if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) {
|
if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) {
|
||||||
(void)usbd_device_set_bcd(&sample_usbd, USBD_SPEED_FS, 0x0201);
|
(void)usbd_device_set_bcd(&sample_usbd, USBD_SPEED_FS, 0x0201);
|
||||||
|
|
@ -148,11 +169,13 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* doc device init start */
|
||||||
err = usbd_init(&sample_usbd);
|
err = usbd_init(&sample_usbd);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOG_ERR("Failed to initialize device support");
|
LOG_ERR("Failed to initialize device support");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* doc device init end */
|
||||||
|
|
||||||
return &sample_usbd;
|
return &sample_usbd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,11 +184,13 @@ int main(void)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* doc device enable start */
|
||||||
ret = usbd_enable(sample_usbd);
|
ret = usbd_enable(sample_usbd);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
LOG_ERR("Failed to enable device support");
|
LOG_ERR("Failed to enable device support");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
/* doc device enable end */
|
||||||
|
|
||||||
LOG_INF("HID keyboard sample is initialized");
|
LOG_INF("HID keyboard sample is initialized");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue