/** Write out data over the USART using DMA. Note that this function is not * reentrant and does not guard against DMA IRQs running at the same time which * will also cause spurious behaviours. Ensure that the calling function * appropriately prevents these things from happeing. * * \param data A pointer to the data to write out. * \param len The number of bytes to write. * * \return The number of bytes that will be written, may be less than len. */ u32 usart_write_dma(u8 data[], u32 len) { /* Check if the write would cause a buffer overflow, if so only write up to * the end of the buffer. */ u32 n_free = usart_tx_n_free(); if (len > n_free) len = n_free; /* If there is no data to write, just return. */ if (len == 0) return 0; u32 old_wr = wr; wr = (wr + len) % USART_TX_BUFFER_LEN; if (old_wr + len <= USART_TX_BUFFER_LEN) { memcpy(&buff[old_wr], data, len); } else { /* Deal with case where write wraps the buffer. */ memcpy(&buff[old_wr], &data[0], USART_TX_BUFFER_LEN - old_wr); memcpy(&buff[0], &data[USART_TX_BUFFER_LEN-old_wr], len - (USART_TX_BUFFER_LEN - old_wr)); } /* Check if there is a DMA transfer either in progress or waiting for its * interrupt to be serviced. Its very important to also check the interrupt * flag as EN will be cleared when the transfer finishes but we really need * to make sure the ISR has been run to finish up the bookkeeping for the * transfer. Also, make sure that this is done atomically without a DMA * interrupt squeezing in there. */ if (!((DMA2_S7CR & DMA_SxCR_EN) || (DMA2_HISR & DMA_HISR_TCIF7))) dma_schedule(); return len; }
u32 usart_support_write(void *sd, const u8 data[], u32 len) { struct usart_tx_dma_state *s = &((struct usart_support_s *)sd)->tx; /* If there is no data to write, just return. */ if (len == 0) return 0; chSysLock(); /* Check if the write would cause a buffer overflow, if so only write up to * the end of the buffer. */ u32 n_free = usart_tx_n_free(sd); if (len > n_free) { chSysUnlock(); return 0; } u32 old_wr = s->wr; s->wr = (s->wr + len) % USART_TX_BUFFER_LEN; if (old_wr + len <= USART_TX_BUFFER_LEN) memcpy(&(s->buff[old_wr]), data, len); else { /* Deal with case where write wraps the buffer. */ memcpy(&(s->buff[old_wr]), &data[0], USART_TX_BUFFER_LEN - old_wr); memcpy(&(s->buff[0]), &data[USART_TX_BUFFER_LEN - old_wr], len - (USART_TX_BUFFER_LEN - old_wr)); } /* Check if there is a DMA transfer either in progress or waiting for its * interrupt to be serviced. Its very important to also check the interrupt * flag as EN will be cleared when the transfer finishes but we really need * to make sure the ISR has been run to finish up the bookkeeping for the * transfer. Also, make sure that this is done atomically without a DMA * interrupt squeezing in there. */ if (!s->busy) dma_schedule(s); chSysUnlock(); return len; }