This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

USBD keyboard doesn't receive status led change. (Capslock, Numlock ...)

Hi Everybody,

I am working on USBD keyboard.

I used USBD example for implementation.

I changed mouse profile to keyboard profile.

And it's recognized well without problem in device manager.

But it doesn't get status change from PC. (capslock, numlock, ...)

When I test it using "usbd_hid_composite" example it receive well.  But that example is too complex to extend devices.

So I choosed USBD example.

SDK version: 17.0.2

Used example: USBD

Changes : mouse to keyboard

  • Modified main.c (mouse -> keyboard)

    /**
     * Copyright (c) 2016 - 2020, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    #include <stdint.h>
    #include <stdbool.h>
    #include <stddef.h>
    #include "nrf.h"
    #include "nrf_drv_usbd.h"
    #include "nrf_drv_clock.h"
    #include "nrf_gpio.h"
    #include "nrf_delay.h"
    #include "nrf_drv_power.h"
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    #include "app_timer.h"
    #include "app_error.h"
    #include "bsp.h"
    #include "bsp_cli.h"
    #include "nrf_cli.h"
    #include "nrf_cli_uart.h"
    
    /**
     * @brief CLI interface over UART
     */
    NRF_CLI_UART_DEF(m_cli_uart_transport, 0, 64, 16);
    NRF_CLI_DEF(m_cli_uart,
                "uart_cli:~$ ",
                &m_cli_uart_transport.transport,
                '\r',
                4);
    
    static bool m_send_flag = 0;
    
    #define BTN_DATA_SEND               0
    #define BTN_DATA_KEY_RELEASE        (bsp_event_t)(BSP_EVENT_KEY_LAST + 1)
    
    /**
     * @brief Button used to simulate mouse move
     *
     * Every button press would move the cursor one step in the square.
     */
    #define BTN_MOUSE_MOVE BSP_BOARD_BUTTON_0
    /**
     * @brief Button for system OFF request
     *
     * This button would set the request for system OFF.
     */
    #define BTN_SYSTEM_OFF BSP_BOARD_BUTTON_1
    
    /**
     * @brief Configuration status LED
     *
     * This LED would blink quickly (5 Hz) when device is not configured
     * or slowly (1 Hz) when configured and working properly.
     */
    #define LED_USB_STATUS BSP_BOARD_LED_0
    /**
     * @brief Power detect LED
     *
     * The LED is ON when connection is detected on USB port.
     * It is turned off when connection is removed.
     */
    #define LED_USB_POWER BSP_BOARD_LED_1
    
    /**
     * @brief Running LED
     *
     * LED that turns on when program is not sleeping
     */
    #define LED_RUNNING BSP_BOARD_LED_2
    
    /**
     * @brief Active LED
     *
     * LED that turns on when program is not in system OFF
     */
    #define LED_ACTIVE BSP_BOARD_LED_3
    
    /**
     * @brief Enable power USB detection
     *
     * Configure if example supports USB port connection
     */
    #ifndef USBD_POWER_DETECTION
    #define USBD_POWER_DETECTION true
    #endif
    
    /**
     * @brief Startup delay
     *
     * Number of microseconds to start USBD after powering up.
     * Kind of port insert debouncing.
     */
    #define STARTUP_DELAY 100
    
    
    /** Maximum size of the packed transfered by EP0 */
    #define EP0_MAXPACKETSIZE NRF_DRV_USBD_EPSIZE
    
    /** Device descriptor */
    #define USBD_DEVICE_DESCRIPTOR \
        0x12,                        /* bLength | size of descriptor                                                  */\
        0x01,                        /* bDescriptorType | descriptor type                                             */\
        0x00, 0x02,                  /* bcdUSB | USB spec release (ver 2.0)                                           */\
        0x00,                        /* bDeviceClass ¦ class code (each interface specifies class information)        */\
        0x00,                        /* bDeviceSubClass ¦ device sub-class (must be set to 0 because class code is 0) */\
        0x00,                        /* bDeviceProtocol | device protocol (no class specific protocol)                */\
        EP0_MAXPACKETSIZE,           /* bMaxPacketSize0 | maximum packet size (64 bytes)                              */\
        0x15, 0x19,                  /* vendor ID  (0x1915 Nordic)                                                    */\
        0x0A, 0x52,                  /* product ID (0x520A nRF52 HID mouse on nrf_drv)                                */\
        0x01, 0x01,                  /* bcdDevice | final device release number in BCD Format                         */\
        USBD_STRING_MANUFACTURER_IX, /* iManufacturer | index of manufacturer string                                  */\
        USBD_STRING_PRODUCT_IX,      /* iProduct | index of product string                                            */\
        USBD_STRING_SERIAL_IX,       /* iSerialNumber | Serial Number string                                          */\
        0x01                         /* bNumConfigurations | number of configurations                                 */
    
    /** Configuration descriptor */
    #define DEVICE_SELF_POWERED 1
    #define REMOTE_WU           1
    
    #define USBD_CONFIG_DESCRIPTOR_SIZE   9
    #define USBD_CONFIG_DESCRIPTOR_FULL_SIZE   (9 + (9 + 9 + 7))
    #define USBD_CONFIG_DESCRIPTOR  \
        0x09,         /* bLength | length of descriptor                                             */\
        0x02,         /* bDescriptorType | descriptor type (CONFIGURATION)                          */\
        USBD_CONFIG_DESCRIPTOR_FULL_SIZE, 0x00,    /* wTotalLength | total length of descriptor(s)  */\
        0x01,         /* bNumInterfaces                                                             */\
        0x01,         /* bConfigurationValue                                                        */\
        0x00,         /* index of string Configuration | configuration string index (not supported) */\
        0x80| (((DEVICE_SELF_POWERED) ? 1U:0U)<<6) | (((REMOTE_WU) ? 1U:0U)<<5), /* bmAttributes    */\
        49            /* maximum power in steps of 2mA (98mA)                                       */
    
    #define USBD_INTERFACE0_DESCRIPTOR  \
        0x09,         /* bLength                                                                          */\
        0x04,         /* bDescriptorType | descriptor type (INTERFACE)                                    */\
        0x00,         /* bInterfaceNumber                                                                 */\
        0x00,         /* bAlternateSetting                                                                */\
        0x01,         /* bNumEndpoints | number of endpoints (1)                                          */\
        0x03,         /* bInterfaceClass | interface class (3..defined by USB spec: HID)                  */\
        0x00,         /* bInterfaceSubClass |interface sub-class (0.. no boot interface)                  */\
        0x01,         /* bInterfaceProtocol | interface protocol (1..defined by USB spec: keyboard, 2 is mouse)          */\
        0x00          /* interface string index (not supported)                                           */
    
    /**
     * HID Table must normally be between Interface and EndPoint Descriptor
     * as written in HID spec§7.1 but it doesn't work with OSR2.1
     */
    #define USBD_HID0_DESCRIPTOR  \
        0x09,         /* bLength | length of descriptor (9 bytes)                    */\
        0x21,         /* bHIDDescriptor | descriptor type (HID)                      */\
        0x11, 0x01,   /* HID wBcdHID | Spec version 01.11                            */\
        0x00,         /* bCountryCode | HW Target country                            */\
        0x01,         /* bNumDescriptors | Number of HID class descriptors to follow */\
        0x22,         /* bDescriptorType | Report descriptor type is 0x22 (report)   */\
        (uint8_t)(USBD_KEYBOARD_REPORT_DESCRIPTOR_SIZE),      /* Total length of Report descr., low byte */ \
        (uint8_t)(USBD_KEYBOARD_REPORT_DESCRIPTOR_SIZE / 256) /* Total length of Report descr., high byte */
    
    #define USBD_ENDPOINT1_DESCRIPTOR  \
        0x07,         /* bLength | length of descriptor (7 bytes)                                     */\
        0x05,         /* bDescriptorType | descriptor type (ENDPOINT)                                 */\
        0x81,         /* bEndpointAddress | endpoint address (IN endpoint, endpoint 1)                */\
        0x03,         /* bmAttributes | endpoint attributes (interrupt)                               */\
        0x08,0x00,    /* bMaxPacketSizeLowByte,bMaxPacketSizeHighByte | maximum packet size (8 bytes) */\
        0x08          /* bInterval | polling interval (10ms)                                          */
    
    
    /**
     * String config descriptor
     */
    #define USBD_STRING_LANG_IX  0x00
    #define USBD_STRING_LANG \
        0x04,         /* length of descriptor                   */\
        0x03,         /* descriptor type                        */\
        0x09,         /*                                        */\
        0x04          /* Supported LangID = 0x0409 (US-English) */
    
    #define USBD_STRING_MANUFACTURER_IX  0x01
    #define USBD_STRING_MANUFACTURER \
        42,           /* length of descriptor (? bytes)   */\
        0x03,         /* descriptor type                  */\
        'N', 0x00,    /* Define Unicode String "Nordic Semiconductor  */\
        'o', 0x00, \
        'r', 0x00, \
        'd', 0x00, \
        'i', 0x00, \
        'c', 0x00, \
        ' ', 0x00, \
        'S', 0x00, \
        'e', 0x00, \
        'm', 0x00, \
        'i', 0x00, \
        'c', 0x00, \
        'o', 0x00, \
        'n', 0x00, \
        'd', 0x00, \
        'u', 0x00, \
        'c', 0x00, \
        't', 0x00, \
        'o', 0x00, \
        'r', 0x00
    
    #define USBD_STRING_PRODUCT_IX  0x02
    #define USBD_STRING_PRODUCT \
        78,           /* length of descriptor (? bytes)         */\
        0x03,         /* descriptor type                        */\
        'n', 0x00,    /* generic unicode string for all devices */\
        'R', 0x00, \
        'F', 0x00, \
        '5', 0x00, \
        '2', 0x00, \
        ' ', 0x00, \
        'U', 0x00, \
        'S', 0x00, \
        'B', 0x00, \
        ' ', 0x00, \
        'H', 0x00, \
        'I', 0x00, \
        'D', 0x00, \
        ' ', 0x00, \
        'k', 0x00, \
        'e', 0x00, \
        'y', 0x00, \
        'b', 0x00, \
        'o', 0x00, \
        'a', 0x00, \
        'r', 0x00, \
        'd', 0x00, \
        ' ', 0x00, \
        'o', 0x00, \
        'n', 0x00, \
        ' ', 0x00, \
        'n', 0x00, \
        'r', 0x00, \
        'f', 0x00, \
        '_', 0x00, \
        'd', 0x00, \
        'r', 0x00, \
        'v', 0x00, \
        ' ', 0x00, \
        'D', 0x00, \
        'e', 0x00, \
        'm', 0x00, \
        'o', 0x00, \
    
    #define USBD_STRING_SERIAL_IX  0x00
    
    #define USBD_KEYBOARD_REPORT_DESCRIPTOR_SIZE  63
    #define USBD_KEYBOARD_REPORT_DESCRIPTOR \
    	0x05, 0x01, \
    	0x09, 0x06, \
    	0xa1, 0x01, \
    	0x05, 0x07, \
    	0x19, 0xe0, \
    	0x29, 0xe7, \
    	0x15, 0x00, \
    	0x25, 0x01, \
    	0x75, 0x01, \
    	0x95, 0x08, \
    	0x81, 0x02, \
    	0x95, 0x01, \
    	0x75, 0x08, \
    	0x81, 0x03, \
    	0x95, 0x05, \
    	0x75, 0x01, \
    	0x05, 0x08, \
    	0x19, 0x01, \
    	0x29, 0x05, \
    	0x91, 0x02, \
    	0x95, 0x01, \
    	0x75, 0x03, \
    	0x91, 0x03, \
    	0x95, 0x06, \
    	0x75, 0x08, \
    	0x15, 0x00, \
    	0x25, 0x65, \
    	0x05, 0x07, \
    	0x19, 0x00, \
    	0x29, 0x65, \
    	0x81, 0x00, \
    	0xc0
    
    
    static const uint8_t get_descriptor_device[] = {
        USBD_DEVICE_DESCRIPTOR
    };
    
    static const uint8_t get_descriptor_configuration[] = {
        USBD_CONFIG_DESCRIPTOR,
        USBD_INTERFACE0_DESCRIPTOR,
        USBD_HID0_DESCRIPTOR,
        USBD_ENDPOINT1_DESCRIPTOR
    };
    static const uint8_t get_descriptor_string_lang[] = {
        USBD_STRING_LANG
    };
    static const uint8_t get_descriptor_string_manuf[] = {
        USBD_STRING_MANUFACTURER
    };
    static const uint8_t get_descriptor_string_prod[] = {
        USBD_STRING_PRODUCT
    };
    static const uint8_t get_descriptor_report_interface_0[] = {
        USBD_KEYBOARD_REPORT_DESCRIPTOR
    };
    
    static const uint8_t get_config_resp_configured[]   = {1};
    static const uint8_t get_config_resp_unconfigured[] = {0};
    
    static const uint8_t get_status_device_resp_nrwu[] = {
        ((DEVICE_SELF_POWERED) ? 1 : 0), //LSB first: self-powered, no remoteWk
        0
    };
    static const uint8_t get_status_device_resp_rwu[]  = {
        ((DEVICE_SELF_POWERED) ? 1 : 0) | 2, //LSB first: self-powered, remoteWk
        0
    };
    
    static const uint8_t get_status_interface_resp[] = {0, 0};
    static const uint8_t get_status_ep_halted_resp[] = {1, 0};
    static const uint8_t get_status_ep_active_resp[] = {0, 0};
    
    
    #define GET_CONFIG_DESC_SIZE    sizeof(get_descriptor_configuration)
    #define GET_INTERFACE_DESC_SIZE 9
    #define GET_HID_DESC_SIZE       9
    #define GET_ENDPOINT_DESC_SIZE  7
    
    #define get_descriptor_interface_0 \
        &get_descriptor_configuration[9]
    #define get_descriptor_hid_0       \
        &get_descriptor_configuration[9+GET_INTERFACE_DESC_SIZE]
    #define get_descriptor_endpoint_1  \
        &get_descriptor_configuration[9+GET_INTERFACE_DESC_SIZE+GET_HID_DESC_SIZE]
    
    /**
     * @brief USB configured flag
     *
     * The flag that is used to mark the fact that USB is configured and ready
     * to transmit data
     */
    static volatile bool m_usbd_configured = false;
    
    /**
     * @brief USB suspended
     *
     * The flag that is used to mark the fact that USB is suspended and requires wake up
     * if new data is available.
     *
     * @note This variable is changed from the main loop.
     */
    static bool m_usbd_suspended = false;
    
    /**
     * @brief Mark the fact if remote wake up is enabled
     *
     * The internal flag that marks if host enabled the remote wake up functionality in this device.
     */
    static
    #if REMOTE_WU
        volatile // Disallow optimization only if Remote wakeup is enabled
    #endif
    bool m_usbd_rwu_enabled = false;
    
    /**
     * @brief Current mouse position
     *
     * The index of current mouse position that would be changed to real offset.
     */
    static volatile uint8_t m_mouse_position = 0;
    
    /**
     * @brief The flag for mouse position send pending
     *
     * Setting this flag means that USB endpoint is busy by sending
     * last mouse position.
     */
    static volatile bool m_send_mouse_position = false;
    
    /**
     * @brief The requested suspend state
     *
     * The currently requested suspend state based on the events
     * received from USBD library.
     * If the value here is different than the @ref m_usbd_suspended
     * the state changing would be processed inside main loop.
     */
    static volatile bool m_usbd_suspend_state_req = false;
    
    /**
     * @brief System OFF request flag
     *
     * This flag is used in button event processing and marks the fact that
     * system OFF should be activated from main loop.
     */
    static volatile bool m_system_off_req = false;
    
    
    /**
     * @brief Setup all the endpoints for selected configuration
     *
     * Function sets all the endpoints for specific configuration.
     *
     * @note
     * Setting the configuration index 0 means technically disabling the HID interface.
     * Such configuration should be set when device is starting or USB reset is detected.
     *
     * @param index Configuration index
     *
     * @retval NRF_ERROR_INVALID_PARAM Invalid configuration
     * @retval NRF_SUCCESS             Configuration successfully set
     */
    static ret_code_t ep_configuration(uint8_t index)
    {
        if ( index == 1 )
        {
            nrf_drv_usbd_ep_dtoggle_clear(NRF_DRV_USBD_EPIN1);
            nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPIN1);
            nrf_drv_usbd_ep_enable(NRF_DRV_USBD_EPIN1);
            m_usbd_configured = true;
            nrf_drv_usbd_setup_clear();
        }
        else if ( index == 0 )
        {
            nrf_drv_usbd_ep_disable(NRF_DRV_USBD_EPIN1);
            m_usbd_configured = false;
            nrf_drv_usbd_setup_clear();
        }
        else
        {
            return NRF_ERROR_INVALID_PARAM;
        }
        return NRF_SUCCESS;
    }
    
    /**
     * @name Processing setup requests
     *
     * @{
     */
    /**
     * @brief Respond on ep 0
     *
     * Auxiliary function for sending respond on endpoint 0
     * @param[in] p_setup Pointer to setup data from current setup request.
     *                    It would be used to calculate the size of data to send.
     * @param[in] p_data  Pointer to the data to send.
     * @param[in] size    Number of bytes to send.
     * @note Data pointed by p_data has to be available till the USBD_EVT_BUFREADY event.
     */
    static void respond_setup_data(
        nrf_drv_usbd_setup_t const * const p_setup,
        void const * p_data, size_t size)
    {
        /* Check the size against required response size */
        if (size > p_setup->wLength)
        {
            size = p_setup->wLength;
        }
        ret_code_t ret;
        nrf_drv_usbd_transfer_t transfer =
        {
            .p_data = {.tx = p_data},
            .size = size
        };
        ret = nrf_drv_usbd_ep_transfer(NRF_DRV_USBD_EPIN0, &transfer);
        if (ret != NRF_SUCCESS)
        {
            NRF_LOG_ERROR("Transfer starting failed: %d", (uint32_t)ret);
        }
        ASSERT(ret == NRF_SUCCESS);
        UNUSED_VARIABLE(ret);
    }
    
    
    /** React to GetStatus */
    static void usbd_setup_GetStatus(nrf_drv_usbd_setup_t const * const p_setup)
    {
        switch (p_setup->bmRequestType)
        {
        case 0x80: // Device
            if (((p_setup->wIndex) & 0xff) == 0)
            {
                respond_setup_data(
                    p_setup,
                    m_usbd_rwu_enabled ? get_status_device_resp_rwu : get_status_device_resp_nrwu,
                    sizeof(get_status_device_resp_nrwu));
                return;
            }
            break;
        case 0x81: // Interface
            if (m_usbd_configured) // Respond only if configured
            {
                if (((p_setup->wIndex) & 0xff) == 0) // Only interface 0 supported
                {
                    respond_setup_data(
                        p_setup,
                        get_status_interface_resp,
                        sizeof(get_status_interface_resp));
                    return;
                }
            }
            break;
        case 0x82: // Endpoint
            if (((p_setup->wIndex) & 0xff) == 0) // Endpoint 0
            {
                respond_setup_data(
                    p_setup,
                    get_status_ep_active_resp,
                    sizeof(get_status_ep_active_resp));
                return;
            }
            if (m_usbd_configured) // Other endpoints responds if configured
            {
                if (((p_setup->wIndex) & 0xff) == NRF_DRV_USBD_EPIN1)
                {
                    if (nrf_drv_usbd_ep_stall_check(NRF_DRV_USBD_EPIN1))
                    {
                        respond_setup_data(
                            p_setup,
                            get_status_ep_halted_resp,
                            sizeof(get_status_ep_halted_resp));
                        return;
                    }
                    else
                    {
                        respond_setup_data(
                            p_setup,
                            get_status_ep_active_resp,
                            sizeof(get_status_ep_active_resp));
                        return;
                    }
                }
            }
            break;
        default:
            break; // Just go to stall
        }
        NRF_LOG_ERROR("Unknown status: 0x%2x", p_setup->bmRequestType);
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_ClearFeature(nrf_drv_usbd_setup_t const * const p_setup)
    {
        if ((p_setup->bmRequestType) == 0x02) // standard request, recipient=endpoint
        {
            if ((p_setup->wValue) == 0)
            {
                if ((p_setup->wIndex) == NRF_DRV_USBD_EPIN1)
                {
                    nrf_drv_usbd_ep_stall_clear(NRF_DRV_USBD_EPIN1);
                    nrf_drv_usbd_setup_clear();
                    return;
                }
            }
        }
        else if ((p_setup->bmRequestType) ==  0x0) // standard request, recipient=device
        {
            if (REMOTE_WU)
            {
                if ((p_setup->wValue) == 1) // Feature Wakeup
                {
                    m_usbd_rwu_enabled = false;
                    nrf_drv_usbd_setup_clear();
                    return;
                }
            }
        }
        NRF_LOG_ERROR("Unknown feature to clear");
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_SetFeature(nrf_drv_usbd_setup_t const * const p_setup)
    {
        if ((p_setup->bmRequestType) == 0x02) // standard request, recipient=endpoint
        {
            if ((p_setup->wValue) == 0) // Feature HALT
            {
                if ((p_setup->wIndex) == NRF_DRV_USBD_EPIN1)
                {
                    nrf_drv_usbd_ep_stall(NRF_DRV_USBD_EPIN1);
                    nrf_drv_usbd_setup_clear();
                    return;
                }
            }
        }
        else if ((p_setup->bmRequestType) ==  0x0) // standard request, recipient=device
        {
            if (REMOTE_WU)
            {
                if ((p_setup->wValue) == 1) // Feature Wakeup
                {
                    m_usbd_rwu_enabled = true;
                    nrf_drv_usbd_setup_clear();
                    return;
                }
            }
        }
        NRF_LOG_ERROR("Unknown feature to set");
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_GetDescriptor(nrf_drv_usbd_setup_t const * const p_setup)
    {
        //determine which descriptor has been asked for
        switch ((p_setup->wValue) >> 8)
        {
        case 1: // Device
            if ((p_setup->bmRequestType) == 0x80)
            {
                respond_setup_data(
                    p_setup,
                    get_descriptor_device,
                    sizeof(get_descriptor_device));
                return;
            }
            break;
        case 2: // Configuration
            if ((p_setup->bmRequestType) == 0x80)
            {
                respond_setup_data(
                    p_setup,
                    get_descriptor_configuration,
                    GET_CONFIG_DESC_SIZE);
                return;
            }
            break;
        case 3: // String
            if ((p_setup->bmRequestType) == 0x80)
            {
                // Select the string
                switch ((p_setup->wValue) & 0xFF)
                {
                case USBD_STRING_LANG_IX:
                    respond_setup_data(
                        p_setup,
                        get_descriptor_string_lang,
                        sizeof(get_descriptor_string_lang));
                    return;
                case USBD_STRING_MANUFACTURER_IX:
                    respond_setup_data(
                        p_setup,
                        get_descriptor_string_manuf,
                        sizeof(get_descriptor_string_manuf));
                    return;
                case USBD_STRING_PRODUCT_IX:
                    respond_setup_data(p_setup,
                        get_descriptor_string_prod,
                        sizeof(get_descriptor_string_prod));
                    return;
                default:
                    break;
                }
            }
            break;
        case 4: // Interface
            if ((p_setup->bmRequestType) == 0x80)
            {
                // Which interface?
                if ((((p_setup->wValue) & 0xFF) == 0))
                {
                    respond_setup_data(
                        p_setup,
                        get_descriptor_interface_0,
                        GET_INTERFACE_DESC_SIZE);
                    return;
                }
            }
            break;
        case 5: // Endpoint
            if ((p_setup->bmRequestType) == 0x80)
            {
                // Which endpoint?
                if (((p_setup->wValue) & 0xFF) == 1)
                {
                    respond_setup_data(
                        p_setup,
                        get_descriptor_endpoint_1,
                        GET_ENDPOINT_DESC_SIZE);
                    return;
                }
            }
            break;
        case 0x21: // HID
            if ((p_setup->bmRequestType) == 0x81)
            {
                // Which interface
                if (((p_setup->wValue) & 0xFF) == 0)
                {
                    respond_setup_data(
                        p_setup,
                        get_descriptor_hid_0,
                        GET_HID_DESC_SIZE);
                    return;
                }
            }
            break;
        case 0x22: // HID report
            if ((p_setup->bmRequestType) == 0x81)
            {
                // Which interface?
                if (((p_setup->wValue) & 0xFF) == 0)
                {
                    respond_setup_data(
                        p_setup,
                        get_descriptor_report_interface_0,
                        sizeof(get_descriptor_report_interface_0));
                    return;
                }
            }
            break;
        default:
            break; // Not supported - go to stall
        }
    
        NRF_LOG_ERROR("Unknown descriptor requested: 0x%2x, type: 0x%2x or value: 0x%2x",
            p_setup->wValue >> 8,
            p_setup->bmRequestType,
            p_setup->wValue & 0xFF);
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_GetConfig(nrf_drv_usbd_setup_t const * const p_setup)
    {
        if (m_usbd_configured)
        {
            respond_setup_data(
                p_setup,
                get_config_resp_configured,
                sizeof(get_config_resp_configured));
        }
        else
        {
            respond_setup_data(
                p_setup,
                get_config_resp_unconfigured,
                sizeof(get_config_resp_unconfigured));
        }
    }
    
    static void usbd_setup_SetConfig(nrf_drv_usbd_setup_t const * const p_setup)
    {
        if ((p_setup->bmRequestType) == 0x00)
        {
            // accept only 0 and 1
            if (((p_setup->wIndex) == 0) && ((p_setup->wLength) == 0) &&
                ((p_setup->wValue) <= UINT8_MAX))
            {
                if (NRF_SUCCESS == ep_configuration((uint8_t)(p_setup->wValue)))
                {
                    nrf_drv_usbd_setup_clear();
                    return;
                }
            }
        }
        NRF_LOG_ERROR("Wrong configuration: Index: 0x%2x, Value: 0x%2x.",
            p_setup->wIndex,
            p_setup->wValue);
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_SetIdle(nrf_drv_usbd_setup_t const * const p_setup)
    {
        if (p_setup->bmRequestType == 0x21)
        {
            //accept any value
            nrf_drv_usbd_setup_clear();
            return;
        }
        NRF_LOG_ERROR("Set Idle wrong type: 0x%2x.", p_setup->bmRequestType);
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_SetInterface(
        nrf_drv_usbd_setup_t const * const p_setup)
    {
        //no alternate setting is supported - STALL always
        NRF_LOG_ERROR("No alternate interfaces supported.");
        nrf_drv_usbd_setup_stall();
    }
    
    static void usbd_setup_SetProtocol(
        nrf_drv_usbd_setup_t const * const p_setup)
    {
        if (p_setup->bmRequestType == 0x21)
        {
            //accept any value
            nrf_drv_usbd_setup_clear();
            return;
        }
        NRF_LOG_ERROR("Set Protocol wrong type: 0x%2x.", p_setup->bmRequestType);
        nrf_drv_usbd_setup_stall();
    }
    
    /** @} */ /* End of processing setup requests functions */
    
    
    static void usbd_event_handler(nrf_drv_usbd_evt_t const * const p_event)
    {
        switch (p_event->type)
        {
        case NRF_DRV_USBD_EVT_SUSPEND:
            NRF_LOG_INFO("SUSPEND state detected");
            m_usbd_suspend_state_req = true;
            break;
        case NRF_DRV_USBD_EVT_RESUME:
            NRF_LOG_INFO("RESUMING from suspend");
            m_usbd_suspend_state_req = false;
            break;
        case NRF_DRV_USBD_EVT_WUREQ:
            NRF_LOG_INFO("RemoteWU initiated");
            m_usbd_suspend_state_req = false;
            break;
        case NRF_DRV_USBD_EVT_RESET:
            {
                ret_code_t ret = ep_configuration(0);
                ASSERT(ret == NRF_SUCCESS);
                UNUSED_VARIABLE(ret);
                m_usbd_suspend_state_req = false;
                break;
            }
        case NRF_DRV_USBD_EVT_SOF:
            {
                static uint32_t cycle = 0;
                ++cycle;
                if ((cycle % (m_usbd_configured ? 500 : 100)) == 0)
                {
                    bsp_board_led_invert(LED_USB_STATUS);
                }
                break;
            }
        case NRF_DRV_USBD_EVT_EPTRANSFER:
            if (NRF_DRV_USBD_EPIN1 == p_event->data.eptransfer.ep)
            {
                m_send_mouse_position = false;
            }
            else
            if (NRF_DRV_USBD_EPIN0 == p_event->data.eptransfer.ep)
            {
                if (NRF_USBD_EP_OK == p_event->data.eptransfer.status)
                {
                    /* Transfer ok - allow status stage */
                    nrf_drv_usbd_setup_clear();
                }
                else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status)
                {
                    /* Just ignore */
                    NRF_LOG_INFO("Transfer aborted event on EPIN0");
                }
                else
                {
                    NRF_LOG_ERROR("Transfer failed on EPIN0: %d", p_event->data.eptransfer.status);
                    nrf_drv_usbd_setup_stall();
                }
            }
            else
            if (NRF_DRV_USBD_EPOUT0 == p_event->data.eptransfer.ep)
            {
                /* NOTE: No EPOUT0 data transfers are used.
                 * The code is here as a pattern how to support such a transfer. */
                if (NRF_USBD_EP_OK == p_event->data.eptransfer.status)
                {
                    /* Transfer ok - allow status stage */
                    nrf_drv_usbd_setup_clear();
                }
                else if (NRF_USBD_EP_ABORTED == p_event->data.eptransfer.status)
                {
                    /* Just ignore */
                    NRF_LOG_INFO("Transfer aborted event on EPOUT0");
                }
                else
                {
                    NRF_LOG_ERROR("Transfer failed on EPOUT0: %d", p_event->data.eptransfer.status);
                    nrf_drv_usbd_setup_stall();
                }
            }
            else
            {
                /* Nothing to do */
            }
            break;
        case NRF_DRV_USBD_EVT_SETUP:
            {
                nrf_drv_usbd_setup_t setup;
                nrf_drv_usbd_setup_get(&setup);
                switch (setup.bRequest)
                {
                case 0x00: // GetStatus
                    usbd_setup_GetStatus(&setup);
                    break;
                case 0x01: // CleartFeature
                    usbd_setup_ClearFeature(&setup);
                    break;
                case 0x03: // SetFeature
                    usbd_setup_SetFeature(&setup);
                    break;
                case 0x05: // SetAddress
                    //nothing to do, handled by hardware; but don't STALL
                    break;
                case 0x06: // GetDescriptor
                    usbd_setup_GetDescriptor(&setup);
                    break;
                case 0x08: // GetConfig
                    usbd_setup_GetConfig(&setup);
                    break;
                case 0x09: // SetConfig
                    usbd_setup_SetConfig(&setup);
                    break;
                //HID class
                case 0x0A: // SetIdle
                    usbd_setup_SetIdle(&setup);
                    break;
                case 0x0B: // SetProtocol or SetInterface
                    if (setup.bmRequestType == 0x01) // standard request, recipient=interface
                    {
                        usbd_setup_SetInterface(&setup);
                    }
                    else if (setup.bmRequestType == 0x21) // class request, recipient=interface
                    {
                        usbd_setup_SetProtocol(&setup);
                    }
                    else
                    {
                        NRF_LOG_ERROR("Command 0xB. Unknown request: 0x%2x", setup.bmRequestType);
                        nrf_drv_usbd_setup_stall();
                    }
                    break;
                default:
                    NRF_LOG_ERROR("Unknown request: 0x%2x", setup.bRequest);
                    nrf_drv_usbd_setup_stall();
                    return;
                }
                break;
            }
        default:
            break;
        }
    }
    
    
    static void move_mouse_pointer(void)
    {
        static uint32_t databuffer;
    
        if (!m_usbd_configured)
            return;
        if (!m_send_mouse_position)
        {
            switch (m_mouse_position & 0x3)
            {
            case 0:
                /* X = 10, rest all are unchanged */
                databuffer = 0x00000A00;
                break;
            case 1:
                /* Y = 10, rest all are unchanged */
                databuffer = 0x000A0000;
                break;
            case 2:
                /* X = -10, rest all are unchanged */
                databuffer = 0x0000F600;
                break;
            case 3:
                /* Y = -10, rest all are unchanged */
                databuffer = 0x00F60000;
                break;
            }
            m_mouse_position++;
    
            /* Send data */
            static const nrf_drv_usbd_transfer_t transfer =
            {
                .p_data = {.tx = &databuffer},
                .size = sizeof(databuffer)
            };
            m_send_mouse_position = true;
            UNUSED_RETURN_VALUE(nrf_drv_usbd_ep_transfer(
                NRF_DRV_USBD_EPIN1,
                &transfer));
        }
    }
    
    static void power_usb_event_handler(nrf_drv_power_usb_evt_t event)
    {
        switch (event)
        {
        case NRF_DRV_POWER_USB_EVT_DETECTED:
            NRF_LOG_INFO("USB power detected");
            if (!nrf_drv_usbd_is_enabled())
            {
                nrf_drv_usbd_enable();
            }
            break;
        case NRF_DRV_POWER_USB_EVT_REMOVED:
            NRF_LOG_INFO("USB power removed");
            m_usbd_configured = false;
            m_send_mouse_position = false;
            if (nrf_drv_usbd_is_started())
            {
                nrf_drv_usbd_stop();
            }
            if (nrf_drv_usbd_is_enabled())
            {
                nrf_drv_usbd_disable();
            }
            /* Turn OFF LEDs */
            bsp_board_led_off(LED_USB_STATUS);
            bsp_board_led_off(LED_USB_POWER);
            break;
        case NRF_DRV_POWER_USB_EVT_READY:
            NRF_LOG_INFO("USB ready");
            bsp_board_led_on(LED_USB_POWER);
            if (!nrf_drv_usbd_is_started())
            {
                nrf_drv_usbd_start(true);
            }
            break;
        default:
            ASSERT(false);
        }
    }
    
    static void bsp_evt_handler(bsp_event_t evt)
    {
        switch ((unsigned int)evt)
        {
        case BSP_EVENT_SYSOFF:
        {
            m_system_off_req = true;
            break;
        }
        case CONCAT_2(BSP_EVENT_KEY_, BTN_DATA_SEND):
        {
            m_send_flag = 1;
            break;
        }
    
        case BTN_DATA_KEY_RELEASE:
        {
            m_send_flag = 0;
            break;
        }
        default:
            return;
        }
    }
    
    static void init_power_clock(void)
    {
        ret_code_t ret;
        /* Initializing power and clock */
        ret = nrf_drv_clock_init();
        APP_ERROR_CHECK(ret);
        ret = nrf_drv_power_init(NULL);
        APP_ERROR_CHECK(ret);
        nrf_drv_clock_hfclk_request(NULL);
        nrf_drv_clock_lfclk_request(NULL);
        while (!(nrf_drv_clock_hfclk_is_running() &&
                nrf_drv_clock_lfclk_is_running()))
        {
            /* Just waiting */
        }
    
        ret = app_timer_init();
        APP_ERROR_CHECK(ret);
    
        /* Avoid warnings if assertion is disabled */
        UNUSED_VARIABLE(ret);
    }
    
    static void init_bsp(void)
    {
        ret_code_t ret;
        ret = bsp_init(BSP_INIT_BUTTONS, bsp_evt_handler);
        APP_ERROR_CHECK(ret);
    
        ret = bsp_event_to_button_action_assign(
            BTN_SYSTEM_OFF,
            BSP_BUTTON_ACTION_RELEASE,
            BSP_EVENT_SYSOFF);
        APP_ERROR_CHECK(ret);
        ret = bsp_event_to_button_action_assign(BTN_DATA_SEND,
                                                BSP_BUTTON_ACTION_RELEASE,
                                                BTN_DATA_KEY_RELEASE);
        APP_ERROR_CHECK(ret);
        /* Avoid warnings if assertion is disabled */
        UNUSED_VARIABLE(ret);
    }
    
    static void init_cli(void)
    {
        ret_code_t ret;
        ret = bsp_cli_init(bsp_evt_handler);
        APP_ERROR_CHECK(ret);
        nrf_drv_uart_config_t uart_config = NRF_DRV_UART_DEFAULT_CONFIG;
        uart_config.pseltxd = TX_PIN_NUMBER;
        uart_config.pselrxd = RX_PIN_NUMBER;
        uart_config.hwfc    = NRF_UART_HWFC_DISABLED;
        ret = nrf_cli_init(&m_cli_uart, &uart_config, true, true, NRF_LOG_SEVERITY_INFO);
        APP_ERROR_CHECK(ret);
        ret = nrf_cli_start(&m_cli_uart);
        APP_ERROR_CHECK(ret);
    }
    
    static void log_resetreason(void)
    {
        /* Reset reason */
        uint32_t rr = nrf_power_resetreas_get();
        NRF_LOG_INFO("Reset reasons:");
        if (0 == rr)
        {
            NRF_LOG_INFO("- NONE");
        }
        if (0 != (rr & NRF_POWER_RESETREAS_RESETPIN_MASK))
        {
            NRF_LOG_INFO("- RESETPIN");
        }
        if (0 != (rr & NRF_POWER_RESETREAS_DOG_MASK     ))
        {
            NRF_LOG_INFO("- DOG");
        }
        if (0 != (rr & NRF_POWER_RESETREAS_SREQ_MASK    ))
        {
            NRF_LOG_INFO("- SREQ");
        }
        if (0 != (rr & NRF_POWER_RESETREAS_LOCKUP_MASK  ))
        {
            NRF_LOG_INFO("- LOCKUP");
        }
        if (0 != (rr & NRF_POWER_RESETREAS_OFF_MASK     ))
        {
            NRF_LOG_INFO("- OFF");
        }
    #if defined(NRF_POWER_RESETREAS_LPCOMP_MASK)
        if (0 != (rr & NRF_POWER_RESETREAS_LPCOMP_MASK  ))
        {
            NRF_LOG_INFO("- LPCOMP");
        }
    #endif
        if (0 != (rr & NRF_POWER_RESETREAS_DIF_MASK     ))
        {
            NRF_LOG_INFO("- DIF");
        }
    #if defined(NRF_POWER_RESETREAS_NFC_MASK)
        if (0 != (rr & NRF_POWER_RESETREAS_NFC_MASK     ))
        {
            NRF_LOG_INFO("- NFC");
        }
    #endif
        if (0 != (rr & NRF_POWER_RESETREAS_VBUS_MASK    ))
        {
            NRF_LOG_INFO("- VBUS");
        }
    }
    
    int main(void)
    {
        ret_code_t ret;
        UNUSED_RETURN_VALUE(NRF_LOG_INIT(NULL));
    
        init_power_clock();
        init_bsp();
        init_cli();
    
        NRF_LOG_INFO("USDB example started.");
        log_resetreason();
        nrf_power_resetreas_clear(nrf_power_resetreas_get());
    
        /* USB work starts right here */
        ret = nrf_drv_usbd_init(usbd_event_handler);
        APP_ERROR_CHECK(ret);
    
        /* Configure selected size of the packed on EP0 */
        nrf_drv_usbd_ep_max_packet_size_set(NRF_DRV_USBD_EPOUT0, EP0_MAXPACKETSIZE);
        nrf_drv_usbd_ep_max_packet_size_set(NRF_DRV_USBD_EPIN0, EP0_MAXPACKETSIZE);
    
        /* Configure LED and button */
        bsp_board_init(BSP_INIT_LEDS);
        bsp_board_led_on(LED_RUNNING);
        bsp_board_led_on(LED_ACTIVE);
    
    
        if (USBD_POWER_DETECTION)
        {
            static const nrf_drv_power_usbevt_config_t config =
            {
                .handler = power_usb_event_handler
            };
            ret = nrf_drv_power_usbevt_init(&config);
            APP_ERROR_CHECK(ret);
        }
        else
        {
            NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
            nrf_delay_us(STARTUP_DELAY);
            if (!nrf_drv_usbd_is_enabled())
            {
                nrf_drv_usbd_enable();
                ret = ep_configuration(0);
                APP_ERROR_CHECK(ret);
            }
            /* Wait for regulator power up */
            while (NRF_DRV_POWER_USB_STATE_CONNECTED
                  ==
                  nrf_drv_power_usbstatus_get())
            {
                /* Just waiting */
            }
    
            if (NRF_DRV_POWER_USB_STATE_READY == nrf_drv_power_usbstatus_get())
            {
                if (!nrf_drv_usbd_is_started())
                {
                    nrf_drv_usbd_start(true);
                }
            }
            else
            {
                nrf_drv_usbd_disable();
            }
        }
    
    
        while (true)
        {
            if (m_system_off_req)
            {
                NRF_LOG_INFO("Going to system OFF");
                NRF_LOG_FLUSH();
                bsp_board_led_off(LED_RUNNING);
                bsp_board_led_off(LED_ACTIVE);
                nrf_power_system_off();
            }
            if (m_usbd_suspended != m_usbd_suspend_state_req)
            {
                if (m_usbd_suspend_state_req)
                {
                    m_usbd_suspended = nrf_drv_usbd_suspend();
                    if (m_usbd_suspended)
                    {
                        bsp_board_leds_off();
                    }
                }
                else
                {
                    m_usbd_suspended = false;
                }
            }
    
            if (m_usbd_configured)
            {
                if (m_send_flag)
                {
                    if (m_usbd_suspended)
                    {
                        if (m_usbd_rwu_enabled)
                        {
                            UNUSED_RETURN_VALUE(nrf_drv_usbd_wakeup_req());
                        }
                    }
                    else
                    {
                        NRF_LOG_INFO("   TX pointer");
                        move_mouse_pointer();
                    }
                }
            }
    
            nrf_cli_process(&m_cli_uart);
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
            bsp_board_led_off(LED_RUNNING);
            /* Even if we miss an event enabling USB,
             * USB event would wake us up. */
            __WFE();
            /* Clear SEV flag if CPU was woken up by event */
            __SEV();
            __WFE();
            bsp_board_led_on(LED_RUNNING);
        }
    }
    

  • If I press capslock, usbd_setup_SetConfig is called. and below log  (main.c LINE: 792) is shown.

    <error> app: Wrong configuration: Index: 0x 0, Value: 0x200.

  • Hi

    What do you mean by the "usbd_hid_composite" example is too complex to extend devices? By default the USBD_HID_composite example uses a keyboard interface, so I would suggest using that for reference instead of modifying the entire USBD example.

    Getting an error from the usbd_setup points to your SETUP packet not being correct according to the USB specification, so I would double check how you've set up the keyboard on your end. 

    Best regards,

    Simon

  • Hi Simonr,

    Thank you for follow up.

    I meant "complex" is read to hard because of many macros. (I think these macro is only good for designers )

    Actually, I implemented many devices to USBC without problem.  The only problem is that I asked.

    From the example of "usbd_hid_composite". It looks some response using NRF_DRV_USBD_EPOUT0 is needed to get status change.

    I've tried "nrf_drv_usbd_ep_transfer(NRF_DRV_USBD_EPOUT0 , transfer)" in usbd_setup_SetConfig. but in vain.

    static ret_code_t hid_kbd_on_set_report(app_usbd_class_inst_t const * p_inst,
                                            app_usbd_setup_evt_t const  * p_setup_ev)
    {
        app_usbd_hid_kbd_t const * p_kbd = hid_kbd_get(p_inst);
    
        /*Request setup data*/
        app_usbd_hid_report_buffer_t const * p_rep_buff;
    
        p_rep_buff = app_usbd_hid_rep_buff_out_get(&p_kbd->specific.inst.hid_inst);
    
        p_rep_buff->p_buff[0] = 0;
        NRF_DRV_USBD_TRANSFER_OUT(transfer, p_rep_buff->p_buff + 1, p_rep_buff->size - 1);
    
        ret_code_t ret;
        CRITICAL_REGION_ENTER();
        ret = app_usbd_ep_transfer(NRF_DRV_USBD_EPOUT0, &transfer);
        if (ret == NRF_SUCCESS)
        {
            app_usbd_core_setup_data_handler_desc_t desc = {
                .handler   = hid_kbd_on_set_report_data_cb,
                .p_context = (app_usbd_hid_kbd_t *)p_kbd
            };
    
            ret = app_usbd_core_setup_data_handler_set(NRF_DRV_USBD_EPOUT0, &desc);
        }
        CRITICAL_REGION_EXIT();
    
        return ret;
    }

    I will check "usbd_hid_composite" also according to your suggestion.

    But It would be good if the problem is solved in UBBD example.

    Best Regards,

    Steven

  • Hi Steven

    Please check out Dmitry's replies in this case, where it is explained how the HID specification treats various output reports. Please also check out this thread for more information on how to handle endpoint data transfers over USBD.

    Best regards,

    Simon

Related