/* * 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 #include "usbd_cdc_msc_hid.h" #include "usbd_cdc_interface.h" #include "pendsv.h" #include "py/obj.h" #include "lib/utils/interrupt_char.h" #include "irq.h" #if MICROPY_HW_ENABLE_USB // CDC control commands #define CDC_SEND_ENCAPSULATED_COMMAND 0x00 #define CDC_GET_ENCAPSULATED_RESPONSE 0x01 #define CDC_SET_COMM_FEATURE 0x02 #define CDC_GET_COMM_FEATURE 0x03 #define CDC_CLEAR_COMM_FEATURE 0x04 #define CDC_SET_LINE_CODING 0x20 #define CDC_GET_LINE_CODING 0x21 #define CDC_SET_CONTROL_LINE_STATE 0x22 #define CDC_SEND_BREAK 0x23 uint8_t *usbd_cdc_init(usbd_cdc_state_t *cdc_in) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in; // Reset all the CDC state // Note: we don't reset tx_buf_ptr_in in order to allow the output buffer to // be filled (by usbd_cdc_tx_always) before the USB device is connected. cdc->rx_buf_put = 0; cdc->rx_buf_get = 0; cdc->tx_buf_ptr_out = 0; cdc->tx_buf_ptr_out_shadow = 0; cdc->tx_buf_ptr_wait_count = 0; cdc->tx_need_empty_packet = 0; cdc->dev_is_connected = 0; #if MICROPY_HW_USB_ENABLE_CDC2 cdc->attached_to_repl = &cdc->base == cdc->base.usbd->cdc; #else cdc->attached_to_repl = 1; #endif // Return the buffer to place the first USB OUT packet return cdc->rx_packet_buf; } void usbd_cdc_deinit(usbd_cdc_state_t *cdc_in) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in; cdc->dev_is_connected = 0; } // Manage the CDC class requests // cmd: command code // pbuf: buffer containing command data (request parameters) // length: number of data to be sent (in bytes) // Returns USBD_OK if all operations are OK else USBD_FAIL int8_t usbd_cdc_control(usbd_cdc_state_t *cdc_in, uint8_t cmd, uint8_t* pbuf, uint16_t length) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in; switch (cmd) { case CDC_SEND_ENCAPSULATED_COMMAND: /* Add your code here */ break; case CDC_GET_ENCAPSULATED_RESPONSE: /* Add your code here */ break; case CDC_SET_COMM_FEATURE: /* Add your code here */ break; case CDC_GET_COMM_FEATURE: /* Add your code here */ break; case CDC_CLEAR_COMM_FEATURE: /* Add your code here */ break; case CDC_SET_LINE_CODING: #if 0 LineCoding.bitrate = (uint32_t)(pbuf[0] | (pbuf[1] << 8) |\ (pbuf[2] << 16) | (pbuf[3] << 24)); LineCoding.format = pbuf[4]; LineCoding.paritytype = pbuf[5]; LineCoding.datatype = pbuf[6]; /* Set the new configuration */ #endif break; case CDC_GET_LINE_CODING: /* Add your code here */ pbuf[0] = (uint8_t)(115200); pbuf[1] = (uint8_t)(115200 >> 8); pbuf[2] = (uint8_t)(115200 >> 16); pbuf[3] = (uint8_t)(115200 >> 24); pbuf[4] = 0; // stop bits (1) pbuf[5] = 0; // parity (none) pbuf[6] = 8; // number of bits (8) break; case CDC_SET_CONTROL_LINE_STATE: cdc->dev_is_connected = length & 1; // wValue is passed in Len (bit of a hack) break; case CDC_SEND_BREAK: /* Add your code here */ break; default: break; } return USBD_OK; } // This function is called to process outgoing data. We hook directly into the // SOF (start of frame) callback so that it is called exactly at the time it is // needed (reducing latency), and often enough (increasing bandwidth). static void usbd_cdc_sof(PCD_HandleTypeDef *hpcd, usbd_cdc_itf_t *cdc) { if (cdc == NULL || !cdc->dev_is_connected) { // CDC device is not connected to a host, so we are unable to send any data return; } if (cdc->tx_buf_ptr_out == cdc->tx_buf_ptr_in && !cdc->tx_need_empty_packet) { // No outstanding data to send return; } if (cdc->tx_buf_ptr_out != cdc->tx_buf_ptr_out_shadow) { // We have sent data and are waiting for the low-level USB driver to // finish sending it over the USB in-endpoint. // SOF occurs every 1ms, so we have a 500 * 1ms = 500ms timeout // We have a relatively large timeout because the USB host may be busy // doing other things and we must give it a chance to read our data. if (cdc->tx_buf_ptr_wait_count < 500) { USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; if (USBx_INEP(cdc->base.in_ep & 0x7f)->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ) { // USB in-endpoint is still reading the data cdc->tx_buf_ptr_wait_count++; return; } } cdc->tx_buf_ptr_out = cdc->tx_buf_ptr_out_shadow; } if (cdc->tx_buf_ptr_out_shadow != cdc->tx_buf_ptr_in || cdc->tx_need_empty_packet) { uint32_t buffptr; uint32_t buffsize; if (cdc->tx_buf_ptr_out_shadow > cdc->tx_buf_ptr_in) { // rollback buffsize = USBD_CDC_TX_DATA_SIZE - cdc->tx_buf_ptr_out_shadow; } else { buffsize = cdc->tx_buf_ptr_in - cdc->tx_buf_ptr_out_shadow; } buffptr = cdc->tx_buf_ptr_out_shadow; if (USBD_CDC_TransmitPacket(&cdc->base, buffsize, &cdc->tx_buf[buffptr]) == USBD_OK) { cdc->tx_buf_ptr_out_shadow += buffsize; if (cdc->tx_buf_ptr_out_shadow == USBD_CDC_TX_DATA_SIZE) { cdc->tx_buf_ptr_out_shadow = 0; } cdc->tx_buf_ptr_wait_count = 0; // According to the USB specification, a packet size of 64 bytes (CDC_DATA_FS_MAX_PACKET_SIZE) // gets held at the USB host until the next packet is sent. This is because a // packet of maximum size is considered to be part of a longer chunk of data, and // the host waits for all data to arrive (ie, waits for a packet < max packet size). // To flush a packet of exactly max packet size, we need to send a zero-size packet. // See eg http://www.cypress.com/?id=4&rID=92719 cdc->tx_need_empty_packet = (buffsize > 0 && buffsize % usbd_cdc_max_packet(cdc->base.usbd->pdev) == 0 && cdc->tx_buf_ptr_out_shadow == cdc->tx_buf_ptr_in); } } } void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) { usbd_cdc_msc_hid_state_t *usbd = ((USBD_HandleTypeDef*)hpcd->pData)->pClassData; usbd_cdc_sof(hpcd, (usbd_cdc_itf_t*)usbd->cdc); #if MICROPY_HW_USB_ENABLE_CDC2 usbd_cdc_sof(hpcd, (usbd_cdc_itf_t*)usbd->cdc2); #endif } // Data received over USB OUT endpoint is processed here. // len: number of bytes received into the buffer we passed to USBD_CDC_ReceivePacket // Returns USBD_OK if all operations are OK else USBD_FAIL int8_t usbd_cdc_receive(usbd_cdc_state_t *cdc_in, size_t len) { usbd_cdc_itf_t *cdc = (usbd_cdc_itf_t*)cdc_in; // copy the incoming data into the circular buffer for (const uint8_t *src = cdc->rx_packet_buf, *top = cdc->rx_packet_buf + len; src < top; ++src) { if (cdc->attached_to_repl && mp_interrupt_char != -1 && *src == mp_interrupt_char) { pendsv_kbd_intr(); } else { uint16_t next_put = (cdc->rx_buf_put + 1) & (USBD_CDC_RX_DATA_SIZE - 1); if (next_put == cdc->rx_buf_get) { // overflow, we just discard the rest of the chars break; } cdc->rx_user_buf[cdc->rx_buf_put] = *src; cdc->rx_buf_put = next_put; } } // initiate next USB packet transfer USBD_CDC_ReceivePacket(&cdc->base, cdc->rx_packet_buf); return USBD_OK; } int usbd_cdc_tx_half_empty(usbd_cdc_itf_t *cdc) { int32_t tx_waiting = (int32_t)cdc->tx_buf_ptr_in - (int32_t)cdc->tx_buf_ptr_out; if (tx_waiting < 0) { tx_waiting += USBD_CDC_TX_DATA_SIZE; } return tx_waiting <= USBD_CDC_TX_DATA_SIZE / 2; } // timout in milliseconds. // Returns number of bytes written to the device. int usbd_cdc_tx(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len, uint32_t timeout) { for (uint32_t i = 0; i < len; i++) { // Wait until the device is connected and the buffer has space, with a given timeout uint32_t start = HAL_GetTick(); while (!cdc->dev_is_connected || ((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out) { // Wraparound of tick is taken care of by 2's complement arithmetic. if (HAL_GetTick() - start >= timeout) { // timeout return i; } if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be drained; return immediately return i; } __WFI(); // enter sleep mode, waiting for interrupt } // Write data to device buffer cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); } // Success, return number of bytes read return len; } // Always write all of the data to the device tx buffer, even if the // device is not connected, or if the buffer is full. Has a small timeout // to wait for the buffer to be drained, in the case the device is connected. void usbd_cdc_tx_always(usbd_cdc_itf_t *cdc, const uint8_t *buf, uint32_t len) { for (int i = 0; i < len; i++) { // If the CDC device is not connected to the host then we don't have anyone to receive our data. // The device may become connected in the future, so we should at least try to fill the buffer // and hope that it doesn't overflow by the time the device connects. // If the device is not connected then we should go ahead and fill the buffer straight away, // ignoring overflow. Otherwise, we should make sure that we have enough room in the buffer. if (cdc->dev_is_connected) { // If the buffer is full, wait until it gets drained, with a timeout of 500ms // (wraparound of tick is taken care of by 2's complement arithmetic). uint32_t start = HAL_GetTick(); while (((cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1)) == cdc->tx_buf_ptr_out && HAL_GetTick() - start <= 500) { if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be drained; exit loop break; } __WFI(); // enter sleep mode, waiting for interrupt } // Some unused code that makes sure the low-level USB buffer is drained. // Waiting for low-level is handled in HAL_PCD_SOFCallback. /* start = HAL_GetTick(); PCD_HandleTypeDef *hpcd = hUSBDDevice.pData; if (hpcd->IN_ep[0x83 & 0x7f].is_in) { //volatile uint32_t *xfer_count = &hpcd->IN_ep[0x83 & 0x7f].xfer_count; //volatile uint32_t *xfer_len = &hpcd->IN_ep[0x83 & 0x7f].xfer_len; USB_OTG_GlobalTypeDef *USBx = hpcd->Instance; while ( // *xfer_count < *xfer_len // using this works // (USBx_INEP(3)->DIEPTSIZ & USB_OTG_DIEPTSIZ_XFRSIZ) // using this works && HAL_GetTick() - start <= 2000) { __WFI(); // enter sleep mode, waiting for interrupt } } */ } cdc->tx_buf[cdc->tx_buf_ptr_in] = buf[i]; cdc->tx_buf_ptr_in = (cdc->tx_buf_ptr_in + 1) & (USBD_CDC_TX_DATA_SIZE - 1); } } // Returns number of bytes in the rx buffer. int usbd_cdc_rx_num(usbd_cdc_itf_t *cdc) { int32_t rx_waiting = (int32_t)cdc->rx_buf_put - (int32_t)cdc->rx_buf_get; if (rx_waiting < 0) { rx_waiting += USBD_CDC_RX_DATA_SIZE; } return rx_waiting; } // timout in milliseconds. // Returns number of bytes read from the device. int usbd_cdc_rx(usbd_cdc_itf_t *cdc, uint8_t *buf, uint32_t len, uint32_t timeout) { // loop to read bytes for (uint32_t i = 0; i < len; i++) { // Wait until we have at least 1 byte to read uint32_t start = HAL_GetTick(); while (cdc->rx_buf_put == cdc->rx_buf_get) { // Wraparound of tick is taken care of by 2's complement arithmetic. if (HAL_GetTick() - start >= timeout) { // timeout return i; } if (query_irq() == IRQ_STATE_DISABLED) { // IRQs disabled so buffer will never be filled; return immediately return i; } __WFI(); // enter sleep mode, waiting for interrupt } // Copy byte from device to user buffer buf[i] = cdc->rx_user_buf[cdc->rx_buf_get]; cdc->rx_buf_get = (cdc->rx_buf_get + 1) & (USBD_CDC_RX_DATA_SIZE - 1); } // Success, return number of bytes read return len; } #endif