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
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值