From e8701195e66f2d27ffe17fb514eae8173795aaf7 Mon Sep 17 00:00:00 2001 From: Georgiy Bondarenko <69736697+nehilo@users.noreply.github.com> Date: Thu, 4 Mar 2021 22:54:23 +0500 Subject: Initial commit --- Marlin/src/HAL/DUE/usb/udi_cdc.c | 1155 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1155 insertions(+) create mode 100644 Marlin/src/HAL/DUE/usb/udi_cdc.c (limited to 'Marlin/src/HAL/DUE/usb/udi_cdc.c') diff --git a/Marlin/src/HAL/DUE/usb/udi_cdc.c b/Marlin/src/HAL/DUE/usb/udi_cdc.c new file mode 100644 index 0000000..cbe23db --- /dev/null +++ b/Marlin/src/HAL/DUE/usb/udi_cdc.c @@ -0,0 +1,1155 @@ +/** + * \file + * + * \brief USB Device Communication Device Class (CDC) interface. + * + * Copyright (c) 2009-2016 Atmel Corporation. All rights reserved. + * + * \asf_license_start + * + * \page License + * + * 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. The name of Atmel may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 4. This software may only be redistributed and used in connection with an + * Atmel microcontroller product. + * + * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. + * + * \asf_license_stop + * + */ +/* + * Support and FAQ: visit Atmel Support + */ + +#ifdef ARDUINO_ARCH_SAM + +#include "conf_usb.h" +#include "usb_protocol.h" +#include "usb_protocol_cdc.h" +#include "udd.h" +#include "udc.h" +#include "udi_cdc.h" +#include + +#ifdef UDI_CDC_LOW_RATE +# ifdef USB_DEVICE_HS_SUPPORT +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# else +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_FS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_FS_SIZE) +# endif +#else +# ifdef USB_DEVICE_HS_SUPPORT +# define UDI_CDC_TX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# define UDI_CDC_RX_BUFFERS (UDI_CDC_DATA_EPS_HS_SIZE) +# else +# define UDI_CDC_TX_BUFFERS (5*UDI_CDC_DATA_EPS_FS_SIZE) +# define UDI_CDC_RX_BUFFERS (5*UDI_CDC_DATA_EPS_FS_SIZE) +# endif +#endif + +#ifndef UDI_CDC_TX_EMPTY_NOTIFY +# define UDI_CDC_TX_EMPTY_NOTIFY(port) +#endif + +/** + * \ingroup udi_cdc_group + * \defgroup udi_cdc_group_udc Interface with USB Device Core (UDC) + * + * Structures and functions required by UDC. + * + * @{ + */ +bool udi_cdc_comm_enable(void); +void udi_cdc_comm_disable(void); +bool udi_cdc_comm_setup(void); +bool udi_cdc_data_enable(void); +void udi_cdc_data_disable(void); +bool udi_cdc_data_setup(void); +uint8_t udi_cdc_getsetting(void); +void udi_cdc_data_sof_notify(void); +UDC_DESC_STORAGE udi_api_t udi_api_cdc_comm = { + .enable = udi_cdc_comm_enable, + .disable = udi_cdc_comm_disable, + .setup = udi_cdc_comm_setup, + .getsetting = udi_cdc_getsetting, +}; +UDC_DESC_STORAGE udi_api_t udi_api_cdc_data = { + .enable = udi_cdc_data_enable, + .disable = udi_cdc_data_disable, + .setup = udi_cdc_data_setup, + .getsetting = udi_cdc_getsetting, + .sof_notify = udi_cdc_data_sof_notify, +}; +//@} + +/** + * \ingroup udi_cdc_group + * \defgroup udi_cdc_group_internal Implementation of UDI CDC + * + * Class internal implementation + * @{ + */ + +/** + * \name Internal routines + */ +//@{ + +/** + * \name Routines to control serial line + */ +//@{ + +/** + * \brief Returns the port number corresponding at current setup request + * + * \return port number + */ +static uint8_t udi_cdc_setup_to_port(void); + +/** + * \brief Sends line coding to application + * + * Called after SETUP request when line coding data is received. + */ +static void udi_cdc_line_coding_received(void); + +/** + * \brief Records new state + * + * \param port Communication port number to manage + * \param b_set State is enabled if true, else disabled + * \param bit_mask Field to process (see CDC_SERIAL_STATE_ defines) + */ +static void udi_cdc_ctrl_state_change(uint8_t port, bool b_set, le16_t bit_mask); + +/** + * \brief Check and eventually notify the USB host of new state + * + * \param port Communication port number to manage + * \param ep Port communication endpoint + */ +static void udi_cdc_ctrl_state_notify(uint8_t port, udd_ep_id_t ep); + +/** + * \brief Ack sent of serial state message + * Callback called after serial state message sent + * + * \param status UDD_EP_TRANSFER_OK, if transfer finished + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data transfered + */ +static void udi_cdc_serial_state_msg_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +//@} + +/** + * \name Routines to process data transfer + */ +//@{ + +/** + * \brief Enable the reception of data from the USB host + * + * The value udi_cdc_rx_trans_sel indicate the RX buffer to fill. + * + * \param port Communication port number to manage + * + * \return \c 1 if function was successfully done, otherwise \c 0. + */ +static bool udi_cdc_rx_start(uint8_t port); + +/** + * \brief Update rx buffer management with a new data + * Callback called after data reception on USB line + * + * \param status UDD_EP_TRANSFER_OK, if transfer finish + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data received + */ +static void udi_cdc_data_received(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +/** + * \brief Ack sent of tx buffer + * Callback called after data transfer on USB line + * + * \param status UDD_EP_TRANSFER_OK, if transfer finished + * \param status UDD_EP_TRANSFER_ABORT, if transfer aborted + * \param n number of data transfered + */ +static void udi_cdc_data_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep); + +/** + * \brief Send buffer on line or wait a SOF event + * + * \param port Communication port number to manage + */ +static void udi_cdc_tx_send(uint8_t port); + +//@} + +//@} + +/** + * \name Information about configuration of communication line + */ +//@{ +COMPILER_WORD_ALIGNED + static usb_cdc_line_coding_t udi_cdc_line_coding[UDI_CDC_PORT_NB]; +static bool udi_cdc_serial_state_msg_ongoing[UDI_CDC_PORT_NB]; +static volatile le16_t udi_cdc_state[UDI_CDC_PORT_NB]; +COMPILER_WORD_ALIGNED static usb_cdc_notify_serial_state_t uid_cdc_state_msg[UDI_CDC_PORT_NB]; + +//! Status of CDC COMM interfaces +static volatile uint8_t udi_cdc_nb_comm_enabled = 0; +//@} + +/** + * \name Variables to manage RX/TX transfer requests + * Two buffers for each sense are used to optimize the speed. + */ +//@{ + +//! Status of CDC DATA interfaces +static volatile uint8_t udi_cdc_nb_data_enabled = 0; +static volatile bool udi_cdc_data_running = false; +//! Buffer to receive data +COMPILER_WORD_ALIGNED static uint8_t udi_cdc_rx_buf[UDI_CDC_PORT_NB][2][UDI_CDC_RX_BUFFERS]; +//! Data available in RX buffers +static volatile uint16_t udi_cdc_rx_buf_nb[UDI_CDC_PORT_NB][2]; +//! Give the current RX buffer used (rx0 if 0, rx1 if 1) +static volatile uint8_t udi_cdc_rx_buf_sel[UDI_CDC_PORT_NB]; +//! Read position in current RX buffer +static volatile uint16_t udi_cdc_rx_pos[UDI_CDC_PORT_NB]; +//! Signal a transfer on-going +static volatile bool udi_cdc_rx_trans_ongoing[UDI_CDC_PORT_NB]; + +//! Define a transfer halted +#define UDI_CDC_TRANS_HALTED 2 + +//! Buffer to send data +COMPILER_WORD_ALIGNED static uint8_t udi_cdc_tx_buf[UDI_CDC_PORT_NB][2][UDI_CDC_TX_BUFFERS]; +//! Data available in TX buffers +static uint16_t udi_cdc_tx_buf_nb[UDI_CDC_PORT_NB][2]; +//! Give current TX buffer used (tx0 if 0, tx1 if 1) +static volatile uint8_t udi_cdc_tx_buf_sel[UDI_CDC_PORT_NB]; +//! Value of SOF during last TX transfer +static uint16_t udi_cdc_tx_sof_num[UDI_CDC_PORT_NB]; +//! Signal a transfer on-going +static volatile bool udi_cdc_tx_trans_ongoing[UDI_CDC_PORT_NB]; +//! Signal that both buffer content data to send +static volatile bool udi_cdc_tx_both_buf_to_send[UDI_CDC_PORT_NB]; + +//@} + +bool udi_cdc_comm_enable(void) +{ + uint8_t port; + uint8_t iface_comm_num; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; + udi_cdc_nb_comm_enabled = 0; +#else + if (udi_cdc_nb_comm_enabled > UDI_CDC_PORT_NB) { + udi_cdc_nb_comm_enabled = 0; + } + port = udi_cdc_nb_comm_enabled; +#endif + + // Initialize control signal management + udi_cdc_state[port] = CPU_TO_LE16(0); + + uid_cdc_state_msg[port].header.bmRequestType = + USB_REQ_DIR_IN | USB_REQ_TYPE_CLASS | + USB_REQ_RECIP_INTERFACE; + uid_cdc_state_msg[port].header.bNotification = USB_REQ_CDC_NOTIFY_SERIAL_STATE; + uid_cdc_state_msg[port].header.wValue = LE16(0); + + switch (port) { +#define UDI_CDC_PORT_TO_IFACE_COMM(index, unused) \ + case index: \ + iface_comm_num = UDI_CDC_COMM_IFACE_NUMBER_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_IFACE_COMM, ~) +#undef UDI_CDC_PORT_TO_IFACE_COMM + default: + iface_comm_num = UDI_CDC_COMM_IFACE_NUMBER_0; + break; + } + + uid_cdc_state_msg[port].header.wIndex = LE16(iface_comm_num); + uid_cdc_state_msg[port].header.wLength = LE16(2); + uid_cdc_state_msg[port].value = CPU_TO_LE16(0); + + udi_cdc_line_coding[port].dwDTERate = CPU_TO_LE32(UDI_CDC_DEFAULT_RATE); + udi_cdc_line_coding[port].bCharFormat = UDI_CDC_DEFAULT_STOPBITS; + udi_cdc_line_coding[port].bParityType = UDI_CDC_DEFAULT_PARITY; + udi_cdc_line_coding[port].bDataBits = UDI_CDC_DEFAULT_DATABITS; + // Call application callback + // to initialize memories or indicate that interface is enabled + UDI_CDC_SET_CODING_EXT(port,(&udi_cdc_line_coding[port])); + if (!UDI_CDC_ENABLE_EXT(port)) { + return false; + } + udi_cdc_nb_comm_enabled++; + return true; +} + +bool udi_cdc_data_enable(void) +{ + uint8_t port; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; + udi_cdc_nb_data_enabled = 0; +#else + if (udi_cdc_nb_data_enabled > UDI_CDC_PORT_NB) { + udi_cdc_nb_data_enabled = 0; + } + port = udi_cdc_nb_data_enabled; +#endif + + // Initialize TX management + udi_cdc_tx_trans_ongoing[port] = false; + udi_cdc_tx_both_buf_to_send[port] = false; + udi_cdc_tx_buf_sel[port] = 0; + udi_cdc_tx_buf_nb[port][0] = 0; + udi_cdc_tx_buf_nb[port][1] = 0; + udi_cdc_tx_sof_num[port] = 0; + udi_cdc_tx_send(port); + + // Initialize RX management + udi_cdc_rx_trans_ongoing[port] = false; + udi_cdc_rx_buf_sel[port] = 0; + udi_cdc_rx_buf_nb[port][0] = 0; + udi_cdc_rx_buf_nb[port][1] = 0; + udi_cdc_rx_pos[port] = 0; + if (!udi_cdc_rx_start(port)) { + return false; + } + udi_cdc_nb_data_enabled++; + if (udi_cdc_nb_data_enabled == UDI_CDC_PORT_NB) { + udi_cdc_data_running = true; + } + return true; +} + +void udi_cdc_comm_disable(void) +{ + Assert(udi_cdc_nb_comm_enabled != 0); + udi_cdc_nb_comm_enabled--; +} + +void udi_cdc_data_disable(void) +{ + uint8_t port; + + Assert(udi_cdc_nb_data_enabled != 0); + udi_cdc_nb_data_enabled--; + port = udi_cdc_nb_data_enabled; + UDI_CDC_DISABLE_EXT(port); + udi_cdc_data_running = false; +} + +bool udi_cdc_comm_setup(void) +{ + uint8_t port = udi_cdc_setup_to_port(); + + if (Udd_setup_is_in()) { + // GET Interface Requests + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Get + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_CDC_GET_LINE_CODING: + // Get configuration of CDC line + if (sizeof(usb_cdc_line_coding_t) != + udd_g_ctrlreq.req.wLength) + return false; // Error for USB host + udd_g_ctrlreq.payload = + (uint8_t *) & + udi_cdc_line_coding[port]; + udd_g_ctrlreq.payload_size = + sizeof(usb_cdc_line_coding_t); + return true; + } + } + } + if (Udd_setup_is_out()) { + // SET Interface Requests + if (Udd_setup_type() == USB_REQ_TYPE_CLASS) { + // Requests Class Interface Set + switch (udd_g_ctrlreq.req.bRequest) { + case USB_REQ_CDC_SET_LINE_CODING: + // Change configuration of CDC line + if (sizeof(usb_cdc_line_coding_t) != + udd_g_ctrlreq.req.wLength) + return false; // Error for USB host + udd_g_ctrlreq.callback = + udi_cdc_line_coding_received; + udd_g_ctrlreq.payload = + (uint8_t *) & + udi_cdc_line_coding[port]; + udd_g_ctrlreq.payload_size = + sizeof(usb_cdc_line_coding_t); + return true; + case USB_REQ_CDC_SET_CONTROL_LINE_STATE: + // According cdc spec 1.1 chapter 6.2.14 + UDI_CDC_SET_DTR_EXT(port, (0 != + (udd_g_ctrlreq.req.wValue + & CDC_CTRL_SIGNAL_DTE_PRESENT))); + UDI_CDC_SET_RTS_EXT(port, (0 != + (udd_g_ctrlreq.req.wValue + & CDC_CTRL_SIGNAL_ACTIVATE_CARRIER))); + return true; + } + } + } + return false; // request Not supported +} + +bool udi_cdc_data_setup(void) +{ + return false; // request Not supported +} + +uint8_t udi_cdc_getsetting(void) +{ + return 0; // CDC don't have multiple alternate setting +} + +void udi_cdc_data_sof_notify(void) +{ + static uint8_t port_notify = 0; + + // A call of udi_cdc_data_sof_notify() is done for each port + udi_cdc_tx_send(port_notify); +#if UDI_CDC_PORT_NB != 1 // To optimize code + port_notify++; + if (port_notify >= UDI_CDC_PORT_NB) { + port_notify = 0; + } +#endif +} + + +// ------------------------ +//------- Internal routines to control serial line + +static uint8_t udi_cdc_setup_to_port(void) +{ + uint8_t port; + + switch (udd_g_ctrlreq.req.wIndex & 0xFF) { +#define UDI_CDC_IFACE_COMM_TO_PORT(iface, unused) \ + case UDI_CDC_COMM_IFACE_NUMBER_##iface: \ + port = iface; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_IFACE_COMM_TO_PORT, ~) +#undef UDI_CDC_IFACE_COMM_TO_PORT + default: + port = 0; + break; + } + return port; +} + +static void udi_cdc_line_coding_received(void) +{ + uint8_t port = udi_cdc_setup_to_port(); + UNUSED(port); + + UDI_CDC_SET_CODING_EXT(port, (&udi_cdc_line_coding[port])); +} + +static void udi_cdc_ctrl_state_change(uint8_t port, bool b_set, le16_t bit_mask) +{ + irqflags_t flags; + udd_ep_id_t ep_comm; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + // Update state + flags = cpu_irq_save(); // Protect udi_cdc_state + if (b_set) { + udi_cdc_state[port] |= bit_mask; + } else { + udi_cdc_state[port] &= ~(unsigned)bit_mask; + } + cpu_irq_restore(flags); + + // Send it if possible and state changed + switch (port) { +#define UDI_CDC_PORT_TO_COMM_EP(index, unused) \ + case index: \ + ep_comm = UDI_CDC_COMM_EP_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_COMM_EP, ~) +#undef UDI_CDC_PORT_TO_COMM_EP + default: + ep_comm = UDI_CDC_COMM_EP_0; + break; + } + udi_cdc_ctrl_state_notify(port, ep_comm); +} + + +static void udi_cdc_ctrl_state_notify(uint8_t port, udd_ep_id_t ep) +{ +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + // Send it if possible and state changed + if ((!udi_cdc_serial_state_msg_ongoing[port]) + && (udi_cdc_state[port] != uid_cdc_state_msg[port].value)) { + // Fill notification message + uid_cdc_state_msg[port].value = udi_cdc_state[port]; + // Send notification message + udi_cdc_serial_state_msg_ongoing[port] = + udd_ep_run(ep, + false, + (uint8_t *) & uid_cdc_state_msg[port], + sizeof(uid_cdc_state_msg[0]), + udi_cdc_serial_state_msg_sent); + } +} + + +static void udi_cdc_serial_state_msg_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t port; + UNUSED(n); + UNUSED(status); + + switch (ep) { +#define UDI_CDC_GET_PORT_FROM_COMM_EP(iface, unused) \ + case UDI_CDC_COMM_EP_##iface: \ + port = iface; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_GET_PORT_FROM_COMM_EP, ~) +#undef UDI_CDC_GET_PORT_FROM_COMM_EP + default: + port = 0; + break; + } + + udi_cdc_serial_state_msg_ongoing[port] = false; + + // For the irregular signals like break, the incoming ring signal, + // or the overrun error state, this will reset their values to zero + // and again will not send another notification until their state changes. + udi_cdc_state[port] &= ~(CDC_SERIAL_STATE_BREAK | + CDC_SERIAL_STATE_RING | + CDC_SERIAL_STATE_FRAMING | + CDC_SERIAL_STATE_PARITY | CDC_SERIAL_STATE_OVERRUN); + uid_cdc_state_msg[port].value &= ~(CDC_SERIAL_STATE_BREAK | + CDC_SERIAL_STATE_RING | + CDC_SERIAL_STATE_FRAMING | + CDC_SERIAL_STATE_PARITY | CDC_SERIAL_STATE_OVERRUN); + // Send it if possible and state changed + udi_cdc_ctrl_state_notify(port, ep); +} + + +// ------------------------ +//------- Internal routines to process data transfer + + +static bool udi_cdc_rx_start(uint8_t port) +{ + irqflags_t flags; + uint8_t buf_sel_trans; + udd_ep_id_t ep; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + flags = cpu_irq_save(); + buf_sel_trans = udi_cdc_rx_buf_sel[port]; + if (udi_cdc_rx_trans_ongoing[port] || + (udi_cdc_rx_pos[port] < udi_cdc_rx_buf_nb[port][buf_sel_trans])) { + // Transfer already on-going or current buffer no empty + cpu_irq_restore(flags); + return false; + } + + // Change current buffer + udi_cdc_rx_pos[port] = 0; + udi_cdc_rx_buf_sel[port] = (buf_sel_trans==0)?1:0; + + // Start transfer on RX + udi_cdc_rx_trans_ongoing[port] = true; + cpu_irq_restore(flags); + + if (udi_cdc_multi_is_rx_ready(port)) { + UDI_CDC_RX_NOTIFY(port); + } + // Send the buffer with enable of short packet + switch (port) { +#define UDI_CDC_PORT_TO_DATA_EP_OUT(index, unused) \ + case index: \ + ep = UDI_CDC_DATA_EP_OUT_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_DATA_EP_OUT, ~) +#undef UDI_CDC_PORT_TO_DATA_EP_OUT + default: + ep = UDI_CDC_DATA_EP_OUT_0; + break; + } + return udd_ep_run(ep, + true, + udi_cdc_rx_buf[port][buf_sel_trans], + UDI_CDC_RX_BUFFERS, + udi_cdc_data_received); +} + + +static void udi_cdc_data_received(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t buf_sel_trans; + uint8_t port; + + switch (ep) { +#define UDI_CDC_DATA_EP_OUT_TO_PORT(index, unused) \ + case UDI_CDC_DATA_EP_OUT_##index: \ + port = index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_DATA_EP_OUT_TO_PORT, ~) +#undef UDI_CDC_DATA_EP_OUT_TO_PORT + default: + port = 0; + break; + } + + if (UDD_EP_TRANSFER_OK != status) { + // Abort reception + return; + } + buf_sel_trans = (udi_cdc_rx_buf_sel[port]==0)?1:0; + if (!n) { + udd_ep_run( ep, + true, + udi_cdc_rx_buf[port][buf_sel_trans], + UDI_CDC_RX_BUFFERS, + udi_cdc_data_received); + return; + } + udi_cdc_rx_buf_nb[port][buf_sel_trans] = n; + udi_cdc_rx_trans_ongoing[port] = false; + udi_cdc_rx_start(port); +} + + +static void udi_cdc_data_sent(udd_ep_status_t status, iram_size_t n, udd_ep_id_t ep) +{ + uint8_t port; + UNUSED(n); + + switch (ep) { +#define UDI_CDC_DATA_EP_IN_TO_PORT(index, unused) \ + case UDI_CDC_DATA_EP_IN_##index: \ + port = index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_DATA_EP_IN_TO_PORT, ~) +#undef UDI_CDC_DATA_EP_IN_TO_PORT + default: + port = 0; + break; + } + + if (UDD_EP_TRANSFER_OK != status) { + // Abort transfer + return; + } + udi_cdc_tx_buf_nb[port][(udi_cdc_tx_buf_sel[port]==0)?1:0] = 0; + udi_cdc_tx_both_buf_to_send[port] = false; + udi_cdc_tx_trans_ongoing[port] = false; + + if (n != 0) { + UDI_CDC_TX_EMPTY_NOTIFY(port); + } + udi_cdc_tx_send(port); +} + + +static void udi_cdc_tx_send(uint8_t port) +{ + irqflags_t flags; + uint8_t buf_sel_trans; + bool b_short_packet; + udd_ep_id_t ep; + static uint16_t sof_zlp_counter = 0; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + if (udi_cdc_tx_trans_ongoing[port]) { + return; // Already on going or wait next SOF to send next data + } + if (udd_is_high_speed()) { + if (udi_cdc_tx_sof_num[port] == udd_get_micro_frame_number()) { + return; // Wait next SOF to send next data + } + }else{ + if (udi_cdc_tx_sof_num[port] == udd_get_frame_number()) { + return; // Wait next SOF to send next data + } + } + + flags = cpu_irq_save(); // to protect udi_cdc_tx_buf_sel + buf_sel_trans = udi_cdc_tx_buf_sel[port]; + if (udi_cdc_tx_buf_nb[port][buf_sel_trans] == 0) { + sof_zlp_counter++; + if (((!udd_is_high_speed()) && (sof_zlp_counter < 100)) + || (udd_is_high_speed() && (sof_zlp_counter < 800))) { + cpu_irq_restore(flags); + return; + } + } + sof_zlp_counter = 0; + + if (!udi_cdc_tx_both_buf_to_send[port]) { + // Send current Buffer + // and switch the current buffer + udi_cdc_tx_buf_sel[port] = (buf_sel_trans==0)?1:0; + }else{ + // Send the other Buffer + // and no switch the current buffer + buf_sel_trans = (buf_sel_trans==0)?1:0; + } + udi_cdc_tx_trans_ongoing[port] = true; + cpu_irq_restore(flags); + + b_short_packet = (udi_cdc_tx_buf_nb[port][buf_sel_trans] != UDI_CDC_TX_BUFFERS); + if (b_short_packet) { + if (udd_is_high_speed()) { + udi_cdc_tx_sof_num[port] = udd_get_micro_frame_number(); + }else{ + udi_cdc_tx_sof_num[port] = udd_get_frame_number(); + } + }else{ + udi_cdc_tx_sof_num[port] = 0; // Force next transfer without wait SOF + } + + // Send the buffer with enable of short packet + switch (port) { +#define UDI_CDC_PORT_TO_DATA_EP_IN(index, unused) \ + case index: \ + ep = UDI_CDC_DATA_EP_IN_##index; \ + break; + MREPEAT(UDI_CDC_PORT_NB, UDI_CDC_PORT_TO_DATA_EP_IN, ~) +#undef UDI_CDC_PORT_TO_DATA_EP_IN + default: + ep = UDI_CDC_DATA_EP_IN_0; + break; + } + udd_ep_run( ep, + b_short_packet, + udi_cdc_tx_buf[port][buf_sel_trans], + udi_cdc_tx_buf_nb[port][buf_sel_trans], + udi_cdc_data_sent); +} + + +// ------------------------ +//------- Application interface + + +//------- Application interface + +void udi_cdc_ctrl_signal_dcd(bool b_set) +{ + udi_cdc_ctrl_state_change(0, b_set, CDC_SERIAL_STATE_DCD); +} + +void udi_cdc_ctrl_signal_dsr(bool b_set) +{ + udi_cdc_ctrl_state_change(0, b_set, CDC_SERIAL_STATE_DSR); +} + +void udi_cdc_signal_framing_error(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_FRAMING); +} + +void udi_cdc_signal_parity_error(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_PARITY); +} + +void udi_cdc_signal_overrun(void) +{ + udi_cdc_ctrl_state_change(0, true, CDC_SERIAL_STATE_OVERRUN); +} + +void udi_cdc_multi_ctrl_signal_dcd(uint8_t port, bool b_set) +{ + udi_cdc_ctrl_state_change(port, b_set, CDC_SERIAL_STATE_DCD); +} + +void udi_cdc_multi_ctrl_signal_dsr(uint8_t port, bool b_set) +{ + udi_cdc_ctrl_state_change(port, b_set, CDC_SERIAL_STATE_DSR); +} + +void udi_cdc_multi_signal_framing_error(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_FRAMING); +} + +void udi_cdc_multi_signal_parity_error(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_PARITY); +} + +void udi_cdc_multi_signal_overrun(uint8_t port) +{ + udi_cdc_ctrl_state_change(port, true, CDC_SERIAL_STATE_OVERRUN); +} + +iram_size_t udi_cdc_multi_get_nb_received_data(uint8_t port) +{ + irqflags_t flags; + uint16_t pos; + iram_size_t nb_received; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + flags = cpu_irq_save(); + pos = udi_cdc_rx_pos[port]; + nb_received = udi_cdc_rx_buf_nb[port][udi_cdc_rx_buf_sel[port]] - pos; + cpu_irq_restore(flags); + return nb_received; +} + +iram_size_t udi_cdc_get_nb_received_data(void) +{ + return udi_cdc_multi_get_nb_received_data(0); +} + +bool udi_cdc_multi_is_rx_ready(uint8_t port) +{ + return (udi_cdc_multi_get_nb_received_data(port) > 0); +} + +bool udi_cdc_is_rx_ready(void) +{ + return udi_cdc_multi_is_rx_ready(0); +} + +int udi_cdc_multi_getc(uint8_t port) +{ + irqflags_t flags; + int rx_data = 0; + bool b_databit_9; + uint16_t pos; + uint8_t buf_sel; + bool again; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + b_databit_9 = (9 == udi_cdc_line_coding[port].bDataBits); + +udi_cdc_getc_process_one_byte: + // Check available data + flags = cpu_irq_save(); + pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + again = pos >= udi_cdc_rx_buf_nb[port][buf_sel]; + cpu_irq_restore(flags); + while (again) { + if (!udi_cdc_data_running) { + return 0; + } + goto udi_cdc_getc_process_one_byte; + } + + // Read data + rx_data |= udi_cdc_rx_buf[port][buf_sel][pos]; + udi_cdc_rx_pos[port] = pos+1; + + udi_cdc_rx_start(port); + + if (b_databit_9) { + // Receive MSB + b_databit_9 = false; + rx_data = rx_data << 8; + goto udi_cdc_getc_process_one_byte; + } + return rx_data; +} + +int udi_cdc_getc(void) +{ + return udi_cdc_multi_getc(0); +} + +iram_size_t udi_cdc_multi_read_buf(uint8_t port, void* buf, iram_size_t size) +{ + irqflags_t flags; + uint8_t *ptr_buf = (uint8_t *)buf; + iram_size_t copy_nb; + uint16_t pos; + uint8_t buf_sel; + bool again; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + +udi_cdc_read_buf_loop_wait: + // Check available data + flags = cpu_irq_save(); + pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + again = pos >= udi_cdc_rx_buf_nb[port][buf_sel]; + cpu_irq_restore(flags); + while (again) { + if (!udi_cdc_data_running) { + return size; + } + goto udi_cdc_read_buf_loop_wait; + } + + // Read data + copy_nb = udi_cdc_rx_buf_nb[port][buf_sel] - pos; + if (copy_nb>size) { + copy_nb = size; + } + memcpy(ptr_buf, &udi_cdc_rx_buf[port][buf_sel][pos], copy_nb); + udi_cdc_rx_pos[port] += copy_nb; + ptr_buf += copy_nb; + size -= copy_nb; + udi_cdc_rx_start(port); + + if (size) { + goto udi_cdc_read_buf_loop_wait; + } + return 0; +} + +static iram_size_t udi_cdc_multi_read_no_polling(uint8_t port, void* buf, iram_size_t size) +{ + uint8_t *ptr_buf = (uint8_t *)buf; + iram_size_t nb_avail_data; + uint16_t pos; + uint8_t buf_sel; + irqflags_t flags; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + //Data interface not started... exit + if (!udi_cdc_data_running) { + return 0; + } + + //Get number of available data + // Check available data + flags = cpu_irq_save(); // to protect udi_cdc_rx_pos & udi_cdc_rx_buf_sel + pos = udi_cdc_rx_pos[port]; + buf_sel = udi_cdc_rx_buf_sel[port]; + nb_avail_data = udi_cdc_rx_buf_nb[port][buf_sel] - pos; + cpu_irq_restore(flags); + //If the buffer contains less than the requested number of data, + //adjust read size + if(nb_avail_data0) { + memcpy(ptr_buf, &udi_cdc_rx_buf[port][buf_sel][pos], size); + flags = cpu_irq_save(); // to protect udi_cdc_rx_pos + udi_cdc_rx_pos[port] += size; + cpu_irq_restore(flags); + + ptr_buf += size; + udi_cdc_rx_start(port); + } + return(nb_avail_data); +} + +iram_size_t udi_cdc_read_no_polling(void* buf, iram_size_t size) +{ + return udi_cdc_multi_read_no_polling(0, buf, size); +} + +iram_size_t udi_cdc_read_buf(void* buf, iram_size_t size) +{ + return udi_cdc_multi_read_buf(0, buf, size); +} + +iram_size_t __attribute__((optimize("O0"))) udi_cdc_multi_get_free_tx_buffer(uint8_t port) +{ + irqflags_t flags; + iram_size_t buf_sel_nb, retval; + uint8_t buf_sel; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + flags = cpu_irq_save(); + buf_sel = udi_cdc_tx_buf_sel[port]; + buf_sel_nb = udi_cdc_tx_buf_nb[port][buf_sel]; + if (buf_sel_nb == UDI_CDC_TX_BUFFERS) { + if ((!udi_cdc_tx_trans_ongoing[port]) + && (!udi_cdc_tx_both_buf_to_send[port])) { + /* One buffer is full, but the other buffer is not used. + * (not used = transfer on-going) + * then move to the other buffer to store data */ + udi_cdc_tx_both_buf_to_send[port] = true; + udi_cdc_tx_buf_sel[port] = (buf_sel == 0)? 1 : 0; + buf_sel_nb = 0; + } + } + retval = UDI_CDC_TX_BUFFERS - buf_sel_nb; + cpu_irq_restore(flags); + return retval; +} + +iram_size_t udi_cdc_get_free_tx_buffer(void) +{ + return udi_cdc_multi_get_free_tx_buffer(0); +} + +bool udi_cdc_multi_is_tx_ready(uint8_t port) +{ + return (udi_cdc_multi_get_free_tx_buffer(port) != 0); +} + +bool udi_cdc_is_tx_ready(void) +{ + return udi_cdc_multi_is_tx_ready(0); +} + +int udi_cdc_multi_putc(uint8_t port, int value) +{ + irqflags_t flags; + bool b_databit_9; + uint8_t buf_sel; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + b_databit_9 = (9 == udi_cdc_line_coding[port].bDataBits); + +udi_cdc_putc_process_one_byte: + // Check available space + if (!udi_cdc_multi_is_tx_ready(port)) { + if (!udi_cdc_data_running) { + return false; + } + goto udi_cdc_putc_process_one_byte; + } + + // Write value + flags = cpu_irq_save(); + buf_sel = udi_cdc_tx_buf_sel[port]; + udi_cdc_tx_buf[port][buf_sel][udi_cdc_tx_buf_nb[port][buf_sel]++] = value; + cpu_irq_restore(flags); + + if (b_databit_9) { + // Send MSB + b_databit_9 = false; + value = value >> 8; + goto udi_cdc_putc_process_one_byte; + } + return true; +} + +int udi_cdc_putc(int value) +{ + return udi_cdc_multi_putc(0, value); +} + +iram_size_t __attribute__((optimize("O0"))) udi_cdc_multi_write_buf(uint8_t port, const void* buf, iram_size_t size) +{ + irqflags_t flags; + uint8_t buf_sel; + uint16_t buf_nb; + iram_size_t copy_nb; + uint8_t *ptr_buf = (uint8_t *)buf; + +#if UDI_CDC_PORT_NB == 1 // To optimize code + port = 0; +#endif + + if (9 == udi_cdc_line_coding[port].bDataBits) { + size *=2; + } + +udi_cdc_write_buf_loop_wait: + // Check available space + if (!udi_cdc_multi_is_tx_ready(port)) { + if (!udi_cdc_data_running) { + return size; + } + goto udi_cdc_write_buf_loop_wait; + } + + // Write values + flags = cpu_irq_save(); + buf_sel = udi_cdc_tx_buf_sel[port]; + buf_nb = udi_cdc_tx_buf_nb[port][buf_sel]; + copy_nb = UDI_CDC_TX_BUFFERS - buf_nb; + if (copy_nb > size) { + copy_nb = size; + } + memcpy(&udi_cdc_tx_buf[port][buf_sel][buf_nb], ptr_buf, copy_nb); + udi_cdc_tx_buf_nb[port][buf_sel] = buf_nb + copy_nb; + cpu_irq_restore(flags); + + // Update buffer pointer + ptr_buf = ptr_buf + copy_nb; + size -= copy_nb; + + if (size) { + goto udi_cdc_write_buf_loop_wait; + } + + return 0; +} + +iram_size_t udi_cdc_write_buf(const void* buf, iram_size_t size) +{ + return udi_cdc_multi_write_buf(0, buf, size); +} + +//@} + +#endif // ARDUINO_ARCH_SAM -- cgit v1.2.3