日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Xilinx XDMA 数据传输sgdma 驱动代码分析

發布時間:2024/3/24 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Xilinx XDMA 数据传输sgdma 驱动代码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Xilinx XDMA 數據傳輸sgdma 驅動代碼分析

我的之前兩篇文章有介紹到上位機軟件的邏輯該如何控制,驅動代碼的框架是怎樣的,驅動的整體邏輯在linux系統中是如何實現的,感興趣的小伙伴可以去考古。
Xilinx XDMA 上位機應用程序控制邏輯
Xilinx XDMA驅動代碼分析及用法
XDMA 傳輸的核心部分代碼是cdev_sgdma.c ,利用DMA進行數據傳輸,傳輸方式為sgdma的傳輸方式,

1、SG-DMA介紹

Scatter-Gather DMA ,分散/集中映射是流式 DMA 映射的一個特例。它將幾個緩沖區集中到一起進行一次映射,并在一個 DMA 操作中傳送所有數據。這些分散的緩沖區由分散表結構scatterlist來描述,多個分散的緩沖區的分散表結構組成緩沖區的struct scatterlist數組。 [DMA技術和及其SG模式](https://blog.csdn.net/chinamaoge/article/details/104606865)

2、sgdma IO文件操作接口

cdev_sgdma.c 是xdma 驅動傳輸的主要接口,驅動正確安裝后會生成xdma0_c2h_0 xdma0_h2c_0 的讀寫設備。

上位機程序。通過文件io.read\write 調用驅動的read\write,以觸發sgdma進行數據傳輸,傳輸完成會有中斷,之余中斷時如何使用的,感興趣的朋友看一下一起的文章。

char_sgdma_read 、char_sgdma_write 兩個接口調用同一個接口進行數據輸出, 接口以一個bool變量驅動,數據是讀還是寫。

函數原型:

static ssize_t char_sgdma_read_write(struct file *file, const char __user *buf,size_t count, loff_t *pos, bool write)

函數的調用關系如圖

static ssize_t char_sgdma_read_write(struct file *file, const char __user *buf,size_t count, loff_t *pos, bool write) {int rv;ssize_t res = 0;struct xdma_cdev *xcdev = (struct xdma_cdev *)file->private_data;struct xdma_dev *xdev;struct xdma_engine *engine;struct xdma_io_cb cb;rv = xcdev_check(__func__, xcdev, 1);if (rv < 0)return rv;xdev = xcdev->xdev;engine = xcdev->engine;dbg_tfr("file 0x%p, priv 0x%p, buf 0x%p,%llu, pos %llu, W %d, %s.\n",file, file->private_data, buf, (u64)count, (u64)*pos, write,engine->name);if ((write && engine->dir != DMA_TO_DEVICE) ||(!write && engine->dir != DMA_FROM_DEVICE)) {pr_err("r/w mismatch. W %d, dir %d.\n",write, engine->dir);return -EINVAL;}rv = check_transfer_align(engine, buf, count, *pos, 1);if (rv) {pr_info("Invalid transfer alignment detected\n");return rv;}memset(&cb, 0, sizeof(struct xdma_io_cb));cb.buf = (char __user *)buf;cb.len = count;cb.ep_addr = (u64)*pos;cb.write = write;rv = char_sgdma_map_user_buf_to_sgl(&cb, write);if (rv < 0)return rv;res = xdma_xfer_submit(xdev, engine->channel, write, *pos, &cb.sgt,0, write ? h2c_timeout * 1000 :c2h_timeout * 1000);char_sgdma_unmap_user_buf(&cb, write);return res; }

這個接口的主要的邏輯就是:

  • 數據對齊檢測:check_transfer_align,主要是檢查AXi 總線 non-incremental addressing mode
  • 進行數據的地址映射:char_sgdma_map_user_buf_to_sgl
  • 數據傳輸:xdma_xfer_submit
  • unmap: char_sgdma_unmap_user_buf
  • 我們先看char_sgdma_map_user_buf_to_sgl()

    static int char_sgdma_map_user_buf_to_sgl(struct xdma_io_cb *cb, bool write) {struct sg_table *sgt = &cb->sgt;unsigned long len = cb->len;void __user *buf = cb->buf;struct scatterlist *sg; /* sgdma 傳輸所需的 scatterlist *//* 會先算出傳輸的數據有多少個Page */unsigned int pages_nr = (((unsigned long)buf + len + PAGE_SIZE - 1) -((unsigned long)buf & PAGE_MASK))>> PAGE_SHIFT;int i;int rv;if (pages_nr == 0)return -EINVAL;/*更具pages的個數,申請所需的sg_table,單個scatterlist 是沒有意義的,*//*需要多個scatterlist組成一個數組,以表示在物理上不連續的虛擬地址空間*/if (sg_alloc_table(sgt, pages_nr, GFP_KERNEL)) {pr_err("sgl OOM.\n");return -ENOMEM;}cb->pages = kcalloc(pages_nr, sizeof(struct page *), GFP_KERNEL);if (!cb->pages) {pr_err("pages OOM.\n");rv = -ENOMEM;goto err_out;}/*get_user_pages_fast調用這個函數進行獲取當前進程的頁,這個函數不鎖mm*/rv = get_user_pages_fast((unsigned long)buf, pages_nr, 1/* write */,cb->pages);/* No pages were pinned */if (rv < 0) {pr_err("unable to pin down %u user pages, %d.\n",pages_nr, rv);goto err_out;}/* Less pages pinned than wanted */if (rv != pages_nr) {pr_err("unable to pin down all %u user pages, %d.\n",pages_nr, rv);cb->pages_nr = rv;rv = -EFAULT;goto err_out;}for (i = 1; i < pages_nr; i++) {if (cb->pages[i - 1] == cb->pages[i]) {pr_err("duplicate pages, %d, %d.\n",i - 1, i);rv = -EFAULT;cb->pages_nr = pages_nr;goto err_out;}}/*將page相關的data cache內容寫回到內存*//*將page中指定offset、指定長度的內存賦給指定的scatterlist*/sg = sgt->sgl;for (i = 0; i < pages_nr; i++, sg = sg_next(sg)) {unsigned int offset = offset_in_page(buf);unsigned int nbytes =min_t(unsigned int, PAGE_SIZE - offset, len);flush_dcache_page(cb->pages[i]);sg_set_page(sg, cb->pages[i], nbytes, offset);buf += nbytes;len -= nbytes;}if (len) {pr_err("Invalid user buffer length. Cannot map to sgl\n");return -EINVAL;}cb->pages_nr = pages_nr;return 0;err_out:char_sgdma_unmap_user_buf(cb, write);return rv; }

    這是將用戶層的數據進行映射,接下來就是數據的傳輸

    xdma_xfer_submit:

    xdma_xfer_submit 會調用 transform_queue進行數據傳輸,它將數據Add 到DMA的傳輸表中,進行數據傳輸。

    static int transfer_queue(struct xdma_engine *engine,struct xdma_transfer *transfer) {int rv = 0;struct xdma_transfer *transfer_started;struct xdma_dev *xdev;unsigned long flags;if (!engine) {pr_err("dma engine NULL\n");return -EINVAL;}if (!engine->xdev) {pr_err("Invalid xdev\n");return -EINVAL;}if (!transfer) {pr_err("%s Invalid DMA transfer\n", engine->name);return -EINVAL;}if (transfer->desc_num == 0) {pr_err("%s void descriptors in the transfer list\n",engine->name);return -EINVAL;}dbg_tfr("%s (transfer=0x%p).\n", __func__, transfer);xdev = engine->xdev;if (xdma_device_flag_check(xdev, XDEV_FLAG_OFFLINE)) {pr_info("dev 0x%p offline, transfer 0x%p not queued.\n", xdev,transfer);return -EBUSY;}/* lock the engine state */spin_lock_irqsave(&engine->lock, flags);engine->prev_cpu = get_cpu();put_cpu();/* engine is being shutdown; do not accept new transfers */if (engine->shutdown & ENGINE_SHUTDOWN_REQUEST) {pr_info("engine %s offline, transfer 0x%p not queued.\n",engine->name, transfer);rv = -EBUSY;goto shutdown;}/* mark the transfer as submitted */transfer->state = TRANSFER_STATE_SUBMITTED;/* add transfer to the tail of the engine transfer queue */list_add_tail(&transfer->entry, &engine->transfer_list);/* engine is idle? */if (!engine->running) {/* start engine */dbg_tfr("%s(): starting %s engine.\n", __func__, engine->name);transfer_started = engine_start(engine);if (!transfer_started) {pr_err("Failed to start dma engine\n");goto shutdown;}dbg_tfr("transfer=0x%p started %s engine with transfer 0x%p.\n",transfer, engine->name, transfer_started);} else {dbg_tfr("transfer=0x%p queued, with %s engine running.\n",transfer, engine->name);}shutdown:/* unlock the engine state */dbg_tfr("engine->running = %d\n", engine->running);spin_unlock_irqrestore(&engine->lock, flags);return rv; } /* transfer_queue() - Queue a DMA transfer on the engine** @engine DMA engine doing the transfer* @transfer DMA transfer submitted to the engine** Takes and releases the engine spinlock*/ static int transfer_queue(struct xdma_engine *engine,struct xdma_transfer *transfer) {int rv = 0;struct xdma_transfer *transfer_started;struct xdma_dev *xdev;unsigned long flags;if (!engine) {pr_err("dma engine NULL\n");return -EINVAL;}if (!engine->xdev) {pr_err("Invalid xdev\n");return -EINVAL;}if (!transfer) {pr_err("%s Invalid DMA transfer\n", engine->name);return -EINVAL;}if (transfer->desc_num == 0) {pr_err("%s void descriptors in the transfer list\n",engine->name);return -EINVAL;}dbg_tfr("%s (transfer=0x%p).\n", __func__, transfer);xdev = engine->xdev;if (xdma_device_flag_check(xdev, XDEV_FLAG_OFFLINE)) {pr_info("dev 0x%p offline, transfer 0x%p not queued.\n", xdev,transfer);return -EBUSY;}/* lock the engine state */spin_lock_irqsave(&engine->lock, flags);engine->prev_cpu = get_cpu();put_cpu();/* engine is being shutdown; do not accept new transfers */if (engine->shutdown & ENGINE_SHUTDOWN_REQUEST) {pr_info("engine %s offline, transfer 0x%p not queued.\n",engine->name, transfer);rv = -EBUSY;goto shutdown;}/* mark the transfer as submitted */transfer->state = TRANSFER_STATE_SUBMITTED;/* add transfer to the tail of the engine transfer queue */list_add_tail(&transfer->entry, &engine->transfer_list);/* engine is idle? */if (!engine->running) {/* start engine */dbg_tfr("%s(): starting %s engine.\n", __func__, engine->name);transfer_started = engine_start(engine);if (!transfer_started) {pr_err("Failed to start dma engine\n");goto shutdown;}dbg_tfr("transfer=0x%p started %s engine with transfer 0x%p.\n",transfer, engine->name, transfer_started);} else {dbg_tfr("transfer=0x%p queued, with %s engine running.\n",transfer, engine->name);}shutdown:/* unlock the engine state */dbg_tfr("engine->running = %d\n", engine->running);spin_unlock_irqrestore(&engine->lock, flags);return rv; }

    接下來就是unmap, 這里的代碼很簡單。

    以上就是傳輸一次數據所調用的接口,與基本邏輯。

    相關的的驅動及測試代碼,以及FPGA工程,已經上傳至網盤,有需要的朋友可以自行下載。
    鏈接:https://pan.baidu.com/s/1wdPqUf8_2K6r8ZbVupizrw
    提取碼:i2sw

    總結

    以上是生活随笔為你收集整理的Xilinx XDMA 数据传输sgdma 驱动代码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。