1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/usb/usbus/hid/hid.c
2021-01-11 00:24:30 +01:00

230 lines
7.1 KiB
C

/*
* Copyright (C) 2020 Nils Ollrogge
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup usbus_hid
* @{
* @file
*
* @author Nils Ollrogge <nils-ollrogge@outlook.de>
* @}
*/
#define USB_H_USER_IS_RIOT_INTERNAL
#include <string.h>
#include "usb/usbus.h"
#include "usb/usbus/control.h"
#include "usb/usbus/hid.h"
#include "tsrb.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
static void _init(usbus_t *usbus, usbus_handler_t *handler);
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_event_usb_t event);
static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_control_request_state_t state,
usb_setup_t *setup);
static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event);
static void _handle_tx_ready(event_t *ev);
static const usbus_handler_driver_t hid_driver = {
.init = _init,
.event_handler = _event_handler,
.control_handler = _control_handler,
.transfer_handler = _transfer_handler
};
static size_t _gen_hid_descriptor(usbus_t *usbus, void *arg);
static const usbus_descr_gen_funcs_t _hid_descriptor = {
.fmt_post_descriptor = _gen_hid_descriptor,
.len = {
.fixed_len = sizeof(usb_desc_hid_t)
},
.len_type = USBUS_DESCR_LEN_FIXED
};
static size_t _gen_hid_descriptor(usbus_t *usbus, void *arg)
{
usbus_hid_device_t *hid_dev = arg;
usb_desc_hid_t hid_desc;
hid_desc.length = sizeof(usb_desc_hid_t);
hid_desc.desc_type = USB_HID_DESCR_HID;
hid_desc.bcd_hid = USB_HID_VERSION_BCD;
hid_desc.country_code = USB_HID_COUNTRY_CODE_NOTSUPPORTED;
hid_desc.num_descrs = 0x01;
hid_desc.report_type = USB_HID_DESCR_REPORT;
hid_desc.report_length = hid_dev->report_desc_size;
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&hid_desc,
sizeof(hid_desc));
return sizeof(usb_desc_hid_t);
}
static void _handle_tx_ready(event_t *ev)
{
usbus_hid_device_t *hid = container_of(ev, usbus_hid_device_t, tx_ready);
usbdev_ep_ready(hid->ep_in->ep, hid->occupied);
}
void usbus_hid_init(usbus_t *usbus, usbus_hid_device_t *hid, usbus_hid_cb_t cb,
const uint8_t *report_desc, size_t report_desc_size)
{
memset(hid, 0, sizeof(usbus_hid_device_t));
hid->usbus = usbus;
mutex_init(&hid->in_lock);
hid->handler_ctrl.driver = &hid_driver;
hid->report_desc = report_desc;
hid->report_desc_size = report_desc_size;
hid->cb = cb;
DEBUG("hid_init: %d %d \n", report_desc_size, report_desc[0]);
usbus_register_event_handler(usbus, &hid->handler_ctrl);
}
static void _init(usbus_t *usbus, usbus_handler_t *handler)
{
DEBUG("USB_HID: initialization\n");
usbus_hid_device_t *hid = (usbus_hid_device_t *)handler;
hid->tx_ready.handler = _handle_tx_ready;
hid->hid_descr.next = NULL;
hid->hid_descr.funcs = &_hid_descriptor;
hid->hid_descr.arg = hid;
/*
Configure Interface as USB_HID interface, choosing NONE for subclass and
protocol in order to represent a generic I/O device
*/
hid->iface.class = USB_CLASS_HID;
hid->iface.subclass = USB_HID_SUBCLASS_NONE;
hid->iface.protocol = USB_HID_PROTOCOL_NONE;
hid->iface.descr_gen = &hid->hid_descr;
hid->iface.handler = handler;
/* IN endpoint to send data to host */
hid->ep_in = usbus_add_endpoint(usbus, &hid->iface,
USB_EP_TYPE_INTERRUPT,
USB_EP_DIR_IN,
CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
/* interrupt endpoint polling rate in ms */
hid->ep_in->interval = 0x05;
usbus_enable_endpoint(hid->ep_in);
/* OUT endpoint to receive data from host */
hid->ep_out = usbus_add_endpoint(usbus, &hid->iface,
USB_EP_TYPE_INTERRUPT, USB_EP_DIR_OUT,
CONFIG_USBUS_HID_INTERRUPT_EP_SIZE);
/* interrupt endpoint polling rate in ms */
hid->ep_out->interval = 0x05;
usbus_enable_endpoint(hid->ep_out);
/* signal that INTERRUPT OUT is ready to receive data */
usbdev_ep_ready(hid->ep_out->ep, 0);
usbus_add_interface(usbus, &hid->iface);
}
static void _event_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_event_usb_t event)
{
(void)usbus;
(void)handler;
switch (event) {
default:
DEBUG("USB HID unhandled event: 0x%x\n", event);
break;
}
}
static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
usbus_control_request_state_t state,
usb_setup_t *setup)
{
usbus_hid_device_t *hid = (usbus_hid_device_t *)handler;
DEBUG("USB_HID: request: %d type: %d value: %d length: %d state: %d \n",
setup->request, setup->type, setup->value >> 8, setup->length, state);
/* Requests defined in USB HID 1.11 spec section 7 */
switch (setup->request) {
case USB_SETUP_REQ_GET_DESCRIPTOR: {
uint8_t desc_type = setup->value >> 8;
if (desc_type == USB_HID_DESCR_REPORT) {
usbus_control_slicer_put_bytes(usbus, hid->report_desc,
hid->report_desc_size);
}
else if (desc_type == USB_HID_DESCR_HID) {
_gen_hid_descriptor(usbus, NULL);
}
break;
}
case USB_HID_REQUEST_GET_REPORT:
break;
case USB_HID_REQUEST_GET_IDLE:
break;
case USB_HID_REQUEST_GET_PROTOCOL:
break;
case USB_HID_REQUEST_SET_REPORT:
if ((state == USBUS_CONTROL_REQUEST_STATE_OUTDATA)) {
size_t size = 0;
uint8_t *data = usbus_control_get_out_data(usbus, &size);
if (size > 0) {
hid->cb(hid, data, size);
}
}
break;
case USB_HID_REQUEST_SET_IDLE:
break;
case USB_HID_REQUEST_SET_PROTOCOL:
break;
default:
DEBUG("USB_HID: unknown request %d \n", setup->request);
return -1;
}
return 1;
}
static void _transfer_handler(usbus_t *usbus, usbus_handler_t *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event)
{
(void)usbus;
(void)event;
DEBUG("USB_HID: transfer_handler\n");
usbus_hid_device_t *hid = (usbus_hid_device_t *)handler;
if ((ep->dir == USB_EP_DIR_IN) && (ep->type == USB_EP_TYPE_INTERRUPT)) {
mutex_unlock(&hid->in_lock);
hid->occupied = 0;
}
else if ((ep->dir == USB_EP_DIR_OUT) &&
(ep->type == USB_EP_TYPE_INTERRUPT)) {
size_t len;
usbdev_ep_get(ep, USBOPT_EP_AVAILABLE, &len, sizeof(size_t));
if (len > 0) {
hid->cb(hid, ep->buf, len);
}
usbdev_ep_ready(ep, 0);
}
}