static int tx_stream_start(struct stream *stream, struct device *dev) { const struct i2s_stm32_cfg *cfg = DEV_CFG(dev); struct i2s_stm32_data *const dev_data = DEV_DATA(dev); size_t mem_block_size; int ret; ret = queue_get(&stream->mem_block_queue, &stream->mem_block, &mem_block_size); if (ret < 0) { return ret; } k_sem_give(&stream->sem); /* Assure cache coherency before DMA read operation */ DCACHE_CLEAN(stream->mem_block, mem_block_size); if (stream->master) { LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_MASTER_TX); } else { LL_I2S_SetTransferMode(cfg->i2s, LL_I2S_MODE_SLAVE_TX); } /* remember active TX DMA channel (used in callback) */ active_dma_tx_channel[stream->dma_channel] = dev; ret = start_dma(dev_data->dev_dma, stream->dma_channel, &stream->dma_cfg, stream->mem_block, (void *)LL_SPI_DMA_GetRegAddr(cfg->i2s), stream->cfg.block_size); if (ret < 0) { LOG_ERR("Failed to start TX DMA transfer: %d", ret); return ret; } LL_I2S_EnableDMAReq_TX(cfg->i2s); LL_I2S_EnableIT_ERR(cfg->i2s); LL_I2S_Enable(cfg->i2s); return 0; }
static int eth_tx(struct net_if *iface, struct net_pkt *pkt) { struct device *const dev = net_if_get_device(iface); const struct eth_sam_dev_cfg *const cfg = DEV_CFG(dev); struct eth_sam_dev_data *const dev_data = DEV_DATA(dev); Gmac *gmac = cfg->regs; struct gmac_queue *queue = &dev_data->queue_list[0]; struct gmac_desc_list *tx_desc_list = &queue->tx_desc_list; struct gmac_desc *tx_desc; struct net_buf *frag; u8_t *frag_data; u16_t frag_len; u32_t err_tx_flushed_count_at_entry = queue->err_tx_flushed_count; unsigned int key; __ASSERT(pkt, "buf pointer is NULL"); __ASSERT(pkt->frags, "Frame data missing"); SYS_LOG_DBG("ETH tx"); /* First fragment is special - it contains link layer (Ethernet * in our case) header. Modify the data pointer to account for more data * in the beginning of the buffer. */ net_buf_push(pkt->frags, net_pkt_ll_reserve(pkt)); frag = pkt->frags; while (frag) { frag_data = frag->data; frag_len = frag->len; /* Assure cache coherency before DMA read operation */ DCACHE_CLEAN(frag_data, frag_len); k_sem_take(&queue->tx_desc_sem, K_FOREVER); /* The following section becomes critical and requires IRQ lock * / unlock protection only due to the possibility of executing * tx_error_handler() function. */ key = irq_lock(); /* Check if tx_error_handler() function was executed */ if (queue->err_tx_flushed_count != err_tx_flushed_count_at_entry) { irq_unlock(key); return -EIO; } tx_desc = &tx_desc_list->buf[tx_desc_list->head]; /* Update buffer descriptor address word */ tx_desc->w0 = (u32_t)frag_data; /* Guarantee that address word is written before the status * word to avoid race condition. */ __DMB(); /* data memory barrier */ /* Update buffer descriptor status word (clear used bit) */ tx_desc->w1 = (frag_len & GMAC_TXW1_LEN) | (!frag->frags ? GMAC_TXW1_LASTBUFFER : 0) | (tx_desc_list->head == tx_desc_list->len - 1 ? GMAC_TXW1_WRAP : 0); /* Update descriptor position */ MODULO_INC(tx_desc_list->head, tx_desc_list->len); __ASSERT(tx_desc_list->head != tx_desc_list->tail, "tx_desc_list overflow"); irq_unlock(key); /* Continue with the rest of fragments (only data) */ frag = frag->frags; } key = irq_lock(); /* Check if tx_error_handler() function was executed */ if (queue->err_tx_flushed_count != err_tx_flushed_count_at_entry) { irq_unlock(key); return -EIO; } /* Ensure the descriptor following the last one is marked as used */ tx_desc = &tx_desc_list->buf[tx_desc_list->head]; tx_desc->w1 |= GMAC_TXW1_USED; /* Account for a sent frame */ ring_buf_put(&queue->tx_frames, POINTER_TO_UINT(pkt)); irq_unlock(key); /* Start transmission */ gmac->GMAC_NCR |= GMAC_NCR_TSTART; return 0; }
static void dma_tx_callback(void *arg, u32_t channel, int status) { struct device *dev = get_dev_from_tx_dma_channel(channel); const struct i2s_stm32_cfg *cfg = DEV_CFG(dev); struct i2s_stm32_data *const dev_data = DEV_DATA(dev); struct stream *stream = &dev_data->tx; size_t mem_block_size; int ret; if (status != 0) { ret = -EIO; stream->state = I2S_STATE_ERROR; goto tx_disable; } __ASSERT_NO_MSG(stream->mem_block != NULL); /* All block data sent */ k_mem_slab_free(stream->cfg.mem_slab, &stream->mem_block); stream->mem_block = NULL; /* Stop transmission if there was an error */ if (stream->state == I2S_STATE_ERROR) { LOG_ERR("TX error detected"); goto tx_disable; } /* Stop transmission if we were requested */ if (stream->last_block) { stream->state = I2S_STATE_READY; goto tx_disable; } /* Prepare to send the next data block */ ret = queue_get(&stream->mem_block_queue, &stream->mem_block, &mem_block_size); if (ret < 0) { if (stream->state == I2S_STATE_STOPPING) { stream->state = I2S_STATE_READY; } else { stream->state = I2S_STATE_ERROR; } goto tx_disable; } k_sem_give(&stream->sem); /* Assure cache coherency before DMA read operation */ DCACHE_CLEAN(stream->mem_block, mem_block_size); ret = reload_dma(dev_data->dev_dma, stream->dma_channel, &stream->dma_cfg, stream->mem_block, (void *)LL_SPI_DMA_GetRegAddr(cfg->i2s), stream->cfg.block_size); if (ret < 0) { LOG_DBG("Failed to start TX DMA transfer: %d", ret); goto tx_disable; } return; tx_disable: tx_stream_disable(stream, dev); }