123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155 |
- /**
- * \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 <a href="https://www.atmel.com/design-support/">Atmel Support</a>
- */
-
- #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 <string.h>
-
- #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_data<size) {
- size = nb_avail_data;
- }
- if(size>0) {
- 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
|