diff --git a/docs/library/pyb.USB_HID.rst b/docs/library/pyb.USB_HID.rst index 65fb4014e0..7d17c3099f 100644 --- a/docs/library/pyb.USB_HID.rst +++ b/docs/library/pyb.USB_HID.rst @@ -20,6 +20,17 @@ Constructors Methods ------- +.. method:: USB_HID.recv(data, \*, timeout=5000) + + Receive data on the bus: + + - ``data`` can be an integer, which is the number of bytes to receive, + or a mutable buffer, which will be filled with received bytes. + - ``timeout`` is the timeout in milliseconds to wait for the receive. + + Return value: if ``data`` is an integer then a new buffer of the bytes received, + otherwise the number of bytes read into ``data`` is returned. + .. method:: USB_HID.send(data) Send data over the USB HID interface: diff --git a/stmhal/Makefile b/stmhal/Makefile index a135fbb5cb..477cb2631d 100644 --- a/stmhal/Makefile +++ b/stmhal/Makefile @@ -121,6 +121,7 @@ SRC_C = \ usbd_conf.c \ usbd_desc.c \ usbd_cdc_interface.c \ + usbd_hid_interface.c \ usbd_msc_storage.c \ mphalport.c \ irq.c \ diff --git a/stmhal/usb.c b/stmhal/usb.c index 4ef6bfa500..4eb67a8ca8 100644 --- a/stmhal/usb.c +++ b/stmhal/usb.c @@ -32,6 +32,7 @@ #include "usbd_cdc_msc_hid.h" #include "usbd_cdc_interface.h" #include "usbd_msc_storage.h" +#include "usbd_hid_interface.h" #include "py/objstr.h" #include "py/runtime.h" @@ -123,6 +124,7 @@ bool pyb_usb_dev_init(uint16_t vid, uint16_t pid, usb_device_mode_t mode, USBD_H USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_FLASH_STORAGE_fops); break; } + USBD_HID_RegisterInterface(&hUSBDDevice, (USBD_HID_ItfTypeDef*)&USBD_HID_fops); USBD_Start(&hUSBDDevice); } pyb_usb_flags |= PYB_USB_FLAG_DEV_ENABLED; @@ -553,6 +555,43 @@ STATIC mp_obj_t pyb_usb_hid_make_new(const mp_obj_type_t *type, mp_uint_t n_args return (mp_obj_t)&pyb_usb_hid_obj; } +/// \method recv(data, *, timeout=5000) +/// +/// Receive data on the bus: +/// +/// - `data` can be an integer, which is the number of bytes to receive, +/// or a mutable buffer, which will be filled with received bytes. +/// - `timeout` is the timeout in milliseconds to wait for the receive. +/// +/// Return value: if `data` is an integer then a new buffer of the bytes received, +/// otherwise the number of bytes read into `data` is returned. +STATIC mp_obj_t pyb_usb_hid_recv(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} }, + }; + + // parse args + mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals); + + // get the buffer to receive into + vstr_t vstr; + mp_obj_t o_ret = pyb_buf_get_for_recv(vals[0].u_obj, &vstr); + + // receive the data + int ret = USBD_HID_Rx((uint8_t*)vstr.buf, vstr.len, vals[1].u_int); + + // return the received data + if (o_ret != MP_OBJ_NULL) { + return mp_obj_new_int(ret); // number of bytes read into given buffer + } else { + vstr.len = ret; // set actual number of bytes read + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); // create a new buffer + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usb_hid_recv_obj, 1, pyb_usb_hid_recv); + STATIC mp_obj_t pyb_usb_hid_send(mp_obj_t self_in, mp_obj_t report_in) { #ifdef USE_DEVICE_MODE mp_buffer_info_t bufinfo; @@ -587,6 +626,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report); STATIC const mp_map_elem_t pyb_usb_hid_locals_dict_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_usb_hid_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_usb_hid_recv_obj }, }; STATIC MP_DEFINE_CONST_DICT(pyb_usb_hid_locals_dict, pyb_usb_hid_locals_dict_table); diff --git a/stmhal/usbd_hid_interface.c b/stmhal/usbd_hid_interface.c new file mode 100644 index 0000000000..10e03fa420 --- /dev/null +++ b/stmhal/usbd_hid_interface.c @@ -0,0 +1,112 @@ +/* + * 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. + */ + +/** + ****************************************************************************** + * @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_cdc_msc_hid.h" +#include "usbd_hid_interface.h" + +#include "py/obj.h" +#include "irq.h" +#include "usb.h" + +/* Private variables ---------------------------------------------------------*/ + +static __IO uint8_t dev_is_connected = 0; // indicates if we are connected + +static uint8_t buffer[2][64]; // pair of buffers to read individual packets into +static int8_t current_read_buffer = 0; // which buffer to read from +static uint32_t last_read_len; // length of last read +static int8_t current_write_buffer = 0; // which buffer to write to + +static size_t rx_packet_size = 64; // expected size of packets to receive + +/* Private function prototypes -----------------------------------------------*/ +static int8_t HID_Itf_Receive (uint8_t* pbuf, uint32_t Len); + +const USBD_HID_ItfTypeDef USBD_HID_fops = { + HID_Itf_Receive +}; + +/** + * @brief HID_Itf_Receive + * Data received over USB OUT endpoint is processed here. + * @param Buf: Buffer of data received + * @param Len: Number of data received (in bytes) + * @retval Result of the opeartion: USBD_OK if all operations are OK else USBD_FAIL + * @note The buffer we are passed here is just UserRxBuffer, so we are + * free to modify it. + */ +static int8_t HID_Itf_Receive(uint8_t* Buf, uint32_t Len) { + current_write_buffer = !current_write_buffer; + last_read_len = Len; + // initiate next USB packet transfer, to append to existing data in buffer + USBD_HID_SetRxBuffer(&hUSBDDevice, buffer[current_write_buffer]); + USBD_HID_ReceivePacket(&hUSBDDevice); + + return USBD_OK; +} + +// timout in milliseconds. +// Returns number of bytes read from the device. +int USBD_HID_Rx(uint8_t *buf, uint32_t len, uint32_t timeout) { + // Wait until we have buffer to read + uint32_t start = HAL_GetTick(); + while (current_read_buffer == current_write_buffer) { + // Wraparound of tick is taken care of by 2's complement arithmetic. + if (HAL_GetTick() - start >= timeout) { + // timeout + return 0; + } + 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 + } + + // There is not enough space in buffer + if (len < last_read_len) { + return 0; + } + + // Copy bytes from device to user buffer + memcpy(buf, buffer[current_read_buffer], last_read_len); + current_read_buffer = !current_read_buffer; + + // Success, return number of bytes read + return last_read_len; +} diff --git a/stmhal/usbd_hid_interface.h b/stmhal/usbd_hid_interface.h new file mode 100644 index 0000000000..9cdf32549d --- /dev/null +++ b/stmhal/usbd_hid_interface.h @@ -0,0 +1,9 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + */ + +#include "usbd_cdc_msc_hid.h" + +extern const USBD_HID_ItfTypeDef USBD_HID_fops; + +int USBD_HID_Rx(uint8_t *buf, uint32_t len, uint32_t timeout); diff --git a/stmhal/usbdev/class/inc/usbd_cdc_msc_hid.h b/stmhal/usbdev/class/inc/usbd_cdc_msc_hid.h index 248ce17f3e..76a7678929 100644 --- a/stmhal/usbdev/class/inc/usbd_cdc_msc_hid.h +++ b/stmhal/usbdev/class/inc/usbd_cdc_msc_hid.h @@ -6,9 +6,10 @@ #include "usbd_msc_scsi.h" #include "usbd_ioreq.h" -// CDC and MSC packet sizes +// CDC, MSC and HID packet sizes #define CDC_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size #define MSC_MEDIA_PACKET (2048) // was 8192; how low can it go whilst still working? +#define HID_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size // Need to define here for BOT and SCSI layers #define MSC_IN_EP (0x81) @@ -46,6 +47,10 @@ typedef struct { __IO uint32_t RxState; } USBD_CDC_HandleTypeDef; +typedef struct _USBD_HID_Itf { + int8_t (* Receive)(uint8_t *, uint32_t); +} USBD_HID_ItfTypeDef; + typedef struct _USBD_STORAGE { int8_t (* Init) (uint8_t lun); int8_t (* GetCapacity) (uint8_t lun, uint32_t *block_num, uint16_t *block_size); @@ -105,6 +110,9 @@ uint8_t USBD_CDC_TransmitPacket (USBD_HandleTypeDef *pdev); uint8_t USBD_MSC_RegisterStorage(USBD_HandleTypeDef *pdev, USBD_StorageTypeDef *fops); +uint8_t USBD_HID_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_HID_ItfTypeDef *fops); +uint8_t USBD_HID_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff); +uint8_t USBD_HID_ReceivePacket(USBD_HandleTypeDef *pdev); int USBD_HID_CanSendReport(USBD_HandleTypeDef *pdev); uint8_t USBD_HID_SendReport(USBD_HandleTypeDef *pdev, uint8_t *report, uint16_t len); diff --git a/stmhal/usbdev/class/src/usbd_cdc_msc_hid.c b/stmhal/usbdev/class/src/usbd_cdc_msc_hid.c index a3c86140f5..3ac9029321 100644 --- a/stmhal/usbdev/class/src/usbd_cdc_msc_hid.c +++ b/stmhal/usbdev/class/src/usbd_cdc_msc_hid.c @@ -81,6 +81,8 @@ typedef struct { uint32_t IdleState; uint32_t AltSetting; HID_StateTypeDef state; + uint8_t *RxBuffer; + uint32_t RxLength; } USBD_HID_HandleTypeDef; static uint8_t usbd_mode; @@ -94,6 +96,7 @@ static const uint8_t *hid_report_desc; static USBD_CDC_ItfTypeDef *CDC_fops; static USBD_StorageTypeDef *MSC_fops; +static USBD_HID_ItfTypeDef *HID_fops; static USBD_CDC_HandleTypeDef CDC_ClassData; static USBD_MSC_BOT_HandleTypeDef MSC_BOT_ClassData; @@ -962,6 +965,9 @@ static uint8_t USBD_CDC_MSC_HID_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) } else if ((usbd_mode & USBD_MODE_MSC) && epnum == (MSC_OUT_EP & 0x7f)) { MSC_BOT_DataOut(pdev, epnum); return USBD_OK; + } else if ((usbd_mode & USBD_MODE_HID) && epnum == (hid_out_ep & 0x7f)) { + HID_ClassData.RxLength = USBD_LL_GetRxDataSize(pdev, epnum); + HID_fops->Receive(HID_ClassData.RxBuffer, HID_ClassData.RxLength); } return USBD_OK; @@ -1039,6 +1045,33 @@ uint8_t USBD_MSC_RegisterStorage(USBD_HandleTypeDef *pdev, USBD_StorageTypeDef * } } +uint8_t USBD_HID_RegisterInterface(USBD_HandleTypeDef *pdev, USBD_HID_ItfTypeDef *fops) { + if (fops == NULL) { + return USBD_FAIL; + } else { + HID_fops = fops; + return USBD_OK; + } +} + +uint8_t USBD_HID_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff) { + HID_ClassData.RxBuffer = pbuff; + return USBD_OK; +} + +// prepare OUT endpoint for reception +uint8_t USBD_HID_ReceivePacket(USBD_HandleTypeDef *pdev) { + // Suspend or Resume USB Out process + if (pdev->dev_speed == USBD_SPEED_HIGH) { + return USBD_FAIL; + } + + // Prepare Out endpoint to receive next packet + USBD_LL_PrepareReceive(pdev, hid_out_ep, HID_ClassData.RxBuffer, HID_DATA_FS_MAX_PACKET_SIZE); + + return USBD_OK; +} + int USBD_HID_CanSendReport(USBD_HandleTypeDef *pdev) { return pdev->dev_state == USBD_STATE_CONFIGURED && HID_ClassData.state == HID_IDLE; }