PCIe-块设备驱动-SG DMA




/*
 * kernel: 4.5.2
 */

#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/interrupt.h>	// needed in 4.3.3

#define TEST_PCIE_DEV_NAME "test_pcie"
//#define PCI_VENDOR_ID_XILINX 0x10EE	/* already defined in <linux/pci_ids.h> The default value, 10EEh, is the Vendor ID for Xilinx. */
#define TEST_PCI_DEVICE_ID_XILINX	0x7033	// !!!check here!	/* the default value is 70<link speed><link width>h */

#define TEST_SSD_DEV_NAME		"test_ssd"
#define TEST_SSD_PARTITONS	1
#define TEST_SSD_MINORS 1
#define TEST_DEV_MEM_SZ	(1 << 22)	// 4MB

#define KTHREAD_NAME "test_kthread_fn"

#define COHERENT_DMA_BUF_SZ (1 << 22)	// !!!4MB; no less than 1MB
#define TEST_DMA_BUF_MAX_NUM 320	// !!!

/* ZC706 AXI Address */
#define DDR3_BASE_ADDR 			(0x0 + 0x400000)	// !!!!!!
#define AXI_BAR0 				0x80800000
#define AXI_BAR1 				0x80000000
#define TRANS_BRAM_BASE_ADDR	0x81000000	
#define AXI_BAR2 0x40000000	// wb

/* offset from TRANS_BRAM_BASE_ADDR */
#define AXI_PCIE_CTL_OFFSET 0x8000
#define AXI_CDMA_LITE_OFFSET 0xc000

/* AXI to PCIe Base Address Translation Configuration Registers
 * offset from AXI_PCIE_CTL_OFFSET
 */
#define AXIBAR2PCIEBAR_0U_OFFSET 0x208
#define AXIBAR2PCIEBAR_0L_OFFSET 0x20c
#define AXIBAR2PCIEBAR_1U_OFFSET 0x210
#define AXIBAR2PCIEBAR_1L_OFFSET 0x214
#define AXIBAR2PCIEBAR_2U_OFFSET 0x218
#define AXIBAR2PCIEBAR_2L_OFFSET 0x21c

/* trans_desc_offset */
/*
#define NXTDESC_PNTR_OFFSET 0x00
#define DESC_SA_OFFSET 0x08
#define DESC_DA_OFFSET 0x10
#define DESC_CTL_OFFSET 0x18
#define DESC_STAT_OFFSET 0x1c
*/
/* cdma_reg_offset
 * offset from AXI_CDMA_LITE_OFFSET
 */
#define	CDMACR_OFFSET 0x00
#define	CDMASR_OFFSET 0x04
#define	CURDESC_PNTR_OFFSET 0x08
#define	TAILDESC_PNTR_OFFSET 0x10
#define	CDMA_SA_OFFSET 0x18
#define	CDMA_DA_OFFSET 0x20
#define	CDMA_BTT_OFFSET 0x28

struct io_cmd {
	struct bio *bio;	// !!!
	struct scatterlist *scatlist;
	dma_addr_t dma_addr;	// used by DMA controller of the device
	void *kvaddr;	// kernel virtual address, used by kernel and driver, especially to deal with data from userspace(__bio_kmap_atomic)
	uint32_t len;
};

struct io_que {
	struct bio_list bio_lst;	// !!!composed of bio, a singly-linked list of bios
	struct task_struct *task_s;
	struct io_cmd *io_cmd;	// above
	struct ssd_dev *ssd_dev;	// !!!below
	spinlock_t lock;
	uint8_t volatile is_busy;    // origin: unsigned int, DMA busy flag
};

struct ssd_dev {
	struct pci_dev *pci_dev;
	struct gendisk *disk;	// linux/genhd.h
	void __iomem *pci_bar;	// !!!!!!above, __iomem is needed
	struct io_que *dev_que;	// !!!above
};

#define TRANS_DESC_ALIGN 0x40	// !!!!!!64

struct trans_desc {	// transfer descriptor, according to xapp1171
	uint32_t nxt_ptr;
	uint32_t reserved0;
	uint32_t src_addr;
	uint32_t reserved1;
	uint32_t dest_addr;
	uint32_t reserved2;
	uint32_t ctrl;
	uint32_t stat;
};

#define __MIN(a, b) ((a) < (b) ? (a) : (b))

