usb: hid: implement idle rate
Idle rate functionality has been implemented for HID USB class. Bassed on Device Class Definition for Human Interface Devices 1.11. Tested with USB3CV and host with idle rate. Signed-off-by: Marcin Szymczyk <Marcin.Szymczyk@nordicsemi.no>
This commit is contained in:
parent
6a4ddffaf9
commit
d5b79ff42c
5 changed files with 169 additions and 37 deletions
|
@ -2,6 +2,7 @@
|
|||
* Human Interface Device (HID) USB class core header
|
||||
*
|
||||
* Copyright (c) 2018 Intel Corporation
|
||||
* Copyright (c) 2018 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -59,6 +60,7 @@ struct usb_hid_descriptor {
|
|||
typedef int (*hid_cb_t)(struct usb_setup_packet *setup, s32_t *len,
|
||||
u8_t **data);
|
||||
typedef void (*hid_int_ready_callback)(void);
|
||||
typedef void (*hid_idle_cb_t)(u16_t report_id);
|
||||
|
||||
struct hid_ops {
|
||||
hid_cb_t get_report;
|
||||
|
@ -67,6 +69,7 @@ struct hid_ops {
|
|||
hid_cb_t set_report;
|
||||
hid_cb_t set_idle;
|
||||
hid_cb_t set_protocol;
|
||||
hid_idle_cb_t on_idle;
|
||||
/*
|
||||
* int_in_ready is an optional callback that is called when
|
||||
* the current interrupt IN transfer has completed. This can
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* Copyright(c) 2015,2016 Intel Corporation.
|
||||
* Copyright(c) 2017 PHYTEC Messtechnik GmbH
|
||||
* Copyright(c) 2018 Nordic Semiconductor ASA
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -86,6 +87,9 @@
|
|||
#define MAX_LOW_POWER 0x32
|
||||
#define MAX_HIGH_POWER 0xFA
|
||||
|
||||
/* Highest value of Frame Number in SOF packets. */
|
||||
#define USB_SOF_MAX 2047
|
||||
|
||||
/* bmAttributes:
|
||||
* D7:Reserved, always 1,
|
||||
* D6:Self-Powered -> 1,
|
||||
|
|
|
@ -89,9 +89,20 @@ static void status_cb(enum usb_dc_status_code status, const u8_t *param)
|
|||
}
|
||||
}
|
||||
|
||||
static void idle_cb(u16_t report_id)
|
||||
{
|
||||
static u8_t report_1[2] = { 0x00, 0xEB };
|
||||
int ret, wrote;
|
||||
|
||||
ret = hid_int_ep_write(report_1, sizeof(report_1), &wrote);
|
||||
|
||||
LOG_DBG("Idle callback: wrote %d bytes with ret %d", wrote, ret);
|
||||
}
|
||||
|
||||
static const struct hid_ops ops = {
|
||||
.int_in_ready = in_ready_cb,
|
||||
.status_cb = status_cb,
|
||||
.on_idle = idle_cb,
|
||||
};
|
||||
|
||||
void main(void)
|
||||
|
|
|
@ -37,4 +37,12 @@ config USB_HID_POLL_INTERVAL_MS
|
|||
help
|
||||
Polling interval in ms selected by the USB HID Device.
|
||||
|
||||
config USB_HID_REPORTS
|
||||
int "HID reports in the instance"
|
||||
default 1
|
||||
range 1 256
|
||||
help
|
||||
Number of HID reports in the instance.
|
||||
Must be equal or higher than highest report ID (if they are not consecutive).
|
||||
|
||||
endif # USB_DEVICE_HID
|
||||
|
|
|
@ -18,6 +18,8 @@ LOG_MODULE_REGISTER(usb_hid);
|
|||
#include <usb_descriptor.h>
|
||||
#include <class/usb_hid.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define HID_INT_IN_EP_ADDR 0x81
|
||||
#define HID_INT_OUT_EP_ADDR 0x01
|
||||
|
||||
|
@ -86,20 +88,38 @@ USBD_CLASS_DESCR_DEFINE(primary) struct usb_hid_config hid_cfg = {
|
|||
static struct hid_device_info {
|
||||
const u8_t *report_desc;
|
||||
size_t report_size;
|
||||
|
||||
const struct hid_ops *ops;
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
u32_t sof_cnt[CONFIG_USB_HID_REPORTS + 1];
|
||||
bool idle_on;
|
||||
bool idle_id_report;
|
||||
u8_t idle_rate[CONFIG_USB_HID_REPORTS + 1];
|
||||
#endif
|
||||
} hid_device;
|
||||
|
||||
static int hid_on_get_idle(struct usb_setup_packet *setup, s32_t *len,
|
||||
u8_t **data)
|
||||
{
|
||||
LOG_DBG("Get Idle callback");
|
||||
|
||||
/* TODO: Do something. */
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
u8_t report_id = sys_le16_to_cpu(setup->wValue) & 0xFF;
|
||||
|
||||
if (report_id > CONFIG_USB_HID_REPORTS) {
|
||||
LOG_ERR("Report id out of limit: %d", report_id);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
u32_t size = sizeof(hid_device.idle_rate[report_id]);
|
||||
|
||||
LOG_DBG("Get Idle callback, report_id: %d", report_id);
|
||||
|
||||
*data = &hid_device.idle_rate[report_id];
|
||||
len = &size;
|
||||
return 0;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int hid_on_get_report(struct usb_setup_packet *setup, s32_t *len,
|
||||
u8_t **data)
|
||||
{
|
||||
|
@ -107,7 +127,7 @@ static int hid_on_get_report(struct usb_setup_packet *setup, s32_t *len,
|
|||
|
||||
/* TODO: Do something. */
|
||||
|
||||
return 0;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int hid_on_get_protocol(struct usb_setup_packet *setup, s32_t *len,
|
||||
|
@ -123,11 +143,53 @@ static int hid_on_get_protocol(struct usb_setup_packet *setup, s32_t *len,
|
|||
static int hid_on_set_idle(struct usb_setup_packet *setup, s32_t *len,
|
||||
u8_t **data)
|
||||
{
|
||||
LOG_DBG("Set Idle callback");
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
u8_t rate = ((sys_le16_to_cpu(setup->wValue) & 0xFF00) >> 8);
|
||||
u8_t report_id = sys_le16_to_cpu(setup->wValue) & 0xFF;
|
||||
|
||||
/* TODO: Do something. */
|
||||
if (report_id > CONFIG_USB_HID_REPORTS) {
|
||||
LOG_ERR("Report id out of limit: %d", report_id);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
LOG_DBG("Set Idle callback, rate: %d, report_id: %d", rate, report_id);
|
||||
|
||||
hid_device.idle_rate[report_id] = rate;
|
||||
|
||||
if (rate == 0) {
|
||||
/* Clear idle */
|
||||
bool clear = true;
|
||||
|
||||
for (u16_t i = 1; i <= CONFIG_USB_HID_REPORTS; i++) {
|
||||
if (hid_device.idle_rate[i] != 0) {
|
||||
/* Report with non-zero id has idle rate. */
|
||||
clear = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (clear) {
|
||||
hid_device.idle_id_report = false;
|
||||
LOG_DBG("Non-zero report idle rate OFF.");
|
||||
|
||||
if (hid_device.idle_rate[0] == 0) {
|
||||
hid_device.idle_on = false;
|
||||
LOG_DBG("Idle rate OFF.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Set idle */
|
||||
hid_device.idle_on = true;
|
||||
LOG_DBG("Idle rate ON.");
|
||||
if (report_id != 0) {
|
||||
/* Report with non-zero id has idle rate set now. */
|
||||
hid_device.idle_id_report = true;
|
||||
LOG_DBG("Non-zero report idle rate ON.");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
return -ENOTSUP;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int hid_on_set_report(struct usb_setup_packet *setup, s32_t *len,
|
||||
|
@ -156,17 +218,53 @@ static void usb_set_hid_report_size(u16_t size)
|
|||
(u8_t *)&(hid_cfg.if0_hid.subdesc[0].wDescriptorLength));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
void hid_clear_idle_ctx(void)
|
||||
{
|
||||
hid_device.idle_on = false;
|
||||
hid_device.idle_id_report = false;
|
||||
for (u16_t i = 0; i <= CONFIG_USB_HID_REPORTS; i++) {
|
||||
hid_device.sof_cnt[i] = 0;
|
||||
hid_device.idle_rate[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_sof_handler(void)
|
||||
{
|
||||
for (u16_t i = 0; i <= CONFIG_USB_HID_REPORTS; i++) {
|
||||
if (hid_device.idle_rate[i]) {
|
||||
hid_device.sof_cnt[i]++;
|
||||
}
|
||||
|
||||
u32_t diff = abs((hid_device.idle_rate[i] * 4)
|
||||
- hid_device.sof_cnt[i]);
|
||||
|
||||
if (diff < (2 + (hid_device.idle_rate[i] / 10))) {
|
||||
hid_device.sof_cnt[i] = 0;
|
||||
hid_device.ops->on_idle(i);
|
||||
}
|
||||
|
||||
if (!hid_device.idle_id_report) {
|
||||
/* Only report with 0 id has idle rate.
|
||||
* No need to check the whole array.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hid_status_cb(enum usb_dc_status_code status, const u8_t *param)
|
||||
{
|
||||
if (hid_device.ops->status_cb) {
|
||||
hid_device.ops->status_cb(status, param);
|
||||
} else {
|
||||
switch (status) {
|
||||
case USB_DC_ERROR:
|
||||
LOG_DBG("USB device error");
|
||||
break;
|
||||
case USB_DC_RESET:
|
||||
LOG_DBG("USB device reset detected");
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
hid_clear_idle_ctx();
|
||||
#endif
|
||||
break;
|
||||
case USB_DC_CONNECTED:
|
||||
LOG_DBG("USB device connected");
|
||||
|
@ -184,12 +282,20 @@ static void hid_status_cb(enum usb_dc_status_code status, const u8_t *param)
|
|||
LOG_DBG("USB device resumed");
|
||||
break;
|
||||
case USB_DC_SOF:
|
||||
#ifdef CONFIG_USB_DEVICE_SOF
|
||||
if (hid_device.idle_on) {
|
||||
hid_sof_handler();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case USB_DC_UNKNOWN:
|
||||
default:
|
||||
LOG_DBG("USB unknown state");
|
||||
break;
|
||||
}
|
||||
|
||||
if (hid_device.ops->status_cb) {
|
||||
hid_device.ops->status_cb(status, param);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue