Hi there,
in the past year I've been developing a USB bridge.
The last major milestone for this bridge was the support for WebUSB.
For WebUSB to work on Windows WinUSB also need's to be added. This has a nice little side effect. A driver installation for our device isn't needed anymore.
I know there are some examples out there with these two working, but as far as I've seen, they have either used nrf_drv_usbd or the nRF Connect SDK.
Since I've started with the nRF5SDK and a usbd_class implementation, this wasn't a viable way for me.
Yesterday I've finally been able to get it to work.
This ticket isn't a question but more of a guide on how to get BOS descriptors to work with a usbd_class in the nRF5SDK.
This is by no means the best solution but a working one.
So let's get started:
There are 2 files within the SDK that need to be adapted: app_usbd_descriptor.h and app_usbd_core.c.
Additionally a module where all the new requests are handled needs to be added. I've gone with app_usbd_bos_desc.
In app_usbd_descriptor.h 2 things are added:
- To the enum app_usbd_descriptor_t APP_USBD_DESCRIPTOR_BINARY_DEVICE_OBJECT_STORE = 15 get's added so that we can handle BOS descriptors later on
- For easier handling of said descriptor the struct app_usbd_descriptor_bos_t get's added, which looks like this:
typedef struct { uint8_t bLength; uint8_t bDescriptorType; uint16_t sTotalLength; uint8_t bNumDeviceCaps; } app_usbd_descriptor_bos_t;
In app_usbd_core.c the following needs to be done:
- Change APP_USBD_CORE_DEVICE_DESCRIPTOR so that the USB version can be changed to 2.1. For a PC to request BOS descriptors the USB version must be greater than 2.0.
I've done it by adding the define APP_USBD_VERSION_MINOR to the sdk_config.h file which I've set to 1..bcdUSB = APP_USBD_BCD_VER_MAKE(2, APP_USBD_VERSION_MINOR, 0), /* USB BCD version */
- To the switch in the function setup_device_req_get_descriptor the following case is added:
case APP_USBD_DESCRIPTOR_BINARY_DEVICE_OBJECT_STORE: { if(p_setup_ev->setup.wLength.w == 0) { return NRF_SUCCESS; } uint8_t const * p_bos_dsc = app_usbd_bos_desc_get(); if (p_bos_dsc == NULL) { return NRF_ERROR_NOT_SUPPORTED; } return app_usbd_core_setup_rsp( &(p_setup_ev->setup), p_bos_dsc, app_usbd_bos_desc_length(p_bos_dsc)); }
With this case it is possible to inform the PC about the BOS descriptor including the WebUSB platform capability descriptor and the MSOS 2.0 platform capability descriptor. - Something the SDK doesn't support are vendor specific setup requests. To support these I've changed the setup_device_req_std_handler to the following:
static ret_code_t setup_device_req_std_handler(app_usbd_class_inst_t const * const p_inst, app_usbd_setup_evt_t const * const p_setup_ev) { ASSERT(p_inst != NULL); ASSERT(p_setup_ev != NULL); bool vendor_req = false; if (APP_USBD_SETUP_REQTYPE_STD != app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType)) { if(APP_USBD_SETUP_REQTYPE_VENDOR == app_usbd_setup_req_typ(p_setup_ev->setup.bmRequestType)) { vendor_req = true; } else return NRF_ERROR_NOT_SUPPORTED; } if (app_usbd_setup_req_dir(p_setup_ev->setup.bmRequestType) == APP_USBD_SETUP_REQDIR_IN) { if(vendor_req) { return app_usbd_vendor_setup(p_inst, p_setup_ev); } else { switch (p_setup_ev->setup.bRequest) { case APP_USBD_SETUP_STDREQ_GET_STATUS: { return setup_device_req_get_status(p_inst, p_setup_ev); } case APP_USBD_SETUP_STDREQ_GET_DESCRIPTOR: { return setup_device_req_get_descriptor(p_inst, p_setup_ev); } case APP_USBD_SETUP_STDREQ_GET_CONFIGURATION: { return setup_device_req_get_configuration(p_inst, p_setup_ev); } default: /*Not supported*/ break; } } return NRF_ERROR_NOT_SUPPORTED; } else /* APP_USBD_SETUP_REQDIR_OUT */ { ...
Now to handle all these changes the module app_usbd_bos_desc is needed.
The h-file looks like this:#ifndef APP_USBD_BOS_DESC_H__
#define APP_USBD_BOS_DESC_H__
#include <stdint.h>
#include "app_usbd.h"
#include "app_usbd_core.h"
/**
* @brief Get bos descriptor.
*
* @return BOS descriptor, or NULL if it does not exist.
* */
uint8_t const * app_usbd_bos_desc_get(void);
/**
* @brief Get bos length.
*
* Function for getting bos length from descriptor (descriptor returned by @ref app_usbd_bos_desc_get).
*
* @param[in] p_str BOS descriptor pointer.
* @return Total descriptor length in bytes.
*/
size_t app_usbd_bos_desc_length(uint8_t const * p_str);
ret_code_t app_usbd_vendor_setup(app_usbd_class_inst_t const * const p_inst,
app_usbd_setup_evt_t const * const p_setup_ev);
#endif
and the c-file like this:
#include "app_usbd_bos_desc.h"
#define NRF_LOG_MODULE_NAME app_usbd_bos_desc
#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();
#define MSOS20_DESC_LEN (0x1E)
#define BOS_DESC_LEN (0x39)
#define URL_DESC_LEN (0x19)
#define APP_USBD_SETUP_VENDORREQ_GET_MSOS2 0x02
#define APP_USBD_SETUP_VENDORREQ_GET_URL 0x01
#define APP_USBD_SETUP_VENDORREQ_LANDIGPAGE 0x01
uint8_t const m_bos_desc[BOS_DESC_LEN] = {
0x05,
0x0F,
BOS_DESC_LEN,
0x00,
0x02,
//WebUSB platform capability descriptor
0x18,
0x10,
0x05,
0x00,
0x38,
0xB6,
0x08,
0x34,
0xA9,
0x09,
0xA0,
0x47,
0x8B,
0xFD,
0xA0,
0x76,
0x88,
0x15,
0xB6,
0x65,
0x00,
0x01,
APP_USBD_SETUP_VENDORREQ_GET_URL,
APP_USBD_SETUP_VENDORREQ_LANDIGPAGE,
//MSOS 2.0 platform capability descriptor
0x1C,
0x10,
0x05,
0x00,
0xDF,
0x60,
0xDD,
0xD8,
0x89,
0x45,
0xC7,
0x4C,
0x9C,
0xD2,
0x65,
0x9D,
0x9E,
0x64,
0x8A,
0x9F,
0x00,
0x00,
0x03,
0x06,
MSOS20_DESC_LEN,
0x00,
APP_USBD_SETUP_VENDORREQ_GET_MSOS2,
0x00
};
uint8_t const m_msos2_desc[MSOS20_DESC_LEN] = {
//MOS2.0 descriptor set header
0x0A,
0x00,
0x00,
0x00,
0x00,
0x00,
0x03,
0x06,
MSOS20_DESC_LEN,
0x00,
//MOS2.0 compatible ID descriptor
0x14,
0x00,
0x03,
0x00,
'W',
'I',
'N',
'U',
'S',
'B',
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00
};
uint8_t const m_url_desc[URL_DESC_LEN] = {
//URL descriptor
URL_DESC_LEN,
0x03,
0x01,
'd',
'e',
'v',
'z',
'o',
'n',
'e',
'.',
'n',
'o',
'r',
'd',
'i',
'c',
's',
'e',
'm',
'i',
'.',
'c',
'o',
'm'
};
uint8_t const * app_usbd_bos_desc_get()
{
return (uint8_t*) &m_bos_desc;
}
static uint8_t const * app_usbd_bos_desc_get_msos2()
{
return (uint8_t*) &m_msos2_desc;
}
static uint8_t const * app_usbd_bos_desc_get_url()
{
return (uint8_t*) &m_url_desc;
}
size_t app_usbd_bos_desc_length(uint8_t const * p_str)
{
return ((const app_usbd_descriptor_bos_t *)p_str)->sTotalLength;
}
static size_t app_usbd_bos_desc_msos2_length(uint8_t const * p_str)
{
return MSOS20_DESC_LEN;
}
static size_t app_usbd_bos_desc_url_length(uint8_t const * p_str)
{
return URL_DESC_LEN;
}
static ret_code_t setup_device_req_get_msos2(
app_usbd_class_inst_t const * const p_inst,
app_usbd_setup_evt_t const * const p_setup_ev)
{
uint8_t const * p_trans_buff = app_usbd_bos_desc_get_msos2();
return app_usbd_core_setup_rsp(&p_setup_ev->setup, p_trans_buff, app_usbd_bos_desc_msos2_length(p_trans_buff));
}
static ret_code_t setup_device_req_get_url(
app_usbd_class_inst_t const * const p_inst,
app_usbd_setup_evt_t const * const p_setup_ev)
{
uint8_t const * p_trans_buff = app_usbd_bos_desc_get_url();
return app_usbd_core_setup_rsp(&p_setup_ev->setup, p_trans_buff, app_usbd_bos_desc_url_length(p_trans_buff));
}
ret_code_t app_usbd_vendor_setup(
app_usbd_class_inst_t const * const p_inst,
app_usbd_setup_evt_t const * const p_setup_ev)
{
switch (p_setup_ev->setup.bRequest)
{
case APP_USBD_SETUP_VENDORREQ_GET_MSOS2:
{
return setup_device_req_get_msos2(p_inst, p_setup_ev);
}
case APP_USBD_SETUP_VENDORREQ_GET_URL:
{
if(p_setup_ev->setup.wValue.lb == APP_USBD_SETUP_VENDORREQ_LANDIGPAGE)
return setup_device_req_get_url(p_inst, p_setup_ev);
}
default: break;
}
return NRF_ERROR_NOT_SUPPORTED;
}
With this WebUSB and WinUSB is working.
If you want to link to a different website you simply change m_url_desc and make sure to change URL_DESC_LEN to the right value (length of the website name + 3).
If you don't want to link to any website set APP_USBD_SETUP_VENDORREQ_LANDIGPAGE to 0.
I hope that if someone else has this problem this can help him/her.
Finally massive thanks to Andre for all his tips.
Kind regards,
Johannes