PCIe
/* * 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_XILINX0x7033// !!!check here!/* the default value is 70<link speed><link width>h */#define TEST_SSD_DEV_NAME"test_ssd"#define TEST_SSD_PARTITONS1#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 AXIBAR2PCIEBAR0_OFFSET_U 0x8208#define AXIBAR2PCIEBAR0_OFFSET_L 0x820c#define AXIBAR2PCIEBAR1_OFFSET_U 0x8210#define AXIBAR2PCIEBAR1_OFFSET_L 0x8214#define CDMA_STAT_OFFSET 0xc004#define AXI_BAR0 0x80800000#define AXI_BAR1 0X80000000#define START_DESCRIPTOR 0xc008#define END_DESCRIPTOR 0xc010#define BRAM_AXI_ADDR 0x81000000#define PCIE_BAR 0x81000000#define C_BASEADDR0XC000// according to hardware project#define CDMA_CTRL_REG_OFFSET 0X00#define CDMA_STAT_REG_OFFSET 0X04#define CDMA_SA_REG_OFFSET 0X18#define CDMA_DA_REG_OFFSET 0X20#define CDMA_BTT_REG_OFFSET 0X28struct io_cmd {struct bio *bio;// !!!struct scatterlist *scatlist;dma_addr_t dma_addr;// used by DMA controller of the devicevoid *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 biosstruct task_struct *task_s;struct io_cmd *io_cmd;// abovestruct ssd_dev *ssd_dev;// !!!belowspinlock_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.hvoid __iomem *pci_bar;// !!!!!!above, __iomem is neededstruct io_que *dev_que;// !!!above};/*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.cphys_segs = 0;memset(&prev_bv, 0, sizeof(struct bio_vec));// !!!!!!prev_bv need to be initializedbio_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.hcur_scatlist->length += cur_bv.bv_len;// !!!} else {if (isnt_first_bio_vec)cur_scatlist++;elsecur_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);// aboveif (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_scatter_map\n");err_dma_map_sg:kfree(io_cmd->scatlist);err_alloc_scatterlist:return -ENOMEM;}*//*static void submit_cmd(struct io_que *dev_que)// !!!actually starts a DMA transfer{dma_addr_t rq_dma_addr;struct ssd_dev *ssd_dev;ssd_dev = dev_que->ssd_dev;rq_dma_addr = dev_que->io_cmd->dma_addr;dev_que->is_busy = 1;// !!!!!!busy flag}*//*static int make_bio_request(struct io_que *io_que, struct bio *bio){int result = -EBUSY;setup_cmd(io_que->io_cmd, bio, io_que);// above, has modified io_que->io_cmdresult = setup_scatter_list(io_que, io_que->io_cmd, bio);// aboveif (result) {printk("err_setup_scatter_list\n");goto err_setup_scatter_list;}submit_cmd(io_que);// abovereturn 0;err_setup_scatter_list:return -ENOMEM;}*/static void test_process_bio(struct io_que *io_que, struct bio *bio){struct bio_vec bvec;struct bvec_iter iter;unsigned int bytes = bio->bi_iter.bi_sector << 9;unsigned int const dir = bio_data_dir(bio);void * const kvaddr = io_que->io_cmd->kvaddr;// get itdma_addr_t const dma_addr = io_que->io_cmd->dma_addr;// get itunsigned int dbg_var = 0;//printk("axi bar1 high 32bits is %x\n",//readl((unsigned char *)io_que->ssd_dev->pci_bar + AXIBAR2PCIEBAR1_OFFSET_U));//printk("axi bar1 low 32 bits is %x\n",//readl((unsigned char *)io_que->ssd_dev->pci_bar + AXIBAR2PCIEBAR1_OFFSET_L));//!!!printk("this bio has %d segs\n", bio_phys_segments(io_que->ssd_dev->disk->queue, bio));// !!!bio_for_each_segment(bvec, bio, iter) {void *buffer = __bio_kmap_atomic(bio, iter);unsigned int cur_bv_len = bio_cur_bytes(bio);unsigned int cnt = 0;//printk("No.%useg begin\n", dbg_var);//printk("bytes=%u, cur_bv_len=%u\n", bytes, cur_bv_len);if (bytes + cur_bv_len >= TEST_DEV_MEM_SZ)// !!!printk("beyond-end rd/wr\n");if (dir == WRITE) {memcpy((void *)(unsigned long)kvaddr, buffer, cur_bv_len);// kvaddrwritel((unsigned long)/*dma_addr*/AXI_BAR1, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_SA_REG_OFFSET);// SA is the mapped AXI_BAR(map to dma_addr before)writel(/*AXI_BAR1*/0x400000 + bytes, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_DA_REG_OFFSET);// DA is DDRwritel(cur_bv_len, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_BTT_REG_OFFSET);// BTTwhile (((readl((unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_STAT_REG_OFFSET) >> 1) & 0x1) == 0x0) {cnt++;if (cnt > 10000) {printk("couldn't wait to idle\n");break;}}} else if (dir == READ) {writel(0x400000 + bytes, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_SA_REG_OFFSET);// SAwritel((unsigned long)AXI_BAR1, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_DA_REG_OFFSET);// DAwritel(cur_bv_len, (unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_BTT_REG_OFFSET);// BTTwhile (((readl((unsigned char *)io_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_STAT_REG_OFFSET) >> 1) & 0x1) == 0x0) {cnt++;if (cnt > 10000) {printk("couldn't wait to idle\n");break;}}memcpy(buffer, (void *)(unsigned long)kvaddr, cur_bv_len);}//printk("after transfer CDMA_STAT is %x\n", readl((unsigned char *)io_que->ssd_dev->pci_bar + CDMA_STAT_OFFSET));// !!!bytes += cur_bv_len;__bio_kunmap_atomic(buffer);//printk("No.%useg end\n", dbg_var);dbg_var++;}bio_endio(bio);// !!!}/*static void free_scatter_map(struct ssd_dev *ssd_dev, struct io_cmd *io_cmd){dma_unmap_sg(&ssd_dev->pci_dev->dev, io_cmd->scatlist, io_cmd->len,bio_data_dir(io_cmd->bio) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);// !!!dma_free_coherent(&ssd_dev->pci_dev->dev, PAGE_SIZE, io_cmd->kvaddr,io_cmd->dma_addr);kfree(io_cmd->scatlist);}*//*static void test_bio_complete(struct ssd_dev *ssd_dev, struct io_que *dev_que)// !!!???logic wrong?{struct bio *bio;struct io_cmd *io_cmd;io_cmd = dev_que->io_cmd;free_scatter_map(ssd_dev, io_cmd);// abovebio = io_cmd->bio;// !!!has been saved beforeif (bio->bi_vcnt == bio->bi_iter.bi_idx)bio_endio(bio);// !!!dev_que->is_busy = 0;// !!!not busyif (bio_list_peek(&dev_que->bio_lst))wake_up_process(dev_que->task_s);}*/// !!!consumer: has been binded below !!!!!!wrong?static int test_kthread_fn(void *data){struct io_que *dev_que;struct bio *bio;dev_que = (struct io_que *)data;if (dev_que == NULL)printk("in test_kthread_fn dev_que is null!\n");do {//struct bio *bio;//if (dev_que->is_busy)// !!!!!!DMA channel is busy//goto sleep_this_thread;if (bio_list_peek(&dev_que->bio_lst)) {spin_lock(&dev_que->lock);bio = bio_list_pop(&dev_que->bio_lst);// !!!!!!get biospin_unlock(&dev_que->lock);//printk("test_kthread_fn: get a bio\n");/*if (make_bio_request(dev_que, bio)) {// abovespin_lock(&dev_que->lock);bio_list_add_head(&dev_que->bio_lst, bio);// add from headspin_unlock(&dev_que->lock);}*/test_process_bio(dev_que, bio);// !!!!!!}//test_bio_complete(ssd_dev, dev_que);// above, orginally it is here!!! why it is not defined but can be compiled??????//sleep_this_thread:schedule();// !!!make this thread sleep!!!!necessary!} while (!kthread_should_stop()); // !!!kthread.creturn 0;}/*static irqreturn_t irq_handler(int irq, void *dev_id)// !!!{struct ssd_dev *ssd_dev;struct io_que *dev_que;printk("irq_handler\n");dev_que = (struct io_que *)dev_id;// !!!ssd_dev = dev_que->ssd_dev;// !!!//spin_lock_irq(&dev_que->lock);test_bio_complete(ssd_dev, dev_que);// !!!above//spin_unlock_irq(&dev_que->lock);return IRQ_HANDLED;}*/static int alloc_kthread(struct io_que *dev_que)// !!!create consumer and make it run{dev_que->task_s = kthread_run(&test_kthread_fn, dev_que, KTHREAD_NAME);// !!!kthread.h kthread.cif (IS_ERR(dev_que->task_s)) {/* def in <linux/err.h> */printk("err: kthread_run\n");return PTR_ERR(dev_que->task_s);} elsereturn 0;}static void free_kthread(struct io_que *dev_que){if (kthread_stop(dev_que->task_s) == 0)// kthread.c, struct task_struct *printk("threadfn has returned\n");}// !!!producer: binded with make_request below, only to add bio to the bio_list. blk_qc_t is unsigned intstatic void test_make_request_fn(struct request_queue *queue, struct bio *bio){struct io_que *dev_que;dev_que = (struct io_que *)queue->queuedata;// !!!spin_lock(&dev_que->lock);bio_list_add(&dev_que->bio_lst, bio);// !!!add from tailspin_unlock(&dev_que->lock);//printk("test_make_request_fn: add a bio\n");}// !!!!!!ssd_dev already alloc, and it's disk already alloc.static struct io_que *alloc_io_que(struct ssd_dev *ssd_dev){struct io_que *dev_que;// constdev_que = (struct io_que *)kmalloc(sizeof(struct io_que), GFP_KERNEL);// !!!if (dev_que == NULL) {printk("err_alloc_dev_que\n");goto err_alloc_dev_que;}ssd_dev->dev_que = dev_que;// !!!!!!dev_que->ssd_dev = ssd_dev;// !!!!!!spin_lock_init(&dev_que->lock);// only for initbio_list_init(&dev_que->bio_lst);// only for init, struct bio_list, bl->head = bl->tail = NULL; comes before consumer!!!dev_que->is_busy = 0;// !!!only for initdev_que->io_cmd = (struct io_cmd *)kmalloc(sizeof(struct io_cmd), GFP_KERNEL);// !!!!!!if (dev_que->io_cmd == NULL) {printk("err_alloc_io_cmd\n");goto err_alloc_io_cmd;}dev_que->io_cmd->kvaddr = dma_alloc_coherent(&dev_que->ssd_dev->pci_dev->dev, COHERENT_DMA_BUF_SZ, &dev_que->io_cmd->dma_addr, GFP_ATOMIC | GFP_DMA);// !!!!!!4MBif (dev_que->io_cmd->kvaddr == NULL) {printk("in alloc_io_que: err_dma_pool_alloc\n");goto err_dma_alloc_coherent;}writel((unsigned long)dev_que->io_cmd->dma_addr, (unsigned char *)dev_que->ssd_dev->pci_bar + AXIBAR2PCIEBAR1_OFFSET_L);// !!!!!!map dma_addr(fixed position) to AXI_BARwritel((unsigned long)(dev_que->io_cmd->dma_addr >> 32), (unsigned char *)dev_que->ssd_dev->pci_bar + AXIBAR2PCIEBAR1_OFFSET_U);printk("before trans stat_reg: %x\n", readl((unsigned char *)dev_que->ssd_dev->pci_bar + C_BASEADDR + CDMA_STAT_REG_OFFSET));if (alloc_kthread(dev_que)) {// !!!!!!consumer comes before producerprintk("err: alloc_kthread\n");goto err_alloc_kthread;}dev_que->ssd_dev->disk->queue = blk_alloc_queue(GFP_KERNEL);// !!!!!!if (dev_que->ssd_dev->disk->queue == NULL) {printk("err: blk_alloc_queue\n");goto err_blk_alloc_queue;}dev_que->ssd_dev->disk->queue->queuedata = dev_que;// !!!void *queuedata, point to itselfdev_que->ssd_dev->disk->queue->queue_flags = QUEUE_FLAG_DEFAULT;// it is needed//queue_flag_set(QUEUE_FLAG_NOMERGES, dev_que->ssd_dev->disk->queue);/* disable merge attempts */queue_flag_set(QUEUE_FLAG_NONROT, dev_que->ssd_dev->disk->queue);/* non-rotational device (SSD) */blk_queue_make_request(dev_que->ssd_dev->disk->queue, &test_make_request_fn);// !!!binded make_request_fn(producer) to the queuereturn dev_que;err_blk_alloc_queue:free_kthread(dev_que);err_alloc_kthread:dma_free_coherent(&dev_que->ssd_dev->pci_dev->dev, COHERENT_DMA_BUF_SZ, dev_que->io_cmd->kvaddr, dev_que->io_cmd->dma_addr);// !!!err_dma_alloc_coherent:kfree(dev_que->io_cmd);err_alloc_io_cmd:kfree(dev_que);err_alloc_dev_que:return NULL;}static void free_io_que(struct ssd_dev *ssd_dev, struct io_que *dev_que){blk_cleanup_queue(dev_que->ssd_dev->disk->queue);free_kthread(dev_que);dma_free_coherent(&dev_que->ssd_dev->pci_dev->dev, COHERENT_DMA_BUF_SZ, dev_que->io_cmd->kvaddr, dev_que->io_cmd->dma_addr);// !!!kfree(dev_que->io_cmd);kfree(dev_que);}static int test_ssd_open(struct block_device *bdev, fmode_t mode){//printk("test_ssd_open\n");return 0;}static void test_ssd_release(struct gendisk *disk, fmode_t mode){//printk("test_ssd_release\n");}static struct block_device_operations ssd_fops = {.open = &test_ssd_open,.release = &test_ssd_release,.owner = THIS_MODULE,};static int blkdev_major;static int test_ssd_init(struct ssd_dev *ssd_dev)// !!!{struct io_que *dev_que;int result = -ENOMEM;printk("blkdev init begin\n");blkdev_major = register_blkdev(0, TEST_SSD_DEV_NAME);// !!!try to allocate any unused major number.if (blkdev_major < 0) {printk("err: register_blkdev\n");goto err_register_blkdev;}ssd_dev->disk = alloc_disk(TEST_SSD_PARTITONS);// !!!if (ssd_dev->disk == NULL) {printk("err: alloc_disk\n");result = -ENOMEM;goto err_alloc_disk;}ssd_dev->disk->major = blkdev_major;ssd_dev->disk->first_minor = 0;// !!!!!!ssd_dev->disk->minors = TEST_SSD_MINORS;sprintf(ssd_dev->disk->disk_name, "%s" , TEST_SSD_DEV_NAME);// !!!ssd_dev->disk->fops = &ssd_fops;ssd_dev->disk->private_data = ssd_dev;// !!!//ssd_dev->disk->driverfs_dev = &ssd_dev->pci_dev->dev;// genhd.h: struct device *driverfs_dev; // FIXME: removeset_capacity(ssd_dev->disk, TEST_DEV_MEM_SZ >> 9);// in unit of sector(512 bytes long independently)dev_que = alloc_io_que(ssd_dev);// !!!above, set ssd_dev->disk->queueif (dev_que == NULL) {printk("err: alloc_io_que\n");result = -ENOMEM;goto err_alloc_io_que;}add_disk(ssd_dev->disk);// !!!!!!// "ssd_dev->pci_dev->irq" init in pci_enable_msi func. dev_que is for param of isr//if (request_irq(ssd_dev->pci_dev->irq, &irq_handler, //IRQF_NOBALANCING | IRQF_SHARED, ssd_dev->disk->disk_name, dev_que) < 0) {//printk("err_request_irq\n");//goto err_request_irq;//}printk("blkdev init end\n");return 0;//err_request_irq:err_alloc_io_que:del_gendisk(ssd_dev->disk);// !!!put_disk(ssd_dev->disk);err_alloc_disk:unregister_blkdev(blkdev_major, TEST_SSD_DEV_NAME);// !!!err_register_blkdev:return result;}static int test_ssd_remove(struct ssd_dev *ssd_dev){struct io_que *dev_que;printk("test_ssd_remove begin\n");dev_que = ssd_dev->dev_que;//free_irq(ssd_dev->pci_dev->irq, dev_que);free_io_que(ssd_dev, dev_que);del_gendisk(ssd_dev->disk);put_disk(ssd_dev->disk);unregister_blkdev(blkdev_major, TEST_SSD_DEV_NAME);// !!!printk("test_ssd_remove end\n");return 0;}static struct pci_device_id test_id_tbl[] = {{ PCI_DEVICE(PCI_VENDOR_ID_XILINX,TEST_PCI_DEVICE_ID_XILINX), },{0,},};MODULE_DEVICE_TABLE(pci, test_id_tbl);static void cdma_init_simple(void __iomem *pci_bar){unsigned long read_val;// default simple moderead_val = readl((unsigned char *)pci_bar + C_BASEADDR + CDMA_CTRL_REG_OFFSET);if ((read_val >> 3) & 0x1)printk("it's sg mode\n");elseprintk("it's simple mode\n");if ((read_val >> 2) & 0x1)printk("reset in progress\n");elseprintk("normal operation\n");read_val = readl((unsigned char *)pci_bar + C_BASEADDR + CDMA_STAT_REG_OFFSET);if ((read_val >> 1) & 0x1)printk("cdma is idle\n");elseprintk("cdma is not idle\n");}static int bars;static int test_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)// !!!{struct ssd_dev *ssd_dev;resource_size_t res_start, res_len;// actually it's integer typeint result = -ENOMEM;printk("pci_driver_probe begin with vendor=%x, device=%x\n", id->vendor, id->device);ssd_dev = (struct ssd_dev *)kmalloc(sizeof(struct ssd_dev), GFP_KERNEL);// !!!!!!if (ssd_dev == NULL) {printk("err: kmalloc ssd_dev\n");goto err_kmalloc_ssd_dev;}ssd_dev->pci_dev = pci_dev;// !!!!!!if (pci_enable_device(pci_dev) < 0) {// !!!printk("err: pci_enable_device\n");goto err_pci_enable_device;}pci_set_master(pci_dev);// !!!enables bus-mastering for device dev//bars = pci_select_bars(pci_dev, IORESOURCE_MEM);// !!!<linux/ioport.h>bars = 0;// !!!!!!it's already set in the hardware projectif (pci_request_selected_regions(pci_dev, bars, TEST_PCIE_DEV_NAME)) {// actually using __request_mem_regionprintk("err: pci_request_selected_regions\n");goto err_pci_request_selected_regions;}res_start = pci_resource_start(pci_dev, bars);res_len = pci_resource_len(pci_dev, bars);printk("pci_res_start=%lu, pci_res_len=%lu\n", (unsigned long)res_start, (unsigned long)res_len);//request_mem_region(pci_resource_start(pci_dev, bars), pci_resource_len(pci_dev, bars), TEST_PCIE_DEV_NAME);/* !!!associate with drivers/pci/msi.c, using pci_enable_msi_range, * updates the @dev's irq member to the lowest new interrupt number; *///if (pci_enable_msi(pci_dev) < 0)//printk("pci_enable_msi: an error occurs\n");//ssd_dev->irq = pci_dev->irq;// !!!!!!pci_set_drvdata(pci_dev, ssd_dev); // !!!bind ssd_dev to pci_dev, for later use//if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64)) < 0)// if return err//printk("err: pci_set_dma_mask\n");//if (pci_set_consistent_dma_mask(pci_dev, DMA_BIT_MASK(64)) < 0)// both needed//printk("pci_set_consistent_dma_mask err\n");// !!!!!!it's bars, not 0? set here! __iomem is neededssd_dev->pci_bar = ioremap(res_start, res_len);// !!!if (ssd_dev->pci_bar == NULL) {printk("err: ioremap\n");goto err_ioremap;}//writel(0x0, (unsigned char *)ssd_dev->pci_bar + 0xc000);// !!!!!!CDMA CTL 设定为single模式//if ((readl((unsigned char *)ssd_dev->pci_bar + 0xc000) >> 3) & 0x1)//printk("CDMA CTL settings err: is not single mode\n");//printk("CDMA STATUS is %x\n", readl((unsigned char *)ssd_dev->pci_bar + 0xc004));// !!!cdma_init_simple(ssd_dev->pci_bar);printk("pci bus init has successfully ended\n");if (test_ssd_init(ssd_dev)) {// aboveprintk("err_test_ssd_init\n");goto err_ssd_init;}printk("pci_driver_probe end\n");return 0;err_ssd_init:iounmap(ssd_dev->pci_bar);err_ioremap:pci_set_drvdata(pci_dev, NULL);// !!!where should it be?//pci_disable_msi(pci_dev);pci_release_selected_regions(pci_dev, bars);// !!!err_pci_request_selected_regions:pci_clear_master(pci_dev);// !!!pci_disable_device(pci_dev);err_pci_enable_device:kfree(ssd_dev);err_kmalloc_ssd_dev:return result;}static void test_remove(struct pci_dev *pci_dev){struct ssd_dev *ssd_dev;printk("pci_driver_remove begin\n");ssd_dev = (struct ssd_dev *)pci_get_drvdata(pci_dev); // has been binded beforetest_ssd_remove(ssd_dev);// aboveiounmap(ssd_dev->pci_bar);// !!!pci_set_drvdata(pci_dev, NULL);//pci_disable_msi(pci_dev);pci_release_selected_regions(pci_dev, bars);// !!!original: pci_release_regions(pci_dev);pci_clear_master(pci_dev);pci_disable_device(pci_dev);kfree(ssd_dev);printk("pci_driver_remove end\n");}static struct pci_driver pci_driver_inst = {.name= TEST_PCIE_DEV_NAME,.id_table= test_id_tbl,.probe= &test_probe,.remove= &test_remove,};static int __init test_module_init(void){int result = -EBUSY;printk("module_init_fn begin\n");if (pci_register_driver(&pci_driver_inst)) {printk("err_register_driver\n");goto err_register_driver;}printk("module_init_fn end\n");return 0;err_register_driver:return result;}module_init(test_module_init);static void __exit test_module_exit(void){printk("module_exit_fn begin\n");pci_unregister_driver(&pci_driver_inst);printk("module_exit_fn end\n");}module_exit(test_module_exit);MODULE_LICENSE("GPL");// !!!
PCIe-块设备驱动-Single DMA