Manage HID output report in nrf connect sdk

Hello,

I am currently working on a USB dongle communicating in hid to my pc, but i can't find how to implemente output reports. 

I started developping based on the hid-cdc example. I activated HID_ENDPOINT_OUT in the kconfig menu and tried to add a callback for output reports, like the one implemented for when

interrupt IN transfer has been completed : 
But I never reach the function. 
With this example when I press button 3 I see the CAPSLOCK LED activating on other kayboards but I don't see my "output_report_rcv" LOG. 
I also tried to add a callback with the function  usb_dc_ep_set_callback, on the endpoint which seems to be the one use for output report, but pressing CAPSLOCK button on any keyboard don't call my function.
I wonder if you have any advice on how to implemente output reports. My purpose being to know if the capslock  is ON on my computer before sending any in report.
Thank you for your help.
Thibaud
Parents
  • Hi,

     

    You want to declare the .set_report member of the hid_ops structure, as shown here:

    https://github.com/nrfconnect/sdk-nrf/blob/v2.3.0/applications/nrf_desktop/src/modules/usb_state.c#L182

     

    Please note that it requires small modifications. Here's the samples/subsys/usb/hid tailored exactly for this usecase:

    ncs_2_3_0_samples_subsys_usb_hid_setreport.zip 

     

    This in the prj.conf is important:

    CONFIG_USB_HID_REPORTS=2

     

    Kind regards,

    Håkon

  • Hello, 

    Thank you for your answer. 

    I've tried with set_reports and it worked. 

    Best regards, 

    Thibaud

  • Great to hear. Hope you have a wonderful day!

     

    Cheers,

    Håkon

  • (context nrfconnect, nrf52840)

    Hello Ha(^o)kon, :-)

    I am also making a USB HID, with 2 OUT reports in my descriptor. 

    I use the .int_out_ready

    interrupt with some success to get my out report from a PC.

    (CONFIG_USB_HID_REPORTS not set)

    however,with a 

    hid_int_ep_read(hdev,data_from_pc,64,&ret_bytes);

    my USB frames get sliced in 4 chucks of 16 bytes (4 .int_out_ready interrupts). and I always get 4 chunks of 16 bytes, regardless of the size of my reports sent by the PC. All the empty bytes contain 205. 

    My descriptor has already been used in another product with no problem.

    I have tried what you describe, but I don't even get the 

    .set_report interrupt.
    when I send the out report from the PC

    which is worse in a sense.


    I come here because doing what's described here does not help me.

    And the CONFIG_USB_HID_REPORTS=2 does not make any sense for me.

    I have no problem with the IN reports.

    Here is the current state of my code (descriptor and in report code  not shown ).


    static void int_in_ready_cb(const struct device *dev)
    {
        ARG_UNUSED(dev);
        if (!atomic_test_and_clear_bit(hid_ep_busy, HID_EPIN_BUSY_FLAG)) {
            LOG_WRN("IN endpoint callback without preceding buffer write");
        }
    }

    uint8_t data_from_pc[65];

    static void int_out_ready_cb(const struct device *dev)
    {
        ARG_UNUSED(dev);
        uint32_t ret_bytes;
        hid_int_ep_read(hdev,data_from_pc,64,&ret_bytes);
        if (ret_bytes>2)
            processReceivedUSBMessage(data_from_pc);
    }


    int set_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len, uint8_t **data)
    {
        uint8_t request_value[2];
        sys_put_le16(setup->wValue, request_value);
        switch (request_value[1]) {
            case 0x02: // REPORT_TYPE_FEATURE
    //          LOG_INF("Output report ID: %x", request_value[0]);
                processReceivedUSBMessage(*data);

                break;
            default:
                LOG_INF("Req: %x", request_value[1]);
                break;
        }
        return 0;
    }

    static const struct hid_ops ops =
    {
        .int_in_ready = int_in_ready_cb,
    //  .on_idle = on_idle_cb,
    //  .protocol_change = protocol_cb,
    //  .int_out_ready = int_out_ready_cb,
        .set_report = set_report_cb
    };

    static void status_cb(enum usb_dc_status_code status, const uint8_t *param)
    {
        switch (status) {
        case USB_DC_RESET:
        case USB_DC_DISCONNECTED:
            configured = false;
            break;
        case USB_DC_CONFIGURED:
            if (!configured) {
                int_in_ready_cb(hdev);
                configured = true;
            }
            break;
        case USB_DC_SOF:
            break;
        default:
            LOG_DBG("status %u unhandled", status);
            break;
        }
    }

    void usbEnable(void)
    {
        int ret = usb_enable(status_cb);
        if (ret != 0)
        {
            LOG_ERR("Failed to enable USB");
            return;
        }
    }



    static int composite_pre_init(const struct device *dev)
    {
        hdev = device_get_binding("HID_0");
        if (hdev == NULL) {
            LOG_ERR("Cannot get USB HID Device");
            return -ENODEV;
        }

        LOG_INF("HID Device: dev %p", hdev);

        usb_hid_register_device(hdev, hid_report_desc, sizeof(hid_report_desc), &ops);

        atomic_set_bit(hid_ep_busy, HID_EPIN_BUSY_FLAG);

        if (usb_hid_set_proto_code(hdev, HID_BOOT_IFACE_CODE_NONE))
        {
            LOG_WRN("Failed to set Protocol Code");
        }

        return usb_hid_init(hdev);
    }

    SYS_INIT(composite_pre_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);



    ###########################################
    # Configs for USB
    CONFIG_USB_DEVICE_STACK=y
    CONFIG_USB_DEVICE_HID=y
    CONFIG_USB_DEVICE_PRODUCT="xxxx"
    #CONFIG_USB_HID_BOOT_PROTOCOL=y
    CONFIG_USB_DEVICE_VID=0xxxxx
    CONFIG_USB_DEVICE_MANUFACTURER="xxxx"
    CONFIG_USB_DEVICE_PID=0xxxx

    CONFIG_LOG=y
    CONFIG_USB_DRIVER_LOG_LEVEL_ERR=y

    CONFIG_USB_DEVICE_SOF=n
    CONFIG_USB_HID_REPORTS=2
    CONFIG_ENABLE_HID_INT_OUT_EP=y

