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

Parents
  • Hi,

     

    When you get the event 

    APP_USBD_HID_USER_EVT_OUT_REPORT_READY
    in your usb handler, you can call function 
    app_usbd_hid_generic_out_report_get
    to get the data.

     

    Kind regards,

    Håkon

  • Hi Håkon,

    Thank you for your help! I managed to control LED by the method you said but here comes a new problem: after I change the LED light once or more times, the mouse function does not work anymore. It seems the mouse interrupt is not working afterwards.

    Here is the code I modified:

    /**
     * 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, 0x01,       /*     LOGICAL_MAXIMUM (1)             */     \
    //    0x75, 0x01,       /*     REPORT_SIZE (1)                 */     \
    //    0x95, 0x03,       /*     REPORT_COUNT (3)                */     \
    //    0x91, 0x02,       /*     OUTPUT (Data,Var,Abs)           */     \
    //    0x75, 0x05,       /*     Report Size (5),                */     \
    //    0x95, 0x01,       /*     Report Count (1),               */     \
    //    0x91, 0x01,       /*     OUTPUT (Constant),              */     \
    //    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 size;
    uint8_t *report;
    uint8_t out_endpoint;
    uint8_t report_0;
    uint8_t report_1;
    uint8_t report_2;
    uint8_t report_3;
    unsigned int t = 0;
    unsigned int h = 0;
    unsigned int s = 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.*/
    						
    						report = (uint8_t *) app_usbd_hid_generic_out_report_get(&m_app_hid_generic, &size);
    						report_0 = report[0];
    						report_1 = report[1];
    						report_2 = report[2];
    						report_3 = report[3];
    						
    						if(report_0 == '1'){
    								bsp_board_led_invert(LED_TEST);
    								t++;
    						}
    						if(report_1 == '1'){
    								bsp_board_led_invert(LED_HID_REP_IN);
    								h++;
    						}
    						if(report_2 == '1'){
    								bsp_board_led_invert(LED_USB_START);
    								s++;
    						}
    						if(report_3 == '1'){
    								if(t%2 == 1) bsp_board_led_invert(LED_TEST);
    								if(h%2 == 1) bsp_board_led_invert(LED_HID_REP_IN);
    								if(s%2 == 1) bsp_board_led_invert(LED_USB_START);
    								t = 0;
    								h = 0;
    								s = 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();
        }
    }
    

    I also changed "i" to "j" in app_usbd_hid_generic.caccording to https://devzone.nordicsemi.com/f/nordic-q-a/36892/nrf52840-adding-out-endpoint-to-usbd_hid_generic-example-in-sdk-15/145594#145594, and REPORT_OUT_MAXSIZE from 0 to 63 according to https://devzone.nordicsemi.com/f/nordic-q-a/36145/nrf52840-usbd_hid_generic-endpoint-out-error.

    Besides, I am not sure whether to use the descriptor including the out endpoint or not, because the code seems to work well without that descriptor including the out endpoint. (Is the out endpoint stored in report[0] - report[3] by default?) And I'm not sure which GLOBAL_DEF should I use.

    //#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, 0x01,       /*     LOGICAL_MAXIMUM (1)             */     \
    //    0x75, 0x01,       /*     REPORT_SIZE (1)                 */     \
    //    0x95, 0x03,       /*     REPORT_COUNT (3)                */     \
    //    0x91, 0x02,       /*     OUTPUT (Data,Var,Abs)           */     \
    //    0x75, 0x05,       /*     Report Size (5),                */     \
    //    0x95, 0x01,       /*     Report Count (1),               */     \
    //    0x91, 0x01,       /*     OUTPUT (Constant),              */     \
    //    0xC0,         /*  End Collection,                        */     \
    //    0xC0,         /* End Collection                          */     \
    //}
    
    //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);

    Again, thank you for your kind support and I look forward to hearing back.

    Kind regards,

    Louis

  • 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

  • Haojz said:
    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.

     Have you checked that the data you receive is correct, and as well the "out_report_size" is 4?

    The LED for the IN report is inverted on the callback (ie: when the host has ACKed), so it might be a problem in the USB communication after the OUT packet is received. Hard to say, as I do not have all the debug information here (usb sniffer trace, checks on validity of data, etc). 

     

    Kind regards,

    Håkon

  • Hello Håkon,

    Have you checked that the data you receive is correct, and as well the "out_report_size" is 4?

    I have checked in hid_generic_mouse_process_state function that the in_report is correctly set and this function works fine. The "out_report_size" is 4 as default. 

    so it might be a problem in the USB communication after the OUT packet is received

    Yes, I think so, but I am not able to find exactly where. The good news is I can still get the data from IN EP from linux side, and it is only the mouse and LED that do not work well.

    Therefore, I change my goal now: I want to remove all things regarding the mouse functions, only leaving the communication tunnels(IN EP & CONTROL EP/OUT EP) to transmit some data and execute some control commands from linux side. I have tried removing mouse-regarding functions manually but the code stopped to work afterwards(it is quite complicated to modify), so do you have clear code only for HID USB communication via endpoints or do you know any easier way to realize this goal? 

    Thank you for your kind support again and I look forward to hearing from you soon.

    Kind regards,

    Louis

     

  • Hi Louis,

     

    Sorry for the late reply.

    Haojz said:
    Therefore, I change my goal now: I want to remove all things regarding the mouse functions, only leaving the communication tunnels(IN EP & CONTROL EP/OUT EP) to transmit some data and execute some control commands from linux side. I have tried removing mouse-regarding functions manually but the code stopped to work afterwards(it is quite complicated to modify), so do you have clear code only for HID USB communication via endpoints or do you know any easier way to realize this goal? 

    Do you need a generic data interface, ie: non keyboard/mouse, to receive and send data (HID not really a requirement)? If yes, then the USB CDC example can be beneficial to look at. Using that you should be able to send and receive data using a COM port interface.

    If you require to use HID, then a generic HID descriptor can be used, but it will require some changes to the descriptor. Here's a generic HID descriptor taken from one of the older nRFready Desktop dongle projects (nRF24LU1+ based):

    0x06, 0x0a, 0xff,              // USAGE_PAGE (Generic Desktop)
    0x09, 0x01,                    // USAGE (Vendor Usage 1)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x02,                    //   USAGE (Vendor Usage 2)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x06, 0x00, 0xff,              //     USAGE_PAGE (Generic Desktop)
    0x09, 0x03,                    //     USAGE (Vendor Usage 1)
    0x09, 0x04,                    //     USAGE (Vendor Usage 1)
    0x15, 0x80,                    //     LOGICAL_MINIMUM (-128)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x35, 0x00,                    //     PHYSICAL_MINIMUM (0)
    0x46, 0xff, 0x00,              //     PHYSICAL_MAXIMUM (255)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x20,                    //     REPORT_COUNT (32)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x09, 0x05,                    //     USAGE (Vendor Usage 1)
    0x09, 0x06,                    //     USAGE (Vendor Usage 1)
    0x15, 0x80,                    //     LOGICAL_MINIMUM (-128)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x35, 0x00,                    //     PHYSICAL_MINIMUM (0)
    0x46, 0xff, 0x00,              //     PHYSICAL_MAXIMUM (255)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x20,                    //     REPORT_COUNT (32)
    0x91, 0x02,                    //     OUTPUT (Data,Var,Abs)
    0xc0,                          //   END_COLLECTION
    0xc0                           // END_COLLECTION

      

    Kind regards,

    Håkon

  • Dear Håkon,

    Thank you for your reply. I think you are right and I am trying CDC example, but how do I use CDC example in Linux? The infocenter only has tutorial for Windows. (And what commands do I use to send and receive data? In order to achieve the goal above, do I need to change anything in the SDK?)

    Kind regards,

    Louis

Reply Children
Related