/*
* 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 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_BASEADDR 0XC000 // 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 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
};
/*
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);