diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index 2d1f6084f..3308f0744 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -787,6 +787,11 @@ STATIC mp_obj_t pyb_usb_hid_recv(size_t n_args, const mp_obj_t *args, mp_map_t * // receive the data int ret = usbd_hid_rx(&self->usb_dev->usbd_hid_itf, vstr.len, (uint8_t*)vstr.buf, vals[1].u_int); + if (ret < 0) { + // error, just return 0/empty bytes + ret = 0; + } + // return the received data if (o_ret != MP_OBJ_NULL) { return mp_obj_new_int(ret); // number of bytes read into given buffer diff --git a/ports/stm32/usbd_hid_interface.c b/ports/stm32/usbd_hid_interface.c index 033d83ea6..386cf34d7 100644 --- a/ports/stm32/usbd_hid_interface.c +++ b/ports/stm32/usbd_hid_interface.c @@ -1,113 +1,70 @@ /* * This file is part of the MicroPython project, http://micropython.org/ * - * Taken from ST Cube library and heavily modified. See below for original - * copyright header. + * The MIT License (MIT) + * + * Copyright (c) 2019 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. */ -/** - ****************************************************************************** - * @file USB_Device/CDC_Standalone/Src/usbd_cdc_interface.c - * @author MCD Application Team - * @version V1.0.1 - * @date 26-February-2014 - * @brief Source file for USBD CDC interface - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2014 STMicroelectronics

- * - * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.st.com/software_license_agreement_liberty_v2 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ****************************************************************************** - */ - -/* Includes ------------------------------------------------------------------*/ - -#include - #include "usbd_hid_interface.h" -#include "py/obj.h" -#include "irq.h" +#include "py/mperrno.h" +#include "py/mphal.h" #include "usb.h" #if MICROPY_HW_USB_HID uint8_t *usbd_hid_init(usbd_hid_state_t *hid_in) { usbd_hid_itf_t *hid = (usbd_hid_itf_t*)hid_in; - - hid->current_read_buffer = 0; - hid->last_read_len = 0; - hid->current_write_buffer = 0; - - // Return the buffer to place the first USB OUT packet - return hid->buffer[hid->current_write_buffer]; + hid->report_in_len = USBD_HID_REPORT_INVALID; + return &hid->report_in_buf[0]; // location to place first incoming report } -// Data received over USB OUT endpoint is processed here. -// len: number of bytes received into the buffer we passed to USBD_HID_ReceivePacket -// Returns USBD_OK if all operations are OK else USBD_FAIL int8_t usbd_hid_receive(usbd_hid_state_t *hid_in, size_t len) { + // Incoming report: save the length but don't schedule next report until user reads this one usbd_hid_itf_t *hid = (usbd_hid_itf_t*)hid_in; - - hid->current_write_buffer = !hid->current_write_buffer; - hid->last_read_len = len; - // initiate next USB packet transfer, to append to existing data in buffer - USBD_HID_ReceivePacket(&hid->base, hid->buffer[hid->current_write_buffer]); - // Set NAK to indicate we need to process read buffer - USBD_HID_SetNAK(&hid->base); + hid->report_in_len = len; return USBD_OK; } -// Returns number of ready rx buffers. -int usbd_hid_rx_num(usbd_hid_itf_t *hid) { - return hid->current_read_buffer != hid->current_write_buffer; -} - -// timout in milliseconds. -// Returns number of bytes read from the device. -int usbd_hid_rx(usbd_hid_itf_t *hid, size_t len, uint8_t *buf, uint32_t timeout) { - // Wait until we have buffer to read - uint32_t start = HAL_GetTick(); - while (hid->current_read_buffer == hid->current_write_buffer) { - // Wraparound of tick is taken care of by 2's complement arithmetic. - if (HAL_GetTick() - start >= timeout) { - // timeout - return 0; +int usbd_hid_rx(usbd_hid_itf_t *hid, size_t len, uint8_t *buf, uint32_t timeout_ms) { + // Wait for an incoming report + uint32_t t0 = mp_hal_ticks_ms(); + while (hid->report_in_len == USBD_HID_REPORT_INVALID) { + if (mp_hal_ticks_ms() - t0 >= timeout_ms || query_irq() == IRQ_STATE_DISABLED) { + return -MP_ETIMEDOUT; } - if (query_irq() == IRQ_STATE_DISABLED) { - // IRQs disabled so buffer will never be filled; return immediately - return 0; - } - __WFI(); // enter sleep mode, waiting for interrupt + MICROPY_EVENT_POLL_HOOK } - // There is not enough space in buffer - if (len < hid->last_read_len) { - return 0; - } + // Copy bytes from report to user buffer + int n = MIN(len, hid->report_in_len); + memcpy(buf, &hid->report_in_buf[0], n); - // Copy bytes from device to user buffer - int read_len = hid->last_read_len; - memcpy(buf, hid->buffer[hid->current_read_buffer], read_len); - hid->current_read_buffer = !hid->current_read_buffer; + // Schedule to receive next incoming report + hid->report_in_len = USBD_HID_REPORT_INVALID; + USBD_HID_ReceivePacket(&hid->base, &hid->report_in_buf[0]); - // Clear NAK to indicate we are ready to read more data - USBD_HID_ClearNAK(&hid->base); - - // Success, return number of bytes read - return read_len; + // Return number of bytes read into user buffer + return n; } #endif // MICROPY_HW_USB_HID diff --git a/ports/stm32/usbd_hid_interface.h b/ports/stm32/usbd_hid_interface.h index 777fa9340..5d2c9ad87 100644 --- a/ports/stm32/usbd_hid_interface.h +++ b/ports/stm32/usbd_hid_interface.h @@ -4,18 +4,22 @@ #ifndef MICROPY_INCLUDED_STM32_USBD_HID_INTERFACE_H #define MICROPY_INCLUDED_STM32_USBD_HID_INTERFACE_H +#include #include "usbd_cdc_msc_hid.h" +#define USBD_HID_REPORT_INVALID ((size_t)-1) + typedef struct _usbd_hid_itf_t { usbd_hid_state_t base; // state for the base HID layer - uint8_t buffer[2][HID_DATA_FS_MAX_PACKET_SIZE]; // pair of buffers to read individual packets into - int8_t current_read_buffer; // which buffer to read from - uint32_t last_read_len; // length of last read - int8_t current_write_buffer; // which buffer to write to + volatile size_t report_in_len; + uint8_t report_in_buf[HID_DATA_FS_MAX_PACKET_SIZE]; } usbd_hid_itf_t; -int usbd_hid_rx_num(usbd_hid_itf_t *hid); -int usbd_hid_rx(usbd_hid_itf_t *hid, size_t len, uint8_t *buf, uint32_t timeout); +static inline int usbd_hid_rx_num(usbd_hid_itf_t *hid) { + return hid->report_in_len != USBD_HID_REPORT_INVALID; +} + +int usbd_hid_rx(usbd_hid_itf_t *hid, size_t len, uint8_t *buf, uint32_t timeout_ms); #endif // MICROPY_INCLUDED_STM32_USBD_HID_INTERFACE_H diff --git a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c index 32ce4ca87..e13a93ddb 100644 --- a/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c +++ b/ports/stm32/usbdev/class/src/usbd_cdc_msc_hid.c @@ -1190,30 +1190,6 @@ uint8_t USBD_HID_SendReport(usbd_hid_state_t *hid, uint8_t *report, uint16_t len return USBD_FAIL; } -uint8_t USBD_HID_SetNAK(usbd_hid_state_t *hid) { - // get USBx object from pdev (needed for USBx_OUTEP macro below) - PCD_HandleTypeDef *hpcd = hid->usbd->pdev->pData; - USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; - #if defined(STM32H7) - uint32_t USBx_BASE = (uint32_t)USBx; - #endif - // set NAK on HID OUT endpoint - USBx_OUTEP(HID_OUT_EP_WITH_CDC)->DOEPCTL |= USB_OTG_DOEPCTL_SNAK; - return USBD_OK; -} - -uint8_t USBD_HID_ClearNAK(usbd_hid_state_t *hid) { - // get USBx object from pdev (needed for USBx_OUTEP macro below) - PCD_HandleTypeDef *hpcd = hid->usbd->pdev->pData; - USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; - #if defined(STM32H7) - uint32_t USBx_BASE = (uint32_t)USBx; - #endif - // clear NAK on HID OUT endpoint - USBx_OUTEP(HID_OUT_EP_WITH_CDC)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK; - return USBD_OK; -} - #endif // CDC/MSC/HID interface class callback structure