#include #include #include #include #include #include #include #include "drexchar.h" #include "drexdma.h" #define MAX_DEV 16 static int drexpcie_open(struct inode *inode, struct file *file); static int drexpcie_release(struct inode *inode, struct file *file); static int drexpcie_mmap(struct file *file, struct vm_area_struct *vma); static long drexpcie_ioctl(struct file *file, unsigned int cmd, unsigned long arg); static ssize_t drexpcie_read(struct file *file, char __user *buf, size_t count, loff_t *offset); static ssize_t drexpcie_write(struct file *file, const char __user *buf, size_t count, loff_t *offset); static const struct file_operations drexpcie_fops = { .owner = THIS_MODULE, .open = drexpcie_open, .release = drexpcie_release, .mmap = drexpcie_mmap, .unlocked_ioctl = drexpcie_ioctl, .read = drexpcie_read, .write = drexpcie_write }; struct drexpcie_device_data { struct device* dev; struct cdev cdev; uint32_t type; uint32_t offset; }; static int dev_major = 0; static struct class *drexpcie_class = NULL; static struct drexpcie_device_data drexpcie_dev_data[MAX_DEV]; static struct drexpcie_driver_priv* driv_access = NULL; static int drexpcie_uevent(const struct device *dev, struct kobj_uevent_env *env) { add_uevent_var(env, "DEVMODE=%#o", 0666); return 0; } int drexpcie_chardev_create(struct drexpcie_driver_priv *driv_priv) { int err, i; dev_t dev; char device_string[64]; int pl_count = 0; int dma_count = 0; driv_access = driv_priv; err = alloc_chrdev_region(&dev, 0, MAX_DEV, "drexpcie"); dev_major = MAJOR(dev); // drexpcie_class = class_create(THIS_MODULE, "drexpcie-dev"); drexpcie_class = class_create("drexpcie-dev"); drexpcie_class->dev_uevent = drexpcie_uevent; for (i = 0; i < MAX_DEV; i++) { drexpcie_dev_data[i].type = ioread32(driv_access->bar0_mem+(i*4)*4); if (drexpcie_dev_data[i].type == 0x10000) { snprintf(device_string,sizeof(device_string),"drexpl%d",pl_count); pl_count++; } else if (drexpcie_dev_data[i].type == 0x20000) { snprintf(device_string,sizeof(device_string),"drexdma%d",dma_count); dma_count++; } else { drexpcie_dev_data[i].type = 0; break; } // save the offset of the device area; drexpcie_dev_data[i].offset = ioread32(driv_access->bar0_mem+(i*4+1)*4); cdev_init(&drexpcie_dev_data[i].cdev, &drexpcie_fops); drexpcie_dev_data[i].cdev.owner = THIS_MODULE; cdev_add(&drexpcie_dev_data[i].cdev, MKDEV(dev_major, i), 1); drexpcie_dev_data[i].dev = device_create(drexpcie_class, NULL, MKDEV(dev_major, i), NULL, "%s",device_string); } return 0; } int drexpcie_chardev_destroy(void) { int i; for (i = 0; i < MAX_DEV; i++) { device_destroy(drexpcie_class, MKDEV(dev_major, i)); } class_unregister(drexpcie_class); class_destroy(drexpcie_class); unregister_chrdev_region(MKDEV(dev_major, 0), MINORMASK); return 0; } static int drexpcie_open(struct inode *inode, struct file *file) { struct drexpcie_device_priv* drexpcie_priv; unsigned int minor = iminor(inode); drexpcie_priv = kzalloc(sizeof(struct drexpcie_device_priv), GFP_KERNEL); drexpcie_priv->chnum = minor; drexpcie_priv->driv_priv = driv_access; // drexpcie_priv->dev = &(drexpcie_dev_data[drexpcie_priv->chnum].pdev->dev); drexpcie_priv->type = drexpcie_dev_data[drexpcie_priv->chnum].type; drexpcie_priv->base_mem = driv_access->bar0_mem + drexpcie_dev_data[drexpcie_priv->chnum].offset; drexpcie_priv->num_bufs = 512; drexpcie_priv->num_bytes = 4194304; drexpcie_priv->valid_bufs = 0; drexpcie_priv->bufPtr = NULL; drexpcie_priv->dmaPtr = (dma_addr_t *) NULL; file->private_data = drexpcie_priv; return 0; } static int drexpcie_release(struct inode *inode, struct file *file) { struct drexpcie_device_priv* drexpcie_priv = file->private_data; kfree(drexpcie_priv); drexpcie_priv = NULL; return 0; } static int drexpcie_mmap(struct file *file, struct vm_area_struct *vma) { struct drexpcie_device_priv* drexpcie_priv = file->private_data; int ret; // DMA doesn't support mmap if (drexpcie_priv->type == 0x20000) return -EINVAL; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ret = io_remap_pfn_range(vma, vma->vm_start, ((unsigned long) drexpcie_priv->driv_priv->mem_start) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot); if (ret != 0) { printk(KERN_ERR "Error in calling remap_pfn_range: returned %d\n", ret); return -EAGAIN; } return 0; } static long drexpcie_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct drexpcie_device_priv* drexpcie_priv = file->private_data; drexpcie_ioctl_t drexpcie_ioctl; // uint32_t temp; if (copy_from_user(&drexpcie_ioctl, (drexpcie_ioctl_t *)arg, sizeof(drexpcie_ioctl_t))) { return -EACCES; } printk("drexpcie[%d]: Received IOCTL cmd=%d\n", drexpcie_priv->chnum, drexpcie_ioctl.cmd); switch (drexpcie_ioctl.cmd) { case DREXDMA_DMA_INIT: return dma_init(drexpcie_priv); break; case DREXDMA_DMA_CLEAR: return dma_clear(drexpcie_priv); break; case DREXDMA_DMA_START: return dma_start(drexpcie_priv); break; case DREXDMA_DMA_STOP: return dma_stop(drexpcie_priv); break; case DREXDMA_SET_NUM_BUFS: drexpcie_priv->num_bufs = drexpcie_ioctl.value; break; case DREXDMA_SET_NUM_BYTES: drexpcie_priv->num_bytes = drexpcie_ioctl.value; break; case DREXDMA_GET_NUM_BUFS: drexpcie_ioctl.value = drexpcie_priv->num_bufs; if (copy_to_user((drexpcie_ioctl_t *)arg, &drexpcie_ioctl, sizeof(drexpcie_ioctl_t))) { return -EACCES; } break; case DREXDMA_GET_NUM_BYTES: drexpcie_ioctl.value = drexpcie_priv->num_bytes; if (copy_to_user((drexpcie_ioctl_t *)arg, &drexpcie_ioctl, sizeof(drexpcie_ioctl_t))) { return -EACCES; } break; case DREXDMA_GET_FREE_BUFS: drexpcie_ioctl.value = ioread32(drexpcie_priv->base_mem + DREXDMA_OFFSET_STAT + 0x8); if (copy_to_user((drexpcie_ioctl_t *)arg, &drexpcie_ioctl, sizeof(drexpcie_ioctl_t))) { return -EACCES; } break; default: printk("drexpcie[%d]: error, invalid IOCTL cmd\n", drexpcie_priv->chnum); return -EINVAL; } return 0; } static ssize_t drexpcie_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { struct drexpcie_device_priv* drexpcie_priv = file->private_data; uint32_t temp[3]; uint32_t dma_index; // read status register to determine is something is in the complete queue temp[0] = ioread32(drexpcie_priv->base_mem + DREXDMA_OFFSET_STAT+0xC); // if bit 0 is high, FIFO is empty return 0 count value; if (temp[0] & 0x01) { count = 0; } // else read 72 bit FIFO value; value is popped after MSW read; else { temp[0] = ioread32(drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO); // temp[1] = ioread32(drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO+4); temp[2] = ioread32(drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO+8); // determine which buffer index; dma_index = temp[0] & 0x000001FF; // determine amount of data in buffer; count = (temp[0] & 0xFFFFFE00) >> 9; //using dma_index, copy correct buffer to user space if (copy_to_user(buf, drexpcie_priv->bufPtr[dma_index], count)) { count = 0; } // create new LSW using index and size temp[0] = (((drexpcie_priv->num_bytes) & 0x7FFFFF) << 9); temp[0] |= (((dma_index) & 0x000001FF)); // write now unused buffer back to the available queue // If using loop mode dont need to write buffers back in // iowrite32(temp[0], drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO); // iowrite32(temp[1], drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO+4); // iowrite32(temp[2], drexpcie_priv->base_mem + DREXDMA_OFFSET_FIFO+8); } return count; } static ssize_t drexpcie_write(struct file *file, const char __user *buf, size_t count, loff_t *offset) { return count; }