/*
static void setup_cmd(struct io_cmd *io_cmd, struct bio *bio, struct io_que *dev_que)
{
	io_cmd->bio = bio;	// !!!!!!save it until test_bio_complete
}
*/
/*
static int setup_scatter_map(struct ssd_dev *ssd_dev, struct io_cmd *io_cmd, unsigned int const phys_segs)
{
	void *kvaddr;	// volatile struct scatter_region *
	dma_addr_t dma_addr;

	// !!!!!!return two params! set here!
	kvaddr = dma_alloc_coherent(&ssd_dev->pci_dev->dev, PAGE_SIZE, &dma_addr, GFP_ATOMIC | GFP_DMA);	
	if (kvaddr == NULL) {
		printk("err_dma_pool_alloc\n");
		return -ENOMEM;
	}

	io_cmd->kvaddr = kvaddr;
	io_cmd->dma_addr = dma_addr;
	io_cmd->len = phys_segs;

	return 0;
}
*/
/*
static int setup_scatter_list(struct io_que *dev_que, struct io_cmd *io_cmd, struct bio *bio)
{
	//struct ssd_dev *ssd_dev;
	struct bio_vec prev_bv, cur_bv;	// !!!
	struct bvec_iter bvec_iter;	// !!!
	struct scatterlist *cur_scatlist = NULL;
	unsigned int phys_segs, bytes_len = 0;
	unsigned char isnt_first_bio_vec = 0u;	// !!!
	int result = -ENOMEM;
	
	phys_segs = bio_phys_segments(dev_que->ssd_dev->disk->queue, bio);	// !!!
	io_cmd->scatlist = (struct scatterlist *)kmalloc(sizeof(struct scatterlist) * phys_segs, GFP_ATOMIC | GFP_DMA);	// !!!
	if (io_cmd->scatlist == NULL) {
		printk("err_alloc_scatterlist\n");
		goto err_alloc_scatterlist;
	}

	sg_init_table(io_cmd->scatlist, phys_segs);	// !!!lib/scatterlist.c

	phys_segs = 0;
	memset(&prev_bv, 0, sizeof(struct bio_vec));	// !!!!!!prev_bv need to be initialized
	bio_for_each_segment(cur_bv, bio, bvec_iter) {	// !!!
		if (isnt_first_bio_vec && BIOVEC_PHYS_MERGEABLE(&prev_bv, &cur_bv)) {	// !!!BIOVEC_PHYS_MERGEABLE is defined in bio.h
			cur_scatlist->length += cur_bv.bv_len;	// !!!
		} else {
			if (isnt_first_bio_vec)
				cur_scatlist++;
			else
				cur_scatlist = io_cmd->scatlist;
			sg_set_page(cur_scatlist, cur_bv.bv_page, cur_bv.bv_len, cur_bv.bv_offset);	// !!!in <linux/scatterlist.h>
			phys_segs++;
		}
		bytes_len += cur_bv.bv_len;	// !!!
		prev_bv = cur_bv;
		isnt_first_bio_vec = 1u;
	}

	sg_mark_end(cur_scatlist);	// !!!<linux/scatterlist.h>

	//ssd_dev = dev_que->ssd_dev;
	result = dma_map_sg(&dev_que->ssd_dev->pci_dev->dev, io_cmd->scatlist, phys_segs,
				bio_data_dir(io_cmd->bio) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);	// !!!???What's its use?
	if (result == 0) {
		printk("err_dma_map_sg\n");
		goto err_dma_map_sg;
	}

	result = setup_scatter_map(dev_que->ssd_dev, io_cmd, phys_segs);	// above
	if (result)
		goto err_setup_scatter_map;

	bio->bi_iter.bi_sector += (sector_t)(bytes_len >> 9);	// !!!it will not be set by the kernel?
	bio->bi_iter.bi_idx = bvec_iter.bi_idx;	// !!!

	return 0;

err_setup_scatter_map:
	dma_unmap_sg(&dev_que->ssd_dev->pci_dev->dev, io_cmd->scatlist, phys_segs,
			bio_data_dir(io_cmd->bio) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
	printk("err_setup_scatt
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
PCIE-DMA是一种基于PCIe接口的直接内存访问技术,能够实现高速数据传输。在我的学习笔记中,我详细记录了与PCIE-DMA相关的知识和学习心得。 首先,我了解到PCIE-DMA技术的基本原理和作用。PCIE-DMA可以通过PCIe总线直接访问系统内存中的数据,而不需要过多的CPU干预,提高数据传输的速度和效率。这种技术在需要大量数据传输的场景下非常有效,比如高性能计算、数据采集等。 其次,我深入学习了PCIE-DMA的工作原理。PCIE-DMA的核心是DMA控制器,它负责管理和控制数据传输的流程。当设备需要读写内存中的数据时,它通过DMA控制器发送请求,然后DMA控制器生成一个事务,将数据直接传输到或从内存中。这样就大大减少了CPU的参与,提高了数据传输的效率。 另外,我还学习了PCIE-DMA的配置和编程方法。PCIE-DMA的配置主要包括硬件配置和软件配置两个部分。硬件配置通常涉及到DMA控制器和PCIe接口的初始化和配置,软件配置则需要编写驱动程序来驱动DMA控制器和处理数据传输过程中的事件和异常。这部分内容对于我来说还比较新颖,需要更多的实践和实践。 最后,我总结了PCIE-DMA的应用场景和发展前景。PCIE-DMA在高性能计算、数据采集等领域具有广阔的应用前景。随着数据量的不断增加和传输速度的要求越来越高,PCIE-DMA技术的需求也将越来越大。因此,对于我来说,学习掌握PCIE-DMA技术非常有价值。 通过学习和记录PCIE-DMA的相关知识和经验,我对这项技术有了更深入的理解和掌握。希望将来能通过应用PCIE-DMA技术解决实际问题,为科研和工程项目的顺利进行做出贡献。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值