Reply
  • (context nrfconnect, nrf52840)

    Hello Ha(^o)kon, :-)

    I am also making a USB HID, with 2 OUT reports in my descriptor. 

    I use the .int_out_ready

    interrupt with some success to get my out report from a PC.

    (CONFIG_USB_HID_REPORTS not set)

    however,with a 

    hid_int_ep_read(hdev,data_from_pc,64,&ret_bytes);

    my USB frames get sliced in 4 chucks of 16 bytes (4 .int_out_ready interrupts). and I always get 4 chunks of 16 bytes, regardless of the size of my reports sent by the PC. All the empty bytes contain 205. 

    My descriptor has already been used in another product with no problem.

    I have tried what you describe, but I don't even get the 

    .set_report interrupt.
    when I send the out report from the PC

    which is worse in a sense.


    I come here because doing what's described here does not help me.

    And the CONFIG_USB_HID_REPORTS=2 does not make any sense for me.

    I have no problem with the IN reports.

    Here is the current state of my code (descriptor and in report code  not shown ).


    static void int_in_ready_cb(const struct device *dev)
    {
        ARG_UNUSED(dev);
        if (!atomic_test_and_clear_bit(hid_ep_busy, HID_EPIN_BUSY_FLAG)) {
            LOG_WRN("IN endpoint callback without preceding buffer write");
        }
    }

    uint8_t data_from_pc[65];

    static void int_out_ready_cb(const struct device *dev)
    {
        ARG_UNUSED(dev);
        uint32_t ret_bytes;
        hid_int_ep_read(hdev,data_from_pc,64,&ret_bytes);
        if (ret_bytes>2)
            processReceivedUSBMessage(data_from_pc);
    }


    int set_report_cb(const struct device *dev, struct usb_setup_packet *setup, int32_t *len, uint8_t **data)
    {
        uint8_t request_value[2];
        sys_put_le16(setup->wValue, request_value);
        switch (request_value[1]) {
            case 0x02: // REPORT_TYPE_FEATURE
    //          LOG_INF("Output report ID: %x", request_value[0]);
                processReceivedUSBMessage(*data);

                break;
            default:
                LOG_INF("Req: %x", request_value[1]);
                break;
        }
        return 0;
    }

    static const struct hid_ops ops =
    {
        .int_in_ready = int_in_ready_cb,
    //  .on_idle = on_idle_cb,
    //  .protocol_change = protocol_cb,
    //  .int_out_ready = int_out_ready_cb,
        .set_report = set_report_cb
    };

    static void status_cb(enum usb_dc_status_code status, const uint8_t *param)
    {
        switch (status) {
        case USB_DC_RESET:
        case USB_DC_DISCONNECTED:
            configured = false;
            break;
        case USB_DC_CONFIGURED:
            if (!configured) {
                int_in_ready_cb(hdev);
                configured = true;
            }
            break;
        case USB_DC_SOF:
            break;
        default:
            LOG_DBG("status %u unhandled", status);
            break;
        }
    }

    void usbEnable(void)
    {
        int ret = usb_enable(status_cb);
        if (ret != 0)
        {
            LOG_ERR("Failed to enable USB");
            return;
        }
    }



    static int composite_pre_init(const struct device *dev)
    {
        hdev = device_get_binding("HID_0");
        if (hdev == NULL) {
            LOG_ERR("Cannot get USB HID Device");
            return -ENODEV;
        }

        LOG_INF("HID Device: dev %p", hdev);

        usb_hid_register_device(hdev, hid_report_desc, sizeof(hid_report_desc), &ops);

        atomic_set_bit(hid_ep_busy, HID_EPIN_BUSY_FLAG);

        if (usb_hid_set_proto_code(hdev, HID_BOOT_IFACE_CODE_NONE))
        {
            LOG_WRN("Failed to set Protocol Code");
        }

        return usb_hid_init(hdev);
    }

    SYS_INIT(composite_pre_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);



    ###########################################
    # Configs for USB
    CONFIG_USB_DEVICE_STACK=y
    CONFIG_USB_DEVICE_HID=y
    CONFIG_USB_DEVICE_PRODUCT="xxxx"
    #CONFIG_USB_HID_BOOT_PROTOCOL=y
    CONFIG_USB_DEVICE_VID=0xxxxx
    CONFIG_USB_DEVICE_MANUFACTURER="xxxx"
    CONFIG_USB_DEVICE_PID=0xxxx

    CONFIG_LOG=y
    CONFIG_USB_DRIVER_LOG_LEVEL_ERR=y

    CONFIG_USB_DEVICE_SOF=n
    CONFIG_USB_HID_REPORTS=2
    CONFIG_ENABLE_HID_INT_OUT_EP=y

Children
Related