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
|
||||
``-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
|
||||
: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
|
||||
|
||||
/* 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,
|
||||
DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)),
|
||||
ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID);
|
||||
/* doc device instantiation end */
|
||||
|
||||
/* doc string instantiation start */
|
||||
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);
|
||||
/* doc string instantiation end */
|
||||
|
||||
/* doc configuration instantiation start */
|
||||
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);
|
||||
/* doc configuration instantiation end */
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* doc add string descriptor start */
|
||||
err = usbd_add_descriptor(&sample_usbd, &sample_lang);
|
||||
if (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);
|
||||
return NULL;
|
||||
}
|
||||
/* doc add string descriptor end */
|
||||
|
||||
if (usbd_caps_speed(&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);
|
||||
}
|
||||
|
||||
/* doc configuration register start */
|
||||
err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS,
|
||||
&sample_fs_config);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to add Full-Speed configuration");
|
||||
return NULL;
|
||||
}
|
||||
/* doc configuration register end */
|
||||
|
||||
/* doc functions register start */
|
||||
err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to add register classes");
|
||||
return NULL;
|
||||
}
|
||||
/* doc functions register end */
|
||||
|
||||
sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS);
|
||||
|
||||
/* doc message callback register start */
|
||||
if (msg_cb != NULL) {
|
||||
err = usbd_msg_register_cb(&sample_usbd, msg_cb);
|
||||
if (err) {
|
||||
|
@ -136,6 +156,7 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb)
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
/* doc message callback register end */
|
||||
|
||||
if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) {
|
||||
(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);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to initialize device support");
|
||||
return NULL;
|
||||
}
|
||||
/* doc device init end */
|
||||
|
||||
return &sample_usbd;
|
||||
}
|
||||
|
|
|
@ -184,11 +184,13 @@ int main(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* doc device enable start */
|
||||
ret = usbd_enable(sample_usbd);
|
||||
if (ret) {
|
||||
LOG_ERR("Failed to enable device support");
|
||||
return ret;
|
||||
}
|
||||
/* doc device enable end */
|
||||
|
||||
LOG_INF("HID keyboard sample is initialized");
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue