|
- /*-
- * Copyright 2022 John-Mark Gurney.
- *
- * 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 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. If you are STMicroelectronics N.V., one of it's subsidiaries, a
- * subsidiary of an owner of STMicroelectronics N.V., or an employee,
- * contractor, or agent of any of the preceeding entities, you are not
- * allowed to use this code, in either source or binary forms.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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 "usb_hid_def.h"
-
- #include <misc.h> /* debug_printf */
-
- #include <stdbool.h>
-
- #include <usb_hid_base.h>
-
- static uint8_t Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
- static uint8_t DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
- static uint8_t Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
- static uint8_t DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
- #if 0
- static uint8_t DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
- #endif
- static uint8_t ep0rxready(USBD_HandleTypeDef *pdev);
-
- #define USB_HID_EPIN_ADDR 0x81
-
- #define REPORT_CNT 10
-
- enum hid_state {
- IDLE,
- BUSY,
- };
-
- struct usb_hid_softc {
- enum hid_state state;
- uint8_t led_status[2];
- uint8_t idle_time;
- uint8_t report_protocol;
- };
-
- static USBD_HandleTypeDef *hid_handle;
- static uint32_t next_idle_report;
-
- static uint8_t reports[REPORT_CNT][REPORT_SIZE] = {
- /* mod pad keys... */
- #if 0
- [0] = { 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [1] = { 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [2] = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [3] = { 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [4] = { 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [5] = { 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [6] = { 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [7] = { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- [9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
- #endif
- };
- /*
- * Ring buffer for reports.
- *
- * reporthead: next available report
- * reporttail: next empty slot
- *
- * reporthead == reporttail: ring buffer is empty
- * reporttail == reporthead - 1: ring buffer is full
- */
- static uint8_t reporthead = 0;
- static uint8_t reporttail = 0;
- static uint32_t reportoverflow;
-
- /* as -1 % posint == -1, adding REPORT_CNT keeps it positive if x is val - 1 when val is 0 */
- #define MAKE_POS(x) (((x) + REPORT_CNT) % REPORT_CNT)
-
- static bool
- report_is_avail(void)
- {
- return reporthead != reporttail;
- }
-
- static uint8_t *
- report_next(void)
- {
- int oldrep;
-
- /* no new report, don't advance, return previous */
- if (!report_is_avail())
- return reports[MAKE_POS(reporthead - 1)];
-
- oldrep = reporthead;
- reporthead = MAKE_POS(reporthead + 1);
-
- return reports[oldrep];
- }
-
- void
- report_process(void)
- {
- struct usb_hid_softc *hid;
- uint32_t tick;
-
- if (hid_handle == NULL)
- return;
-
- hid = (struct usb_hid_softc *)hid_handle->pClassData;
-
- if (hid_handle->dev_state != USBD_STATE_CONFIGURED ||
- hid->state != IDLE) {
- return;
- }
-
- tick = HAL_GetTick();
-
- if (!report_is_avail() && tick < next_idle_report)
- return;
-
- next_idle_report = tick + hid->idle_time;
-
- hid->state = BUSY;
- USBD_LL_Transmit(hid_handle, USB_HID_EPIN_ADDR, report_next(), REPORT_SIZE);
- }
-
- /*
- * This could be smarter by collapsing reports. The algorithm to do
- * so is a bit tricky, and likely not needed, BUT it does mean for a
- * simple algorithm, you could be limited to 50 chars per second assuming
- * 10ms report timing (one down, one up).
- *
- * Return true if successful, false if overflowed.
- */
- int
- report_insert(uint8_t rep[REPORT_SIZE])
- {
- uint8_t newtail;
-
- newtail = MAKE_POS(reporttail + 1);
- if (newtail == reporthead) {
- reportoverflow++;
- return 0;
- }
-
- memcpy(reports[reporttail], rep, sizeof reports[reporttail]);
-
- reporttail = newtail;
-
- return 1;
- }
-
- enum {
- HID_REQ_GET_REPORT = 0x01,
- HID_REQ_GET_IDLE = 0x02,
- HID_REQ_GET_PROTOCOL = 0x03,
- HID_REQ_SET_REPORT = 0x09,
- HID_REQ_SET_IDLE = 0x0a,
- HID_REQ_SET_PROTOCOL = 0x0b,
- };
-
- USBD_ClassTypeDef usb_hid_def = {
- .Init = Init,
- .DeInit = DeInit,
- .Setup = Setup,
- .EP0_RxReady = ep0rxready,
- .DataIn = DataIn,
- #if 0
- .DataOut = DataOut,
- #endif
- /* Get*Desc not used */
- };
-
- static uint8_t
- Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
- {
- struct usb_hid_softc *hid;
- int ret;
-
- ret = 0;
-
- usb_hid_epopen(pdev);
-
- /* allocate state data */
- hid = (void *)USBD_malloc(sizeof(struct usb_hid_softc));
- if (hid == NULL) {
- ret = 1;
- } else {
- *hid = (struct usb_hid_softc){
- .idle_time = 10,
- };
-
- pdev->pClassData = hid;
-
- hid_handle = pdev;
- }
-
- return ret;
- }
-
- static uint8_t
- DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
- {
-
- usb_hid_epclose(pdev);
-
- if (pdev->pClassData != NULL) {
- USBD_free(pdev->pClassData);
- pdev->pClassData = NULL;
- hid_handle = NULL;
- }
-
- return 0;
- }
-
- #define MAKE_CASE_REQ(type, req) ((((type) << 8) & USB_REQ_TYPE_MASK) | (req))
- #define USB_CASE_REQ(req) MAKE_CASE_REQ((req)->bmRequest, (req)->bRequest)
-
- static void
- send_ctlresp(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req, const uint8_t *buf, size_t len)
- {
-
- len = MIN(req->wLength, len);
-
- USBD_CtlSendData(pdev, (uint8_t *)(uintptr_t)buf, len);
- }
-
- /*
- * Handle non-standard control SETUP requests.
- *
- * This includes requests sent to the interface and other special
- * requests.
- */
- static uint8_t
- Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req)
- {
- struct usb_hid_softc *hid;
- uint8_t ret;
-
- ret = USBD_OK;
- hid = (struct usb_hid_softc *)pdev->pClassData;
-
- switch (USB_CASE_REQ(req)) {
- case MAKE_CASE_REQ(USB_REQ_TYPE_STANDARD, USB_REQ_GET_DESCRIPTOR):
- USBD_GetDescriptor(pdev, req);
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_REPORT):
- send_ctlresp(pdev, req, report_next(), REPORT_SIZE);
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_REPORT):
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_IDLE):
- send_ctlresp(pdev, req, &hid->idle_time, sizeof hid->idle_time);
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_IDLE):
- hid->idle_time = (uint8_t)(req->wValue >> 8);
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_GET_PROTOCOL):
- send_ctlresp(pdev, req, &hid->report_protocol, sizeof hid->idle_time);
- break;
-
- case MAKE_CASE_REQ(USB_REQ_TYPE_CLASS, HID_REQ_SET_PROTOCOL):
- hid->report_protocol = !!req->wValue;
- break;
-
- default:
- USBD_CtlError(pdev, req);
- ret = USBD_FAIL;
- break;
- }
-
- return ret;
- }
-
- static uint8_t
- DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
- {
- struct usb_hid_softc *hid;
-
- hid = (struct usb_hid_softc *)pdev->pClassData;
-
- /* Previously queued data was sent */
- hid->state = IDLE;
-
- return USBD_OK;
- }
-
- #if 0
- static uint8_t
- DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
- {
-
- /* received led status */
-
- return USBD_OK;
- }
- #endif
-
- static uint8_t
- ep0rxready(USBD_HandleTypeDef *pdev)
- {
-
- return USBD_OK;
- }
|