📄 pci9054.c
字号:
#define DRV_NAME "pci_9054"#define DRV_VERSION "0.1"#ifndef CONFIG_PCI#define CONFIG_PCI#endif#ifndef CONFIG_PROC_FS#define CONFIG_PROC_FS#endif#include <linux/pci.h>#include <linux/module.h>//#include <asm/uaccess.h>#include <linux/interrupt.h>#include <linux/config.h>#include <linux/ioctl.h>#include <linux/poll.h>#include <asm/dma.h>#include "myvalues.h"// GLOBAL DEFINITIONSstruct pci_dev *pdev = NULL;unsigned long ulPciRegister = 0;unsigned long ulPciRegLen = 0;unsigned long ulFpgaRegister = 0;unsigned long ulFpgaRegLen = 0;void __iomem *fpgaMem = NULL;void __iomem *pciMem = NULL;unsigned char bufFlag = 0; // 0 for send, 1 for receiveunsigned char sendHappened = 0; // to represent that send interrupt has occuredunsigned char recvHappened = 0; // to represent that receive interrupt has occuredstatic DECLARE_WAIT_QUEUE_HEAD(pci9054_wqs);static DECLARE_WAIT_QUEUE_HEAD(pci9054_wqr);void *kernBuffer = NULL;int order = 0;dma_addr_t dmaSendAddr = NULL;dma_addr_t dmaRecvAddr = NULL;unsigned long ulSendSize = 0x200000; // 2Munsigned long ulRecvSize = 0x100000 / 2;// 512Kstatic const struct pci_device_id pci9054_ids[] = { { PCI_DEVICE(VENDORID, DEVICEID) }, { /* end: all zeroes */ },};MODULE_DEVICE_TABLE(pci, pci9054_ids);/****************************************\|* Driver "pci9054.o"`s entries *|\****************************************/voidpci9054_do_tasklet(unsigned long unused){ if (bufFlag == 0) { sendHappened = 1; wake_up_interruptible(&pci9054_wqs); printk(KERN_INFO "Send Over!\n"); } else if (bufFlag == 1) { recvHappened = 1; wake_up_interruptible(&pci9054_wqr); printk(KERN_INFO "Receive Over!\n"); } else return;}DECLARE_TASKLET(pci9054_tasklet, pci9054_do_tasklet, 0);irqreturn_t pci9054_Isr(int irqn, void *dev_id, struct pt_regs *regs){ u32 intcsr; u8 dmacsr; intcsr = readl(pciMem + 0x0068); if (! intcsr & (1 << 21) ) return IRQ_NONE; printk(KERN_INFO "DMA CH0 Interrupt\n"); // Disable PCI interrupt intcsr = readl(pciMem + 0x0068); intcsr &= ~(1 << 8); writel(intcsr, pciMem + 0x0068); //Clear DMA CH0 interrupt dmacsr = readb(pciMem + 0x00A8); dmacsr |= 1 << 3; writeb(dmacsr, pciMem + 0x00A8); // CH0 stop and disable dmacsr = readb(pciMem + 0x00A8); dmacsr &= ~( (1 << 1) | (1 << 0) ); writeb(dmacsr, pciMem + 0x00A8); if (bufFlag == 0) { dma_unmap_single(&pdev->dev, dmaSendAddr, ulSendSize, DMA_TO_DEVICE); dmaSendAddr = NULL; } else if (bufFlag == 1) { dma_unmap_single(&pdev->dev, dmaRecvAddr, ulRecvSize, DMA_FROM_DEVICE); dmaRecvAddr = NULL; } // request dpc...that's tasklet under linux tasklet_schedule(&pci9054_tasklet); // Enable PCI Interrupt intcsr = readl(pciMem + 0x0068); intcsr |= (1 << 8); writel(intcsr, pciMem + 0x0068); return IRQ_HANDLED;}int pci9054_probe(struct pci_dev *dev, const struct pci_device_id *id){ int status = -1; status = pci_enable_device(dev); //pci_set_master(dev); pdev = dev; // save it for later use return status;}voidpci9054_remove(struct pci_dev *dev){ pci_disable_device(dev); return;}/* device module information */static struct pci_driver pci9054_driver = { .name = DRV_NAME, .id_table = pci9054_ids, .probe = pci9054_probe, .remove = pci9054_remove,};int pci9054_open(struct inode *inode, struct file *filp){ // setup the irq interrupt handler request_irq(pdev->irq, pci9054_Isr, SA_SHIRQ, "pci9054", pci9054_ids); return 0;}int pci9054_release(struct inode *inode, struct file *filp){ free_irq(pdev->irq, pci9054_ids); return 0;}int pci9054_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg){ struct FpgaRegister *preg = (struct FpgaRegister *)arg; u32 lregvalue; u8 bregvalue; u32 dmasize; switch (cmd) { case WR_FPGA_REGISTER: writel(preg->value, fpgaMem); break; case RD_FPGA_REGISTER: preg->value = readl(fpgaMem + preg->offset); break; case DMA_CH0_SEND_CTRL: // map the kernBuffer into dma address dmaSendAddr = dma_map_single(&pdev->dev, kernBuffer, ulSendSize, DMA_TO_DEVICE); bufFlag = 0; writel(0x00023DC1, pciMem + 0x0080); // demand mode enable // DMA CH0 descriptor writel(0x00000000, pciMem + 0x0090); // bit 3: From PCI to local bus //Set pointer to the right array address before the first transfer writel(dmaSendAddr, pciMem + 0x0084); // CH0 PCI address // CH0 local address offset writel(0x00000000, pciMem + 0x0088); // Enable CH0 bregvalue = readb(pciMem + 0x00A8); bregvalue |= 1 << 0; writeb(bregvalue, pciMem + 0x00A8); // Start DMA CH0 (Demand mode) bregvalue = readb(pciMem + 0x00A8); bregvalue |= 1 << 1; writeb(bregvalue, pciMem + 0x00A8); break; case DMA_CH0_RECV_CTRL: // map the kernBuffer into dma address dmaRecvAddr = dma_map_single(&pdev->dev, kernBuffer, ulRecvSize, DMA_FROM_DEVICE); bufFlag = 1; writel(0x00023DC3, pciMem + 0x0080); // demand mode enable // DMA CH0 descriptor writel(0x00000008, pciMem + 0x0090); // bit 3: local bus to pci //Set pointer to the right array address before the first transfer writel(dmaRecvAddr, pciMem + 0x0084); // CH0 PCI address // CH0 local address offset writel(ulSendSize, pciMem + 0x0088); // Enable CH0 bregvalue = readb(pciMem + 0x00A8); bregvalue |= 1 << 0; writeb(bregvalue, pciMem + 0x00A8); // Start DMA CH0 (Demand mode) bregvalue = readb(pciMem + 0x00A8); bregvalue |= 1 << 1; writeb(bregvalue, pciMem + 0x00A8); break; case SET_DMA_CH0_SIZE: dmasize = *((u32 *)arg); writel(dmasize, pciMem + 0x008C); break; default: return -EINVAL; } return 0;}static int pci9054_mmap(struct file *filp, struct vm_area_struct *vma){ unsigned long page, pos; unsigned long start = (unsigned long)vma->vm_start; unsigned long size = (unsigned long)(vma->vm_end - vma->vm_start); // if userspace tries to mmap beyond end of our buffer, fail if (size > MMT_BUF_SIZE) return -EINVAL; // start off at the start of the buffer pos = (unsigned long)kernBuffer; //vma->vm_flags |= VM_LOCKED; page = virt_to_phys((void *)pos); if (remap_page_range(vma, start, page, size, PAGE_SHARED)) return -EAGAIN; return 0;}/* now poll... */static unsigned intpci9054_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; poll_wait(file, &pci9054_wqs, wait); poll_wait(file, &pci9054_wqr, wait); if (sendHappened == 1) { mask |= POLLIN | POLLRDNORM; sendHappened = 0; } if (recvHappened == 1) { mask |= POLLOUT | POLLWRNORM; recvHappened = 0; } return mask;}ssize_tpci9054_read(struct file *file, char *buf, size_t len, loff_t *pos){ wait_event_interruptible(pci9054_wqs, sendHappened == 1); return 0;}ssize_tpci9054_write(struct file *file, char *buf, size_t len, loff_t *pos){ wait_event_interruptible(pci9054_wqr, recvHappened == 1); return 0;}/* device file operations interface */struct file_operations pci9054_fops = { owner: THIS_MODULE, poll: pci9054_poll, read: pci9054_read, write: pci9054_write, ioctl: pci9054_ioctl, mmap: pci9054_mmap, open: pci9054_open, release: pci9054_release,};/*****************************************\|* Self-defined Functions *|\*****************************************/voidinitDevice(void){ ulPciRegister = pci_resource_start(pdev, 0); ulPciRegLen = pci_resource_len(pdev, 0); ulFpgaRegister = pci_resource_start(pdev, 3); ulFpgaRegLen = pci_resource_len(pdev, 3); (void) pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &pdev->irq); fpgaMem = ioremap(ulFpgaRegister, 256); pciMem = ioremap(ulPciRegister, 256);}voidstartDevice(void){ int status = -1; unsigned long intcsr, pcicr, cntrl; struct page *page; // Disable PCI interrupt intcsr = readl(pciMem + 0x0068); intcsr &= ~( (1<<19) | (1<<18) | (1<<16) | (1<<11) | (1<<8) ); writel(intcsr, pciMem + 0x0068); // Allocate memory order = get_order(MMT_BUF_SIZE); kernBuffer = __get_free_pages(GFP_DMA, order); if (kernBuffer == NULL) { printk(KERN_ALERT "Buffer allocation failure\n"); return; } // Lock these pages into memory for (page = virt_to_page(kernBuffer); page < virt_to_page(kernBuffer + MMT_BUF_SIZE); page++) SetPageReserved(page); // PCI command pcicr = readw(pciMem + 0x0004); pcicr |= (1<<4) | (1<<2) | (1<<1) | (1<<0); writew(pcicr, pciMem + 0x0004); //Set PCI Write Command Code for DMA to "Write and Invalid" cntrl = readl(pciMem + 0x006C); cntrl |= 0x000000F0; writel(cntrl, pciMem + 0x006C); // Interrupt enable intcsr = readl(pciMem + 0x0068); intcsr |= (1<<18) | (1<<8); writel(intcsr, pciMem + 0x0068); printk(KERN_ALERT "device started \n"); }int init_module(void){ unsigned char isr; int err, status; status = pci_register_driver(&pci9054_driver); register_chrdev(187, "pci9054", &pci9054_fops); //pdev = pci_find_device(VENDORID, DEVICEID, NULL); if (pdev == NULL) { printk(KERN_EMERG "no pci card specified\n"); return -PCIBIOS_DEVICE_NOT_FOUND; } // init the device initDevice(); startDevice(); return status;}void cleanup_module(void){ struct page *page; for (page = virt_to_page(kernBuffer); page < virt_to_page(kernBuffer + MMT_BUF_SIZE); page++) ClearPageReserved(page); pci_unregister_driver(&pci9054_driver); unregister_chrdev(187, "pci9054"); free_pages(kernBuffer, order); printk(KERN_INFO "Goodbye, pci9054...\n");}MODULE_LICENSE("GPL");MODULE_AUTHOR("Ziqian Lu");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -