This commit is contained in:
2026-06-19 07:46:17 -05:00
parent ffd3eab72d
commit dd811684dc
10 changed files with 803 additions and 43 deletions

292
pcie_driver/drexchar.c Executable file
View 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;
}