/**
* \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