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

[nRF52840][usbd_hid_generic] How to read the data in OUT ENDPOINT?

Hello Nordic experts,

I am using nRF52840 and SDK 15.3.0. I have added an OUT ENDPOINT to the usbd_hid_generic example by following the suggestions in https://devzone.nordicsemi.com/f/nordic-q-a/36145/nrf52840-usbd_hid_generic-endpoint-out-error. I can see the OUT ENDPOINT in USB description so it was successfully added. Also, I have a tool to send data to that OUT ENDPOINT. Now, I would like to ask how do I program to read that data stored in OUT ENDPOINT using Keil? (I will use that data to control the LEDs in nRF52840)

#define HID_GENERIC_EPOUT NRF_DRV_USBD_EPOUT1

// .....

#define ENDPOINT_LIST()                                      \
(                                                            \
        HID_GENERIC_EPIN,                                    \
		HID_GENERIC_EPOUT                                    \
)

// .....

Thank you for your kind support in advance.

Kind regards,

Louis

  • Hi Håkon,

    Your descriptor sets the output as 1 byte, with 3 bits of data and 5 bits constant (ie: not used).

    This is what I want. I want to use those 3 bits to control those 3 LED lights. However, I still got 4 bytes from report by using this descriptor. What is even more weird is that after I commented this DIY descriptor, I can still get 4 bytes from report. So why using my own descriptor makes no difference compared to using the default mouse descriptor? And how do I fix the "mouse stopping working bug"?

  • The USB host is driving the communication, ie: sending data on the OUT to the usb device. If the USB device expects 1 byte (restricted by the usb descriptor), you should send one byte from the PC side, not 4. LibUSB seems to allow you to send 4 bytes, even though your descriptor does not allow it.

  • OK, it makes sense. How about the "mouse stopping working bug"? I found this is a general problem not matter what I put in APP_USBD_HID_USER_EVT_OUT_REPORT_READY(I tried putting nothing). After I send some data to OUT EP(0x01), the buttons which should act like mouse do not work any more, and the LED_HID_REP_IN led is always on.

  • Sounds like the firmware is crashing afterwards. Have you tried debugging to see what happens in the firmware flow?

    Try only sending one byte (actually; only the three first bits), as the descriptor allows, and see if the mouse is affected then as well.

     

    Kind regards,

    Håkon

  • Hi Håkon,

    Try only sending one byte (actually; only the three first bits), as the descriptor allows, and see if the mouse is affected then as well.
    If you expect to receive 4 bytes, try setting the "report size" to 8, and "report count" to 4.

    I have changed my descriptor to be 4x8 bits as you said earlier and I send 4 bytes using libusb so I guess this is fine.

    Here is my new descriptor and full code:

    #define USBD_GENERIC_REPORT_DESCRIPTOR(bcnt) {                      \
        0x05, 0x01,       /* Usage Page (Generic Desktop),       */     \
        0x09, 0x02,       /* Usage (Mouse),                      */     \
        0xA1, 0x01,       /*  Collection (Application),          */     \
        0x09, 0x01,       /*   Usage (Pointer),                  */     \
        0xA1, 0x00,       /*  Collection (Physical),             */     \
        0x05, 0x09,       /*     Usage Page (Buttons),           */     \
        0x19, 0x01,       /*     Usage Minimum (01),             */     \
        0x29, bcnt,       /*     Usage Maximum (bcnt),           */     \
        0x15, 0x00,       /*     Logical Minimum (0),            */     \
        0x25, 0x01,       /*     Logical Maximum (1),            */     \
        0x75, 0x01,       /*     Report Size (1),                */     \
        0x95, bcnt,       /*     Report Count (bcnt),            */     \
        0x81, 0x02,       /*     Input (Data, Variable, Absolute)*/     \
        0x75, (8-(bcnt)), /*     Report Size (8-(bcnt)),         */     \
        0x95, 0x01,       /*     Report Count (1),               */     \
        0x81, 0x01,       /*     Input (Constant),               */     \
        0x05, 0x01,       /*     Usage Page (Generic Desktop),   */     \
        0x09, 0x30,       /*     Usage (X),                      */     \
        0x09, 0x31,       /*     Usage (Y),                      */     \
        0x09, 0x38,       /*     Usage (Scroll),                 */     \
        0x15, 0x81,       /*     Logical Minimum (-127),         */     \
        0x25, 0x7F,       /*     Logical Maximum (127),          */     \
        0x75, 0x08,       /*     Report Size (8),                */     \
        0x95, 0x03,       /*     Report Count (3),               */     \
        0x81, 0x06,       /*     Input (Data, Variable, Relative)*/     \
        0x09, 0x21,       /*     USAGE(Output Report Data)       */     \
        0x15, 0x00,       /*     LOGICAL_MINIMUM (0)             */     \
        0x25, 0x7F,       /*     LOGICAL_MAXIMUM (127)           */     \
        0x75, 0x08,       /*     REPORT_SIZE (8)                 */     \
        0x95, 0x04,       /*     REPORT_COUNT (4)                */     \
        0x91, 0x02,       /*     OUTPUT (Data,Var,Abs)           */     \
        0xC0,         /*  End Collection,                        */     \
        0xC0,         /* End Collection                          */     \
    }

    /**
     * Copyright (c) 2017 - 2019, 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 "app_util_platform.h"
    #include "nrf_drv_usbd.h"
    #include "nrf_drv_clock.h"
    #include "nrf_gpio.h"
    #include "nrf_drv_power.h"
    
    #include "app_timer.h"
    #include "app_usbd.h"
    #include "app_usbd_core.h"
    #include "app_usbd_hid_generic.h"
    #include "app_usbd_hid_mouse.h"
    #include "app_usbd_hid_kbd.h"
    #include "app_error.h"
    #include "bsp.h"
    
    #include "bsp_cli.h"
    #include "nrf_cli.h"
    #include "nrf_cli_uart.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.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);
    
    /**
     * @brief Enable USB power detection
     */
    #ifndef USBD_POWER_DETECTION
    #define USBD_POWER_DETECTION true
    #endif
    
    /**
     * @brief HID generic class interface number.
     * */
    #define HID_GENERIC_INTERFACE  0
    
    /**
     * @brief HID generic class endpoint number.
     * */
    #define HID_GENERIC_EPIN       NRF_DRV_USBD_EPIN1
    #define HID_GENERIC_EPOUT NRF_DRV_USBD_EPOUT1
    
    /**
     * @brief Mouse speed (value sent via HID when board button is pressed).
     * */
    #define CONFIG_MOUSE_MOVE_SPEED (3)
    
    /**
     * @brief Mouse move repeat time in milliseconds
     */
    #define CONFIG_MOUSE_MOVE_TIME_MS (5)
    
    
    /* GPIO used as LED & buttons in this example */
    #define LED_USB_START    (BSP_BOARD_LED_0)
    #define LED_TEST   (BSP_BOARD_LED_1) // self added for test purpose
    #define LED_HID_REP_IN   (BSP_BOARD_LED_2)
    
    #define BTN_MOUSE_X_POS  2//0
    #define BTN_MOUSE_Y_POS  3//1
    #define BTN_MOUSE_LEFT   0//2
    #define BTN_MOUSE_RIGHT  1//3
    
    /**
     * @brief Left button mask in buttons report
     */
    #define HID_BTN_LEFT_MASK  (1U << 0)
    
    /**
     * @brief Right button mask in buttons report
     */
    #define HID_BTN_RIGHT_MASK (1U << 1)
    
    /* HID report layout */
    #define HID_BTN_IDX   0 /**< Button bit mask position */
    #define HID_X_IDX     1 /**< X offset position */
    #define HID_Y_IDX     2 /**< Y offset position */
    #define HID_W_IDX     3 /**< Wheel position  */
    #define HID_REP_SIZE  4 /**< The size of the report */
    
    /**
     * @brief Number of reports defined in report descriptor.
     */
    #define REPORT_IN_QUEUE_SIZE    1
    
    /**
     * @brief Size of maximum output report. HID generic class will reserve
     *        this buffer size + 1 memory space. 
     *
     * Maximum value of this define is 63 bytes. Library automatically adds
     * one byte for report ID. This means that output report size is limited
     * to 64 bytes.
     */
    #define REPORT_OUT_MAXSIZE  63//0
    
    /**
     * @brief HID generic class endpoints count.
     * */
    #define HID_GENERIC_EP_COUNT  2//1
    
    /**
     * @brief List of HID generic class endpoints.
     * */
    #define ENDPOINT_LIST()                                      \
    (                                                            \
            HID_GENERIC_EPIN,                                    \
    				HID_GENERIC_EPOUT                                    \
    )
    
    #define USBD_GENERIC_REPORT_DESCRIPTOR(bcnt) {                      \
        0x05, 0x01,       /* Usage Page (Generic Desktop),       */     \
        0x09, 0x02,       /* Usage (Mouse),                      */     \
        0xA1, 0x01,       /*  Collection (Application),          */     \
        0x09, 0x01,       /*   Usage (Pointer),                  */     \
        0xA1, 0x00,       /*  Collection (Physical),             */     \
        0x05, 0x09,       /*     Usage Page (Buttons),           */     \
        0x19, 0x01,       /*     Usage Minimum (01),             */     \
        0x29, bcnt,       /*     Usage Maximum (bcnt),           */     \
        0x15, 0x00,       /*     Logical Minimum (0),            */     \
        0x25, 0x01,       /*     Logical Maximum (1),            */     \
        0x75, 0x01,       /*     Report Size (1),                */     \
        0x95, bcnt,       /*     Report Count (bcnt),            */     \
        0x81, 0x02,       /*     Input (Data, Variable, Absolute)*/     \
        0x75, (8-(bcnt)), /*     Report Size (8-(bcnt)),         */     \
        0x95, 0x01,       /*     Report Count (1),               */     \
        0x81, 0x01,       /*     Input (Constant),               */     \
        0x05, 0x01,       /*     Usage Page (Generic Desktop),   */     \
        0x09, 0x30,       /*     Usage (X),                      */     \
        0x09, 0x31,       /*     Usage (Y),                      */     \
        0x09, 0x38,       /*     Usage (Scroll),                 */     \
        0x15, 0x81,       /*     Logical Minimum (-127),         */     \
        0x25, 0x7F,       /*     Logical Maximum (127),          */     \
        0x75, 0x08,       /*     Report Size (8),                */     \
        0x95, 0x03,       /*     Report Count (3),               */     \
        0x81, 0x06,       /*     Input (Data, Variable, Relative)*/     \
        0x09, 0x21,       /*     USAGE(Output Report Data)       */     \
        0x15, 0x00,       /*     LOGICAL_MINIMUM (0)             */     \
        0x25, 0x7F,       /*     LOGICAL_MAXIMUM (127)           */     \
        0x75, 0x08,       /*     REPORT_SIZE (8)                 */     \
        0x95, 0x04,       /*     REPORT_COUNT (4)                */     \
        0x91, 0x02,       /*     OUTPUT (Data,Var,Abs)           */     \
        0xC0,         /*  End Collection,                        */     \
        0xC0,         /* End Collection                          */     \
    }
    
    /**
     * @brief Additional key release events
     *
     * This example needs to process release events of used buttons
     */
    enum {
        BSP_USER_EVENT_RELEASE_0 = BSP_EVENT_KEY_LAST + 1, /**< Button 0 released */
        BSP_USER_EVENT_RELEASE_1,                          /**< Button 1 released */
        BSP_USER_EVENT_RELEASE_2,                          /**< Button 2 released */
        BSP_USER_EVENT_RELEASE_3,                          /**< Button 3 released */
        BSP_USER_EVENT_RELEASE_4,                          /**< Button 4 released */
        BSP_USER_EVENT_RELEASE_5,                          /**< Button 5 released */
        BSP_USER_EVENT_RELEASE_6,                          /**< Button 6 released */
        BSP_USER_EVENT_RELEASE_7,                          /**< Button 7 released */
    };
    
    /**
     * @brief HID generic mouse action types
     */
    typedef enum {
        HID_GENERIC_MOUSE_X,
        HID_GENERIC_MOUSE_Y,
        HID_GENERIC_MOUSE_BTN_LEFT,
        HID_GENERIC_MOUSE_BTN_RIGHT,
    } hid_generic_mouse_action_t;
    
    /**
     * @brief User event handler.
     * */
    static void hid_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_hid_user_event_t event);
    
    /**
     * @brief Reuse HID mouse report descriptor for HID generic class
     */
    //APP_USBD_HID_GENERIC_SUBCLASS_REPORT_DESC(mouse_desc,APP_USBD_HID_MOUSE_REPORT_DSC_BUTTON(2));
    APP_USBD_HID_GENERIC_SUBCLASS_REPORT_DESC(mouse_desc,USBD_GENERIC_REPORT_DESCRIPTOR(2));
    
    static const app_usbd_hid_subclass_desc_t * reps[] = {&mouse_desc};
    
    /*lint -save -e26 -e64 -e123 -e505 -e651*/
    
    /**
     * @brief Global HID generic instance
     */
    //APP_USBD_HID_GENERIC_GLOBAL_DEF(m_app_hid_generic,
    //                                HID_GENERIC_INTERFACE,
    //                                hid_user_ev_handler,
    //                                ENDPOINT_LIST(),
    //                                reps,
    //                                REPORT_IN_QUEUE_SIZE,
    //                                REPORT_OUT_MAXSIZE,
    //                                APP_USBD_HID_SUBCLASS_BOOT,
    //                                APP_USBD_HID_PROTO_MOUSE);
    
    APP_USBD_HID_GENERIC_GLOBAL_DEF(m_app_hid_generic,
    																HID_GENERIC_INTERFACE,
    																hid_user_ev_handler,
    																ENDPOINT_LIST(),
    																reps,
    																REPORT_IN_QUEUE_SIZE,
    																REPORT_OUT_MAXSIZE,
    																APP_USBD_HID_SUBCLASS_NONE, 
    																APP_USBD_HID_PROTO_GENERIC);
    
    /*lint -restore*/
    
    
    /**
     * @brief Mouse state
     *
     * Current mouse status
     */
    struct
    {
        int16_t acc_x;    /**< Accumulated x state */
        int16_t acc_y;    /**< Accumulated y state */
        uint8_t btn;      /**< Current btn state */
        uint8_t last_btn; /**< Last transfered button state */
    }m_mouse_state;
    
    /**
     * @brief Mark the ongoing transmission
     *
     * Marks that the report buffer is busy and cannot be used until transmission finishes
     * or invalidates (by USB reset or suspend event).
     */
    static bool m_report_pending;
    
    /**
     * @brief Timer to repeat mouse move
     */
    APP_TIMER_DEF(m_mouse_move_timer);
    
    /**
     * @brief Get maximal allowed accumulated value
     *
     * Function gets maximal value from the accumulated input.
     * @sa m_mouse_state::acc_x, m_mouse_state::acc_y
     */
    static int8_t hid_acc_for_report_get(int16_t acc)
    {
        if(acc > INT8_MAX)
        {
            return INT8_MAX;
        }
        else if(acc < INT8_MIN)
        {
            return INT8_MIN;
        }
        else
        {
            return (int8_t)(acc);
        }
    }
    
    /**
     * @brief Internal function that process mouse state
     *
     * This function checks current mouse state and tries to send
     * new report if required.
     * If report sending was successful it clears accumulated positions
     * and mark last button state that was transfered.
     */
    static void hid_generic_mouse_process_state(void)
    {
        if (m_report_pending)
            return;
        if ((m_mouse_state.acc_x != 0) ||
            (m_mouse_state.acc_y != 0) ||
            (m_mouse_state.btn != m_mouse_state.last_btn))
        {
            ret_code_t ret;
            static uint8_t report[HID_REP_SIZE];
            /* We have some status changed that we need to transfer */
            report[HID_BTN_IDX] = m_mouse_state.btn;
            report[HID_X_IDX]   = (uint8_t)hid_acc_for_report_get(m_mouse_state.acc_x);
            report[HID_Y_IDX]   = (uint8_t)hid_acc_for_report_get(m_mouse_state.acc_y);
            /* Start the transfer */
            ret = app_usbd_hid_generic_in_report_set(
                &m_app_hid_generic,
                report,
                sizeof(report));
            if (ret == NRF_SUCCESS)
            {
                m_report_pending = true;
                m_mouse_state.last_btn = report[HID_BTN_IDX];
                CRITICAL_REGION_ENTER();
                /* This part of the code can fail if interrupted by BSP keys processing.
                 * Lock interrupts to be safe */
                m_mouse_state.acc_x   -= (int8_t)report[HID_X_IDX];
                m_mouse_state.acc_y   -= (int8_t)report[HID_Y_IDX];
                CRITICAL_REGION_EXIT();
            }
        }
    }
    
    /**
     * @brief HID generic IN report send handling
     * */
    static void hid_generic_mouse_action(hid_generic_mouse_action_t action, int8_t param)
    {
        CRITICAL_REGION_ENTER();
        /*
         * Update mouse state
         */
        switch (action)
        {
            case HID_GENERIC_MOUSE_X:
                m_mouse_state.acc_x += param;
                break;
            case HID_GENERIC_MOUSE_Y:
                m_mouse_state.acc_y += param;
                break;
            case HID_GENERIC_MOUSE_BTN_RIGHT:
                if(param == 1)
                {
                    m_mouse_state.btn |= HID_BTN_RIGHT_MASK;
                }
                else
                {
                    m_mouse_state.btn &= ~HID_BTN_RIGHT_MASK;
                }
                break;
            case HID_GENERIC_MOUSE_BTN_LEFT:
                if(param == 1)
                {
                    m_mouse_state.btn |= HID_BTN_LEFT_MASK;
                }
                else
                {
                    m_mouse_state.btn &= ~HID_BTN_LEFT_MASK;
                }
                break;
        }
        CRITICAL_REGION_EXIT();
    }
    
    /**
     * @brief Class specific event handler.
     *
     * @param p_inst    Class instance.
     * @param event     Class specific event.
     * */
    
    size_t out_report_size;
    uint8_t *out_report;
    uint8_t out_report_0;
    uint8_t out_report_1;
    uint8_t out_report_2;
    uint8_t out_report_3;
    unsigned int test_led_count = 0;
    unsigned int hid_led_count = 0;
    unsigned int start_led_count = 0;
    
    static void hid_user_ev_handler(app_usbd_class_inst_t const * p_inst,
                                    app_usbd_hid_user_event_t event)
    {
        switch (event)
        {
            case APP_USBD_HID_USER_EVT_OUT_REPORT_READY:
            {
                /* No output report defined for this example.*/
    						
    						out_report = (uint8_t *) app_usbd_hid_generic_out_report_get(&m_app_hid_generic, &out_report_size);
    						out_report_0 = out_report[0];
    						out_report_1 = out_report[1];
    						out_report_2 = out_report[2];
    						out_report_3 = out_report[3];
    						
    						if(out_report_0 == '1'){
    								bsp_board_led_invert(LED_TEST);
    								test_led_count++;
    						}
    						if(out_report_1 == '1'){
    								bsp_board_led_invert(LED_HID_REP_IN);
    								hid_led_count++;
    						}
    						if(out_report_2 == '1'){
    								bsp_board_led_invert(LED_USB_START);
    								start_led_count++;
    						}
    						if(out_report_3 == '1'){
    								if(test_led_count%2 == 1) bsp_board_led_invert(LED_TEST);
    								if(hid_led_count%2 == 1) bsp_board_led_invert(LED_HID_REP_IN);
    								if(start_led_count%2 == 1) bsp_board_led_invert(LED_USB_START);
    								test_led_count = 0;
    								hid_led_count = 0;
    								start_led_count = 0;
    						}
    						
                //ASSERT(0);
                break;
            }
            case APP_USBD_HID_USER_EVT_IN_REPORT_DONE:
            {
                m_report_pending = false;
                hid_generic_mouse_process_state();
                bsp_board_led_invert(LED_HID_REP_IN);
                break;
            }
            case APP_USBD_HID_USER_EVT_SET_BOOT_PROTO:
            {
                UNUSED_RETURN_VALUE(hid_generic_clear_buffer(p_inst));
                NRF_LOG_INFO("SET_BOOT_PROTO");
                break;
            }
            case APP_USBD_HID_USER_EVT_SET_REPORT_PROTO:
            {
                UNUSED_RETURN_VALUE(hid_generic_clear_buffer(p_inst));
                NRF_LOG_INFO("SET_REPORT_PROTO");
                break;
            }
            default:
                break;
        }
    }
    
    /**
     * @brief USBD library specific event handler.
     *
     * @param event     USBD library event.
     * */
    static void usbd_user_ev_handler(app_usbd_event_type_t event)
    {
        switch (event)
        {
            case APP_USBD_EVT_DRV_SOF:
                break;
            case APP_USBD_EVT_DRV_RESET:
                m_report_pending = false;
                break;
            case APP_USBD_EVT_DRV_SUSPEND:
                m_report_pending = false;
                app_usbd_suspend_req(); // Allow the library to put the peripheral into sleep mode
                bsp_board_leds_off();
                break;
            case APP_USBD_EVT_DRV_RESUME:
                m_report_pending = false;
                bsp_board_led_on(LED_USB_START);
                break;
            case APP_USBD_EVT_STARTED:
                m_report_pending = false;
                bsp_board_led_on(LED_USB_START);
                break;
            case APP_USBD_EVT_STOPPED:
                app_usbd_disable();
                bsp_board_leds_off();
                break;
            case APP_USBD_EVT_POWER_DETECTED:
                NRF_LOG_INFO("USB power detected");
                if (!nrf_drv_usbd_is_enabled())
                {
                    app_usbd_enable();
                }
                break;
            case APP_USBD_EVT_POWER_REMOVED:
                NRF_LOG_INFO("USB power removed");
                app_usbd_stop();
                break;
            case APP_USBD_EVT_POWER_READY:
                NRF_LOG_INFO("USB ready");
                app_usbd_start();
                break;
            default:
                break;
        }
    }
    
    static void mouse_move_timer_handler(void * p_context)
    {
        UNUSED_PARAMETER(p_context);
        bool used = false;
    
        if (bsp_button_is_pressed(BTN_MOUSE_X_POS))
        {
            hid_generic_mouse_action(HID_GENERIC_MOUSE_X, CONFIG_MOUSE_MOVE_SPEED);
            used = true;
        }
        if (bsp_button_is_pressed(BTN_MOUSE_Y_POS))
        {
            hid_generic_mouse_action(HID_GENERIC_MOUSE_Y, CONFIG_MOUSE_MOVE_SPEED);
            used = true;
        }
    
        if(!used)
        {
            UNUSED_RETURN_VALUE(app_timer_stop(m_mouse_move_timer));
        }
    }
    
    static void bsp_event_callback(bsp_event_t ev)
    {
        switch ((unsigned int)ev)
        {
            case CONCAT_2(BSP_EVENT_KEY_, BTN_MOUSE_X_POS):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_X, CONFIG_MOUSE_MOVE_SPEED);
                UNUSED_RETURN_VALUE(app_timer_start(m_mouse_move_timer, APP_TIMER_TICKS(CONFIG_MOUSE_MOVE_TIME_MS), NULL));
                break;
    
            case CONCAT_2(BSP_EVENT_KEY_, BTN_MOUSE_Y_POS):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_Y, CONFIG_MOUSE_MOVE_SPEED);
                UNUSED_RETURN_VALUE(app_timer_start(m_mouse_move_timer, APP_TIMER_TICKS(CONFIG_MOUSE_MOVE_TIME_MS), NULL));
                break;
    
            case CONCAT_2(BSP_EVENT_KEY_, BTN_MOUSE_RIGHT):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_BTN_RIGHT, 1);
                break;
            case CONCAT_2(BSP_USER_EVENT_RELEASE_, BTN_MOUSE_RIGHT):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_BTN_RIGHT, -1);
                break;
    
            case CONCAT_2(BSP_EVENT_KEY_, BTN_MOUSE_LEFT):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_BTN_LEFT, 1);
                break;
            case CONCAT_2(BSP_USER_EVENT_RELEASE_, BTN_MOUSE_LEFT):
                hid_generic_mouse_action(HID_GENERIC_MOUSE_BTN_LEFT, -1);
                break;
    
            default:
                return; // no implementation needed
        }
    }
    
    
    /**
     * @brief Auxiliary internal macro
     *
     * Macro used only in @ref init_bsp to simplify the configuration
     */
    #define INIT_BSP_ASSIGN_RELEASE_ACTION(btn)                      \
        APP_ERROR_CHECK(                                             \
            bsp_event_to_button_action_assign(                       \
                btn,                                                 \
                BSP_BUTTON_ACTION_RELEASE,                           \
                (bsp_event_t)CONCAT_2(BSP_USER_EVENT_RELEASE_, btn)) \
        )
    
    static void init_bsp(void)
    {
        ret_code_t ret;
        ret = bsp_init(BSP_INIT_BUTTONS, bsp_event_callback);
        APP_ERROR_CHECK(ret);
    
        INIT_BSP_ASSIGN_RELEASE_ACTION(BTN_MOUSE_LEFT );
        INIT_BSP_ASSIGN_RELEASE_ACTION(BTN_MOUSE_RIGHT);
    
        /* Configure LEDs */
        bsp_board_init(BSP_INIT_LEDS);
    }
    
    static void init_cli(void)
    {
        ret_code_t ret;
        ret = bsp_cli_init(bsp_event_callback);
        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 ret_code_t idle_handle(app_usbd_class_inst_t const * p_inst, uint8_t report_id)
    {
        switch (report_id)
        {
            case 0:
            {
                uint8_t report[] = {0xBE, 0xEF};
                return app_usbd_hid_generic_idle_report_set(
                  &m_app_hid_generic,
                  report,
                  sizeof(report));
            }
            default:
                return NRF_ERROR_NOT_SUPPORTED;
        }
        
    }
    
    int main(void)
    {
        ret_code_t ret;
        static const app_usbd_config_t usbd_config = {
            .ev_state_proc = usbd_user_ev_handler
        };
    
        ret = NRF_LOG_INIT(NULL);
        APP_ERROR_CHECK(ret);
    
        ret = nrf_drv_clock_init();
        APP_ERROR_CHECK(ret);
    
        nrf_drv_clock_lfclk_request(NULL);
    
        while(!nrf_drv_clock_lfclk_is_running())
        {
            /* Just waiting */
        }
    
        ret = app_timer_init();
        APP_ERROR_CHECK(ret);
    
        ret = app_timer_create(&m_mouse_move_timer, APP_TIMER_MODE_REPEATED, mouse_move_timer_handler);
        APP_ERROR_CHECK(ret);
    
        init_bsp();
        init_cli();
        NRF_LOG_INFO("Hello USB!");
    
        ret = app_usbd_init(&usbd_config);
        APP_ERROR_CHECK(ret);
    
        NRF_LOG_INFO("USBD HID generic example started.");
    
        app_usbd_class_inst_t const * class_inst_generic;
        class_inst_generic = app_usbd_hid_generic_class_inst_get(&m_app_hid_generic);
    
        ret = hid_generic_idle_handler_set(class_inst_generic, idle_handle);
        APP_ERROR_CHECK(ret);
    
        ret = app_usbd_class_append(class_inst_generic);
        APP_ERROR_CHECK(ret);
    
        if (USBD_POWER_DETECTION)
        {
            ret = app_usbd_power_events_enable();
            APP_ERROR_CHECK(ret);
        }
        else
        {
            NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
    
            app_usbd_enable();
            app_usbd_start();
        }
    
        while (true)
        {
            while (app_usbd_event_queue_process())
            {
                /* Nothing to do */
            }
            hid_generic_mouse_process_state();
            nrf_cli_process(&m_cli_uart);
    
            UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
            /* Sleep CPU only if there was no interrupt since last loop processing */
            __WFE();
        }
    }
    

    What I found in Keil debug mode is that each time when we press the button, APP_USBD_HID_USER_EVT_IN_REPORT_DONE function is entered twice(hold & release). After data is sent to OUT EP(0x01), APP_USBD_HID_USER_EVT_IN_REPORT_DONE function is only entered once when we press the button(meanwhile the mouse function is not activated), keeping LED lighting up and not turning off. If I go deeper into the sub-function codes, I cannot understand what is going on.

    Kind regards,

    Louis

Related