A newer serial port driver for the AT91 atmel processor. Note that I have only tested this on a custom card, so your mileage might vary. I also don't think that this driver is perfect: I still drop occasional chars when communicating with a PC (~1 every 200k chars @ 38400 baud). But that is dramatically better than I get with the default driver, which drops characters regularily at that baud rate. Note that this driver requires that the following be added to the serial .cdl file: implements CYGINT_IO_SERIAL_BLOCK_TRANSFER for each of the supported devices (eg CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL0 CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1) Next, you need to add a few defines to hal/arm/at91/current/include/plf_io.h I can't remember what I added, so the following is the relevant section from my version of the file: #define AT91_US_CR 0x00 // Control register #define AT91_US_CR_RxRESET (1<<2) #define AT91_US_CR_TxRESET (1<<3) #define AT91_US_CR_RxENAB (1<<4) #define AT91_US_CR_RxDISAB (1<<5) #define AT91_US_CR_TxENAB (1<<6) #define AT91_US_CR_TxDISAB (1<<7) #define AT91_US_CR_RSTATUS (1<<8) #define AT91_US_MR 0x04 // Mode register #define AT91_US_MR_CLOCK 4 #define AT91_US_MR_CLOCK_MCK (0< dev_name (eg at91) MY_DEV -> DEV_NAME (eg AT91) This version is heavily based on the version posted by Paul Sheer some time ago. Happy hacking... jr ============================================================= ============================================================= ============================================================= //========================================================================== // // io/serial/arm/my_dev/my_dev_serial.c // // Atmel MY_DEV/EB40 Serial I/O Interface Module (interrupt driven) // //========================================================================== //####COPYRIGHTBEGIN#### // // ------------------------------------------- // The contents of this file are subject to the Red Hat eCos Public License // Version 1.1 (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.redhat.com/ // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations under // the License. // // The Original Code is eCos - Embedded Configurable Operating System, // released September 30, 1998. // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are // Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // //####COPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas, Paul Sheer, John Recker // Contributors: gthomas // Date: 2001-07-24 // Purpose: Atmel MY_DEV/EB40 Serial I/O module (interrupt driven version) // Description: // //####DESCRIPTIONEND#### // //========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV #include "my_dev_serial.h" #define MY_DEV_UART_BUF_SIZE 256 #define MY_DEV_UART_RECV_BUF_COUNT 16 #define MY_DEV_UART_RECV_BUF_MASK 15 #define MY_DEV_RECV_UART_CHAR_TIMEOUT 20 typedef struct flip_buffer { cyg_uint8 buf[MY_DEV_UART_BUF_SIZE]; cyg_uint8 *outp; int count; } flip_buffer; typedef struct my_dev_pdc_serial_info { CYG_ADDRWORD base; CYG_WORD int_num; cyg_interrupt serial_interrupt; cyg_handle_t serial_interrupt_handle; cyg_interrupt line_interrupt; cyg_handle_t line_interrupt_handle; cyg_mutex_t DSR_mutex; cyg_sem_t flip_semaphore; int send_buf_which; flip_buffer send_buf[2]; int recv_buf_in_index; int recv_buf_out_index; flip_buffer recv_buf[MY_DEV_UART_RECV_BUF_COUNT]; int tx_enabled; cyg_uint32 stat; cyg_serial_info_t config; } my_dev_pdc_serial_info; static bool my_dev_pdc_serial_init(struct cyg_devtab_entry *tab); static bool my_dev_pdc_serial_putc(serial_channel *chan, unsigned char c); static Cyg_ErrNo my_dev_pdc_serial_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, const char *name); static unsigned char my_dev_pdc_serial_getc(serial_channel *chan); static Cyg_ErrNo my_dev_pdc_serial_set_config(serial_channel *chan, cyg_uint32 key, const void *xbuf, cyg_uint32 *len); static void my_dev_pdc_serial_start_xmit(serial_channel *chan); static void my_dev_pdc_serial_stop_xmit(serial_channel *chan); static cyg_uint32 my_dev_pdc_serial_ISR(cyg_vector_t vector, cyg_addrword_t data); static void my_dev_pdc_serial_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data); static SERIAL_FUNS(my_dev_pdc_serial_funs, my_dev_pdc_serial_putc, my_dev_pdc_serial_getc, my_dev_pdc_serial_set_config, my_dev_pdc_serial_start_xmit, my_dev_pdc_serial_stop_xmit ); #ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL0 static my_dev_pdc_serial_info my_dev_pdc_serial_info0 = {(CYG_ADDRWORD)MY_DEV_USART0, CYGNUM_HAL_INTERRUPT_USART0}; #if CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE > 0 static unsigned char my_dev_pdc_serial_out_buf0[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE]; static unsigned char my_dev_pdc_serial_in_buf0[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BUFSIZE]; static SERIAL_CHANNEL_USING_INTERRUPTS(my_dev_pdc_serial_channel0, my_dev_pdc_serial_funs, my_dev_pdc_serial_info0, CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BAUD), CYG_SERIAL_STOP_DEFAULT, CYG_SERIAL_PARITY_DEFAULT, CYG_SERIAL_WORD_LENGTH_DEFAULT, CYG_SERIAL_FLAGS_DEFAULT, &my_dev_pdc_serial_out_buf0[0], sizeof(my_dev_pdc_serial_out_buf0), &my_dev_pdc_serial_in_buf0[0], sizeof(my_dev_pdc_serial_in_buf0) ); #else static SERIAL_CHANNEL(my_dev_pdc_serial_channel0, my_dev_pdc_serial_funs, my_dev_pdc_serial_info0, CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL0_BAUD), CYG_SERIAL_STOP_DEFAULT, CYG_SERIAL_PARITY_DEFAULT, CYG_SERIAL_WORD_LENGTH_DEFAULT, CYG_SERIAL_FLAGS_DEFAULT ); #endif DEVTAB_ENTRY(my_dev_pdc_serial_io0, CYGDAT_IO_SERIAL_ARM_MY_DEV_SERIAL0_NAME, 0, // Does not depend on a lower level interface &cyg_io_serial_devio, my_dev_pdc_serial_init, my_dev_pdc_serial_lookup, // Serial driver may need initializing &my_dev_pdc_serial_channel0 ); #endif // CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1 #ifdef CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1 static my_dev_pdc_serial_info my_dev_pdc_serial_info1 = {(CYG_ADDRWORD)MY_DEV_USART1, CYGNUM_HAL_INTERRUPT_USART1}; #if CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE > 0 static unsigned char my_dev_pdc_serial_out_buf1[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE]; static unsigned char my_dev_pdc_serial_in_buf1[CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BUFSIZE]; static SERIAL_CHANNEL_USING_INTERRUPTS(my_dev_pdc_serial_channel1, my_dev_pdc_serial_funs, my_dev_pdc_serial_info1, CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BAUD), CYG_SERIAL_STOP_DEFAULT, CYG_SERIAL_PARITY_DEFAULT, CYG_SERIAL_WORD_LENGTH_DEFAULT, CYG_SERIAL_FLAGS_DEFAULT, &my_dev_pdc_serial_out_buf1[0], sizeof(my_dev_pdc_serial_out_buf1), &my_dev_pdc_serial_in_buf1[0], sizeof(my_dev_pdc_serial_in_buf1) ); #else static SERIAL_CHANNEL(my_dev_pdc_serial_channel1, my_dev_pdc_serial_funs, my_dev_pdc_serial_info1, CYG_SERIAL_BAUD_RATE(CYGNUM_IO_SERIAL_ARM_MY_DEV_SERIAL1_BAUD), CYG_SERIAL_STOP_DEFAULT, CYG_SERIAL_PARITY_DEFAULT, CYG_SERIAL_WORD_LENGTH_DEFAULT, CYG_SERIAL_FLAGS_DEFAULT ); #endif DEVTAB_ENTRY(my_dev_pdc_serial_io1, CYGDAT_IO_SERIAL_ARM_MY_DEV_SERIAL1_NAME, 0, // Does not depend on a lower level interface &cyg_io_serial_devio, my_dev_pdc_serial_init, my_dev_pdc_serial_lookup, // Serial driver may need initializing &my_dev_pdc_serial_channel1 ); #endif // CYGPKG_IO_SERIAL_ARM_MY_DEV_SERIAL1 // Internal function to actually configure the hardware to desired baud rate, etc. static bool my_dev_pdc_serial_config_port(serial_channel *chan, cyg_serial_info_t *new_config, bool init) { my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv; CYG_ADDRWORD base = my_dev_pdc_chan->base; cyg_uint32 parity = select_parity[new_config->parity]; cyg_uint32 word_length = select_word_length[new_config->word_length-CYGNUM_SERIAL_WORD_LENGTH_5]; cyg_uint32 stop_bits = select_stop_bits[new_config->stop]; if ((word_length == 0xFF) || (parity == 0xFF) || (stop_bits == 0xFF)) { return false; // Unsupported configuration } if (init) { int i; memset (&my_dev_pdc_chan->config, 0, sizeof (my_dev_pdc_chan->config)); my_dev_pdc_chan->send_buf_which = 0; my_dev_pdc_chan->send_buf[0].count = my_dev_pdc_chan->send_buf[1].count = 0; my_dev_pdc_chan->tx_enabled = 0; my_dev_pdc_chan->recv_buf_in_index = 0; my_dev_pdc_chan->recv_buf_out_index = 0; for (i = 0; i < MY_DEV_UART_RECV_BUF_COUNT; i++) my_dev_pdc_chan->recv_buf[i].count = 0; } if (new_config->word_length != my_dev_pdc_chan->config.word_length || new_config->parity != my_dev_pdc_chan->config.parity || new_config->stop != my_dev_pdc_chan->config.stop || new_config->baud != my_dev_pdc_chan->config.baud) { // Reset device HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_RxRESET | MY_DEV_US_CR_TxRESET); // Configuration HAL_WRITE_UINT32(base+MY_DEV_US_MR, parity | word_length | stop_bits); // Baud rate HAL_WRITE_UINT32(base+MY_DEV_US_BRG, MY_DEV_US_BAUD(select_baud[new_config->baud])); // Enable RX and TX HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_RxENAB | MY_DEV_US_CR_TxENAB); my_dev_pdc_chan->config = *new_config; } if (init) { // Disable all interrupts HAL_WRITE_UINT32(base+MY_DEV_US_IDR, 0xFFFFFFFF); // Initialize hardware buffers HAL_WRITE_UINT32 (base + MY_DEV_US_RPR, (cyg_uint32) my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index].buf); HAL_WRITE_UINT32 (base + MY_DEV_US_RCR, (cyg_uint32) MY_DEV_UART_BUF_SIZE); HAL_WRITE_UINT32 (base + MY_DEV_US_TCR, (cyg_uint32) 0); HAL_WRITE_UINT32 (base + MY_DEV_US_TPR, (cyg_uint32) my_dev_pdc_chan->send_buf[my_dev_pdc_chan->send_buf_which].buf); // Enable buffer full and timeout interrupts HAL_WRITE_UINT32(base+MY_DEV_US_IER, MY_DEV_US_IER_TIMEOUT | MY_DEV_US_IER_ENDRX); } // Start timeout HAL_WRITE_UINT32(base+MY_DEV_US_RTO, MY_DEV_RECV_UART_CHAR_TIMEOUT); HAL_WRITE_UINT32(base+MY_DEV_US_CR, MY_DEV_US_CR_STTTO); return true; } // Function to initialize the device. Called at bootstrap time. static bool my_dev_pdc_serial_init(struct cyg_devtab_entry *tab) { serial_channel *chan = (serial_channel *)tab->priv; my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv; int res; #ifdef CYGDBG_IO_INIT diag_printf("MY_DEV SERIAL init - dev: %x.%d\n", my_dev_pdc_chan->base, my_dev_pdc_chan->int_num); #endif cyg_mutex_init(&my_dev_pdc_chan->DSR_mutex); cyg_semaphore_init(&my_dev_pdc_chan->flip_semaphore, 1); (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices if (chan->out_cbuf.len != 0) { cyg_drv_interrupt_create(my_dev_pdc_chan->int_num, 4, // Priority (cyg_addrword_t)chan, // Data item passed to interrupt handler my_dev_pdc_serial_ISR, my_dev_pdc_serial_DSR, &my_dev_pdc_chan->serial_interrupt_handle, &my_dev_pdc_chan->serial_interrupt); cyg_drv_interrupt_attach(my_dev_pdc_chan->serial_interrupt_handle); cyg_drv_interrupt_unmask(my_dev_pdc_chan->int_num); } res = my_dev_pdc_serial_config_port(chan, &chan->config, true); return res; } // This routine is called when the device is "looked" up (i.e. attached) static Cyg_ErrNo my_dev_pdc_serial_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, const char *name) { serial_channel *chan = (serial_channel *)(*tab)->priv; (chan->callbacks->serial_init)(chan); // Really only required for interrupt driven devices return ENOERR; } static void my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_serial_info * my_dev_pdc_chan) { CYG_ADDRWORD base = my_dev_pdc_chan->base; cyg_uint32 count; int other; HAL_READ_UINT32 (base + MY_DEV_US_TCR, count); if (count > 0) { // transmitter still busy HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX); } else { // transmitter done with old buf // Avoid conflicts w/ putc, DSR... if (!cyg_semaphore_trywait(&my_dev_pdc_chan->flip_semaphore)) { // If someone else is messing w/ send buffers, let them re-enable interrupts HAL_WRITE_UINT32 (base + MY_DEV_US_IDR, MY_DEV_US_IER_ENDTX); return; } // empty buffer - my_dev_pdc_chan->send_buf[my_dev_pdc_chan->send_buf_which].count = 0; // flip - other = 1 - my_dev_pdc_chan->send_buf_which; if (my_dev_pdc_chan->send_buf[other].count > 0) { my_dev_pdc_chan->send_buf_which = other; HAL_WRITE_UINT32 (base + MY_DEV_US_TPR, (cyg_uint32) my_dev_pdc_chan->send_buf[other].buf); HAL_WRITE_UINT32 (base + MY_DEV_US_TCR, (cyg_uint32) my_dev_pdc_chan->send_buf[other].count); // enable completion interrupt - HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX); } else { // Nothing to do, disable interrupts HAL_WRITE_UINT32 (base + MY_DEV_US_IDR, MY_DEV_US_IER_ENDTX); } // release semaphore cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore); } } // Send a character to the device output buffer. // Return 'true' if character is sent to device static bool my_dev_pdc_serial_putc (serial_channel * chan, unsigned char c) { my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv; int other; // avoid re-entrancy problems cyg_semaphore_wait(&my_dev_pdc_chan->flip_semaphore); other = 1 - my_dev_pdc_chan->send_buf_which; if (my_dev_pdc_chan->send_buf[other].count >= MY_DEV_UART_BUF_SIZE) return false; // append buffer with char my_dev_pdc_chan->send_buf[other].buf[my_dev_pdc_chan->send_buf[other].count] = (cyg_uint8) c; my_dev_pdc_chan->send_buf[other].count++; // release semaphore cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore); // flip if needed: my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan); return true; } // we don't support this - its only for bufferless operation: see serial.c static unsigned char my_dev_pdc_serial_getc(serial_channel *chan) { return 0; } // Set up the device characteristics; baud rate, etc. static Cyg_ErrNo my_dev_pdc_serial_set_config(serial_channel *chan, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) { switch (key) { case CYG_IO_SET_CONFIG_SERIAL_INFO: { cyg_serial_info_t *config = (cyg_serial_info_t *)xbuf; if ( *len < sizeof(cyg_serial_info_t) ) { return -EINVAL; } *len = sizeof(cyg_serial_info_t); if ( true != my_dev_pdc_serial_config_port(chan, config, false) ) return -EINVAL; } break; default: return -EINVAL; } return ENOERR; } // Enable the transmitter on the device static void my_dev_pdc_serial_start_xmit (serial_channel * chan) { my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv; CYG_ADDRWORD base = my_dev_pdc_chan->base; cyg_uint32 count; HAL_READ_UINT32 (base + MY_DEV_US_TCR, count); if (count > 0) HAL_WRITE_UINT32 (base + MY_DEV_US_IER, MY_DEV_US_IER_ENDTX); my_dev_pdc_chan->tx_enabled = 1; (chan->callbacks->xmt_char) (chan); } // Disable the transmitter on the device static void my_dev_pdc_serial_stop_xmit(serial_channel *chan) { my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *)chan->dev_priv; my_dev_pdc_chan->tx_enabled = 0; } // Serial I/O - low level interrupt handler (ISR) static cyg_uint32 my_dev_pdc_serial_ISR (cyg_vector_t vector, cyg_addrword_t data) { serial_channel *chan = (serial_channel *) data; my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv; CYG_ADDRWORD base = my_dev_pdc_chan->base; cyg_uint32 ipr, imr, p; cyg_uint32 count; cyg_drv_interrupt_mask (vector); HAL_READ_UINT32 (base + MY_DEV_US_CSR, ipr); HAL_READ_UINT32 (base + MY_DEV_US_IMR, imr); if (ipr & (MY_DEV_US_CSR_OVRE // Rx overflow | MY_DEV_US_CSR_FRAME)) { // Rx framing error HAL_WRITE_UINT32 (base + MY_DEV_US_CR, MY_DEV_US_CR_RSTATUS); } if (ipr & imr & (MY_DEV_US_IER_ENDRX | MY_DEV_US_IER_TIMEOUT)) { struct flip_buffer *recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index]; HAL_READ_UINT32 (base + MY_DEV_US_RPR, p); count = p - (cyg_uint32) recv->buf; if (count > 0) { // PDC has done work /* flip the buffer */ recv->count = count; recv->outp = recv->buf; my_dev_pdc_chan->recv_buf_in_index = ++my_dev_pdc_chan->recv_buf_in_index & MY_DEV_UART_RECV_BUF_MASK; HAL_WRITE_UINT32 (base + MY_DEV_US_RPR, (cyg_uint32) my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_in_index].buf); HAL_WRITE_UINT32 (base + MY_DEV_US_RCR, (cyg_uint32) MY_DEV_UART_BUF_SIZE); } HAL_WRITE_UINT32 (base + MY_DEV_US_RTO, MY_DEV_RECV_UART_CHAR_TIMEOUT); HAL_WRITE_UINT32 (base + MY_DEV_US_CR, MY_DEV_US_CR_STTTO); } if ((ipr & imr & MY_DEV_US_IER_ENDTX)) { my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan); } cyg_drv_interrupt_acknowledge (vector); cyg_drv_interrupt_unmask (vector); return CYG_ISR_CALL_DSR | CYG_ISR_HANDLED; } // Serial I/O - high level interrupt handler (DSR) static void my_dev_pdc_serial_DSR (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) { serial_channel *chan = (serial_channel *) data; my_dev_pdc_serial_info *my_dev_pdc_chan = (my_dev_pdc_serial_info *) chan->dev_priv; // prevent re-entrancy problems.... if (!cyg_mutex_lock(&my_dev_pdc_chan->DSR_mutex)) return; // Check status if (my_dev_pdc_chan->tx_enabled) { int count, other; unsigned char *p; // Avoid conflicts w/ putc, ISR... if (cyg_semaphore_trywait(&my_dev_pdc_chan->flip_semaphore)) { other = 1 - my_dev_pdc_chan->send_buf_which; if (CYG_XMT_OK == (chan->callbacks->data_xmt_req) (chan, MY_DEV_UART_BUF_SIZE - my_dev_pdc_chan->send_buf[other].count, &count, &p)) { do { if (count <= 0) break; memcpy (my_dev_pdc_chan->send_buf[other].buf + my_dev_pdc_chan->send_buf[other].count, p, count); my_dev_pdc_chan->send_buf[other].count += count; (chan->callbacks->data_xmt_done) (chan, count); if (!(MY_DEV_UART_BUF_SIZE - my_dev_pdc_chan->send_buf[other].count)) break; } while ( ((my_dev_pdc_chan->send_buf[other].count - MY_DEV_UART_BUF_SIZE) > 0) && (CYG_RCV_OK == (chan->callbacks->data_xmt_req) (chan, MY_DEV_UART_BUF_SIZE - my_dev_pdc_chan->send_buf[other].count, &count, &p)) ); } else { (chan->callbacks->xmt_char) (chan); } // release semaphore cyg_semaphore_post(&my_dev_pdc_chan->flip_semaphore); // flip if needed: my_dev_pdc_serial_flip_send_buffer (my_dev_pdc_chan); } } if (my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index].count > 0) { // chars waiting from ISR struct flip_buffer *recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index]; int avail_count = recv->count; int count; unsigned char *p; if (CYG_RCV_OK == (chan->callbacks->data_rcv_req) (chan, avail_count, &count, &p)) { do { // block transfer acceptable to upper layer: memcpy (p, recv->outp, count); recv->outp += count; recv->count -= count; (chan->callbacks->data_rcv_done) (chan, count); if (recv->count <= 0) { my_dev_pdc_chan->recv_buf_out_index = ++my_dev_pdc_chan->recv_buf_out_index & MY_DEV_UART_RECV_BUF_MASK; recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index]; if (my_dev_pdc_chan->recv_buf_out_index == my_dev_pdc_chan->recv_buf_in_index) { break; } } avail_count = recv->count; } while (CYG_RCV_OK == (chan->callbacks->data_rcv_req) (chan, avail_count, &count, &p)); } else { // block transfer not acceptable to upper layer: while (1) { (chan->callbacks->rcv_char) (chan, (unsigned char)*recv->outp++); if (--recv->count <= 0) { my_dev_pdc_chan->recv_buf_out_index = ++my_dev_pdc_chan->recv_buf_out_index & MY_DEV_UART_RECV_BUF_MASK; recv = &my_dev_pdc_chan->recv_buf[my_dev_pdc_chan->recv_buf_out_index]; if (my_dev_pdc_chan->recv_buf_out_index == my_dev_pdc_chan->recv_buf_in_index) break; } } } } cyg_mutex_unlock(&my_dev_pdc_chan->DSR_mutex); } #endif