updates
This commit is contained in:
292
pcie_driver/drexchar.c
Executable file
292
pcie_driver/drexchar.c
Executable file
@@ -0,0 +1,292 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <linux/iomap.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user