mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
Improve ioctl implementation to support arbitrary number of regions
This commit is contained in:
parent
2babcdd16e
commit
6daf1171b5
@ -54,39 +54,52 @@ static int mqnic_release(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mqnic_map_registers(struct mqnic_dev *mqnic, struct vm_area_struct *vma)
|
||||
{
|
||||
size_t map_size = vma->vm_end - vma->vm_start;
|
||||
int ret;
|
||||
|
||||
if (map_size > mqnic->hw_regs_size) {
|
||||
dev_err(mqnic->dev, "%s: Tried to map registers region with wrong size %lu (expected <= %llu)",
|
||||
__func__, vma->vm_end - vma->vm_start, mqnic->hw_regs_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = remap_pfn_range(vma, vma->vm_start, mqnic->hw_regs_phys >> PAGE_SHIFT,
|
||||
map_size, pgprot_noncached(vma->vm_page_prot));
|
||||
|
||||
if (ret)
|
||||
dev_err(mqnic->dev, "%s: remap_pfn_range failed for registers region", __func__);
|
||||
else
|
||||
dev_dbg(mqnic->dev, "%s: Mapped registers region at phys: 0x%pap, virt: 0x%p",
|
||||
__func__, &mqnic->hw_regs_phys, (void *)vma->vm_start);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mqnic_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct miscdevice *miscdev = file->private_data;
|
||||
struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev);
|
||||
int index;
|
||||
u64 pgoff, req_len, req_start;
|
||||
|
||||
if (vma->vm_pgoff == 0)
|
||||
return mqnic_map_registers(mqnic, vma);
|
||||
index = vma->vm_pgoff >> (40 - PAGE_SHIFT);
|
||||
req_len = vma->vm_end - vma->vm_start;
|
||||
pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1);
|
||||
req_start = pgoff << PAGE_SHIFT;
|
||||
|
||||
if (vma->vm_end < vma->vm_start)
|
||||
return -EINVAL;
|
||||
|
||||
if ((vma->vm_flags & VM_SHARED) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (index) {
|
||||
case 0:
|
||||
if (req_start + req_len > mqnic->hw_regs_size)
|
||||
return -EINVAL;
|
||||
|
||||
return io_remap_pfn_range(vma, vma->vm_start,
|
||||
(mqnic->hw_regs_phys >> PAGE_SHIFT) + pgoff,
|
||||
req_len, pgprot_noncached(vma->vm_page_prot));
|
||||
case 1:
|
||||
if (req_start + req_len > mqnic->app_hw_regs_size)
|
||||
return -EINVAL;
|
||||
|
||||
return io_remap_pfn_range(vma, vma->vm_start,
|
||||
(mqnic->app_hw_regs_phys >> PAGE_SHIFT) + pgoff,
|
||||
req_len, pgprot_noncached(vma->vm_page_prot));
|
||||
case 2:
|
||||
if (req_start + req_len > mqnic->ram_hw_regs_size)
|
||||
return -EINVAL;
|
||||
|
||||
return io_remap_pfn_range(vma, vma->vm_start,
|
||||
(mqnic->ram_hw_regs_phys >> PAGE_SHIFT) + pgoff,
|
||||
req_len, pgprot_noncached(vma->vm_page_prot));
|
||||
default:
|
||||
dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset 0x%lx",
|
||||
__func__, vma->vm_pgoff);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_err(mqnic->dev, "%s: Tried to map an unknown region at page offset %lu",
|
||||
__func__, vma->vm_pgoff);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -94,29 +107,90 @@ static long mqnic_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct miscdevice *miscdev = file->private_data;
|
||||
struct mqnic_dev *mqnic = container_of(miscdev, struct mqnic_dev, misc_dev);
|
||||
size_t minsz;
|
||||
|
||||
if (_IOC_TYPE(cmd) != MQNIC_IOCTL_TYPE)
|
||||
return -ENOTTY;
|
||||
if (cmd == MQNIC_IOCTL_GET_API_VERSION) {
|
||||
// Get API version
|
||||
return MQNIC_IOCTL_API_VERSION;
|
||||
} else if (cmd == MQNIC_IOCTL_GET_DEVICE_INFO) {
|
||||
// Get device information
|
||||
struct mqnic_ioctl_device_info info;
|
||||
|
||||
switch (cmd) {
|
||||
case MQNIC_IOCTL_INFO:
|
||||
{
|
||||
struct mqnic_ioctl_info ctl;
|
||||
minsz = offsetofend(struct mqnic_ioctl_device_info, num_irqs);
|
||||
|
||||
ctl.fw_id = mqnic->fw_id;
|
||||
ctl.fw_ver = mqnic->fw_ver;
|
||||
ctl.board_id = mqnic->board_id;
|
||||
ctl.board_ver = mqnic->board_ver;
|
||||
ctl.regs_size = mqnic->hw_regs_size;
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &ctl, sizeof(ctl)) != 0)
|
||||
return -EFAULT;
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
info.flags = 0;
|
||||
info.fw_id = mqnic->fw_id;
|
||||
info.fw_ver = mqnic->fw_ver;
|
||||
info.board_id = mqnic->board_id;
|
||||
info.board_ver = mqnic->board_ver;
|
||||
info.build_date = mqnic->build_date;
|
||||
info.git_hash = mqnic->git_hash;
|
||||
info.rel_info = mqnic->rel_info;
|
||||
info.num_regions = 3;
|
||||
info.num_irqs = 0;
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
|
||||
} else if (cmd == MQNIC_IOCTL_GET_REGION_INFO) {
|
||||
// Get region information
|
||||
struct mqnic_ioctl_region_info info;
|
||||
|
||||
minsz = offsetofend(struct mqnic_ioctl_region_info, name);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
info.flags = 0;
|
||||
info.type = MQNIC_REGION_TYPE_UNIMPLEMENTED;
|
||||
info.next = 0;
|
||||
info.child = 0;
|
||||
info.size = 0;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
info.name[0] = 0;
|
||||
|
||||
switch (info.index) {
|
||||
case 0:
|
||||
info.type = MQNIC_REGION_TYPE_NIC_CTRL;
|
||||
info.next = 1;
|
||||
info.child = 0;
|
||||
info.size = mqnic->hw_regs_size;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
strlcpy(info.name, "ctrl", sizeof(info.name));
|
||||
break;
|
||||
case 1:
|
||||
info.type = MQNIC_REGION_TYPE_APP_CTRL;
|
||||
info.next = 2;
|
||||
info.child = 0;
|
||||
info.size = mqnic->app_hw_regs_size;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
strlcpy(info.name, "app", sizeof(info.name));
|
||||
break;
|
||||
case 2:
|
||||
info.type = MQNIC_REGION_TYPE_RAM;
|
||||
info.next = 3;
|
||||
info.child = 0;
|
||||
info.size = mqnic->ram_hw_regs_size;
|
||||
info.offset = ((u64)info.index) << 40;
|
||||
strlcpy(info.name, "ram", sizeof(info.name));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -ENOTTY;
|
||||
|
||||
return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct file_operations mqnic_fops = {
|
||||
|
@ -38,16 +38,52 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MQNIC_IOCTL_API_VERSION 0
|
||||
|
||||
#define MQNIC_IOCTL_TYPE 0x88
|
||||
#define MQNIC_IOCTL_BASE 0xC0
|
||||
|
||||
#define MQNIC_IOCTL_INFO _IOR(MQNIC_IOCTL_TYPE, 0xf0, struct mqnic_ioctl_info)
|
||||
enum {
|
||||
MQNIC_REGION_TYPE_UNIMPLEMENTED = 0x00000000,
|
||||
MQNIC_REGION_TYPE_CTRL = 0x00001000,
|
||||
MQNIC_REGION_TYPE_NIC_CTRL = 0x00001001,
|
||||
MQNIC_REGION_TYPE_APP_CTRL = 0x00001002,
|
||||
MQNIC_REGION_TYPE_RAM = 0x00002000
|
||||
};
|
||||
|
||||
struct mqnic_ioctl_info {
|
||||
// get API version
|
||||
#define MQNIC_IOCTL_GET_API_VERSION _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 0)
|
||||
|
||||
// get device information
|
||||
struct mqnic_ioctl_device_info {
|
||||
__u32 argsz;
|
||||
__u32 flags;
|
||||
__u32 fw_id;
|
||||
__u32 fw_ver;
|
||||
__u32 board_id;
|
||||
__u32 board_ver;
|
||||
size_t regs_size;
|
||||
__u32 build_date;
|
||||
__u32 git_hash;
|
||||
__u32 rel_info;
|
||||
__u32 num_regions;
|
||||
__u32 num_irqs;
|
||||
};
|
||||
|
||||
#define MQNIC_IOCTL_GET_DEVICE_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 1)
|
||||
|
||||
// get region information
|
||||
struct mqnic_ioctl_region_info {
|
||||
__u32 argsz;
|
||||
__u32 flags;
|
||||
__u32 index;
|
||||
__u32 type;
|
||||
__u32 next;
|
||||
__u32 child;
|
||||
__u64 size;
|
||||
__u64 offset;
|
||||
__u8 name[32];
|
||||
};
|
||||
|
||||
#define MQNIC_IOCTL_GET_REGION_INFO _IO(MQNIC_IOCTL_TYPE, MQNIC_IOCTL_BASE + 2)
|
||||
|
||||
#endif /* MQNIC_IOCTL_H */
|
||||
|
@ -106,6 +106,12 @@ int main(int argc, char *argv[])
|
||||
printf("PCIe ID: %s\n", ptr+1);
|
||||
}
|
||||
|
||||
printf("Control region size: %lu\n", dev->regs_size);
|
||||
if (dev->app_regs_size)
|
||||
printf("Application region size: %lu\n", dev->app_regs_size);
|
||||
if (dev->ram_size)
|
||||
printf("RAM region size: %lu\n", dev->ram_size);
|
||||
|
||||
printf("Device-level register blocks:\n");
|
||||
for (struct reg_block *rb = dev->rb_list; rb->type && rb->version; rb++)
|
||||
printf(" type 0x%08x (v %d.%d.%d.%d)\n", rb->type, rb->version >> 24,
|
||||
|
222
utils/mqnic.c
222
utils/mqnic.c
@ -75,26 +75,6 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...)
|
||||
goto fail_open;
|
||||
}
|
||||
|
||||
if (fstat(dev->fd, &st))
|
||||
{
|
||||
perror("fstat failed");
|
||||
goto fail_fstat;
|
||||
}
|
||||
|
||||
dev->regs_size = st.st_size;
|
||||
|
||||
if (dev->regs_size == 0)
|
||||
{
|
||||
struct mqnic_ioctl_info info;
|
||||
if (ioctl(dev->fd, MQNIC_IOCTL_INFO, &info) != 0)
|
||||
{
|
||||
perror("MQNIC_IOCTL_INFO ioctl failed");
|
||||
goto fail_ioctl;
|
||||
}
|
||||
|
||||
dev->regs_size = info.regs_size;
|
||||
}
|
||||
|
||||
// determine sysfs path of PCIe device
|
||||
// first, try to find via miscdevice
|
||||
ptr = strrchr(dev->device_path, '/');
|
||||
@ -123,27 +103,173 @@ static int mqnic_try_open(struct mqnic *dev, const char *fmt, ...)
|
||||
dev->pci_device_path[0] = 0;
|
||||
}
|
||||
|
||||
// map registers
|
||||
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0);
|
||||
if (dev->regs == MAP_FAILED)
|
||||
if (fstat(dev->fd, &st))
|
||||
{
|
||||
perror("mmap regs failed");
|
||||
goto fail_mmap_regs;
|
||||
perror("fstat failed");
|
||||
goto fail_fstat;
|
||||
}
|
||||
|
||||
if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff)
|
||||
dev->regs_size = st.st_size;
|
||||
|
||||
if (dev->regs_size == 0)
|
||||
{
|
||||
// if we were given a PCIe resource, then we may need to enable the device
|
||||
snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path);
|
||||
// miscdevice
|
||||
off_t regs_offset = 0;
|
||||
off_t app_regs_offset = 0;
|
||||
off_t ram_offset = 0;
|
||||
|
||||
if (access(path, W_OK) == 0)
|
||||
if (ioctl(dev->fd, MQNIC_IOCTL_GET_API_VERSION, 0) != MQNIC_IOCTL_API_VERSION)
|
||||
{
|
||||
FILE *fp = fopen(path, "w");
|
||||
fprintf(stderr, "Error: unknown API version\n");
|
||||
goto fail_ioctl;
|
||||
}
|
||||
|
||||
if (fp)
|
||||
struct mqnic_ioctl_device_info device_info;
|
||||
device_info.argsz = sizeof(device_info);
|
||||
device_info.flags = 0;
|
||||
|
||||
if (ioctl(dev->fd, MQNIC_IOCTL_GET_DEVICE_INFO, &device_info) != 0)
|
||||
{
|
||||
perror("MQNIC_IOCTL_GET_DEVICE_INFO ioctl failed");
|
||||
goto fail_ioctl;
|
||||
}
|
||||
|
||||
struct mqnic_ioctl_region_info region_info;
|
||||
region_info.argsz = sizeof(region_info);
|
||||
region_info.flags = 0;
|
||||
region_info.index = 0;
|
||||
|
||||
for (region_info.index = 0; region_info.index < device_info.num_regions; region_info.index++)
|
||||
{
|
||||
if (ioctl(dev->fd, MQNIC_IOCTL_GET_REGION_INFO, ®ion_info) != 0)
|
||||
{
|
||||
fputc('1', fp);
|
||||
fclose(fp);
|
||||
perror("MQNIC_IOCTL_GET_REGION_INFO ioctl failed");
|
||||
goto fail_ioctl;
|
||||
}
|
||||
|
||||
switch (region_info.type) {
|
||||
case MQNIC_REGION_TYPE_NIC_CTRL:
|
||||
regs_offset = region_info.offset;
|
||||
dev->regs_size = region_info.size;
|
||||
break;
|
||||
case MQNIC_REGION_TYPE_APP_CTRL:
|
||||
app_regs_offset = region_info.offset;
|
||||
dev->app_regs_size = region_info.size;
|
||||
break;
|
||||
case MQNIC_REGION_TYPE_RAM:
|
||||
ram_offset = region_info.offset;
|
||||
dev->ram_size = region_info.size;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// map registers
|
||||
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, regs_offset);
|
||||
if (dev->regs == MAP_FAILED)
|
||||
{
|
||||
perror("mmap regs failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
|
||||
// map application section registers
|
||||
if (dev->app_regs_size)
|
||||
{
|
||||
dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, app_regs_offset);
|
||||
if (dev->app_regs == MAP_FAILED)
|
||||
{
|
||||
perror("mmap app regs failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
}
|
||||
|
||||
// map RAM
|
||||
if (dev->ram_size)
|
||||
{
|
||||
dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, ram_offset);
|
||||
if (dev->ram == MAP_FAILED)
|
||||
{
|
||||
perror("mmap RAM failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PCIe resource
|
||||
|
||||
// map registers
|
||||
dev->regs = (volatile uint8_t *)mmap(NULL, dev->regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, 0);
|
||||
if (dev->regs == MAP_FAILED)
|
||||
{
|
||||
perror("mmap regs failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
|
||||
if (dev->pci_device_path[0])
|
||||
{
|
||||
// map application section registers
|
||||
snprintf(path, sizeof(path), "%s/resource2", dev->pci_device_path);
|
||||
|
||||
dev->app_fd = open(path, O_RDWR);
|
||||
|
||||
if (dev->app_fd >= 0)
|
||||
{
|
||||
if (fstat(dev->app_fd, &st))
|
||||
{
|
||||
perror("fstat failed");
|
||||
goto fail_fstat;
|
||||
}
|
||||
|
||||
dev->app_regs_size = st.st_size;
|
||||
|
||||
dev->app_regs = (volatile uint8_t *)mmap(NULL, dev->app_regs_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->app_fd, 0);
|
||||
if (dev->app_regs == MAP_FAILED)
|
||||
{
|
||||
perror("mmap app regs failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
}
|
||||
|
||||
// map RAM
|
||||
snprintf(path, sizeof(path), "%s/resource4", dev->pci_device_path);
|
||||
|
||||
dev->ram_fd = open(path, O_RDWR);
|
||||
|
||||
if (dev->ram_fd >= 0)
|
||||
{
|
||||
if (fstat(dev->ram_fd, &st))
|
||||
{
|
||||
perror("fstat failed");
|
||||
goto fail_fstat;
|
||||
}
|
||||
|
||||
dev->ram_size = st.st_size;
|
||||
|
||||
dev->ram = (volatile uint8_t *)mmap(NULL, dev->ram_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev->ram_fd, 0);
|
||||
if (dev->ram == MAP_FAILED)
|
||||
{
|
||||
perror("mmap RAM failed");
|
||||
goto fail_mmap_regs;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->pci_device_path[0] && mqnic_reg_read32(dev->regs, 4) == 0xffffffff)
|
||||
{
|
||||
// if we were given a PCIe resource, then we may need to enable the device
|
||||
snprintf(path, sizeof(path), "%s/enable", dev->pci_device_path);
|
||||
|
||||
if (access(path, W_OK) == 0)
|
||||
{
|
||||
FILE *fp = fopen(path, "w");
|
||||
|
||||
if (fp)
|
||||
{
|
||||
fputc('1', fp);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,11 +303,24 @@ fail_enum:
|
||||
if (dev->rb_list)
|
||||
free_reg_block_list(dev->rb_list);
|
||||
fail_reset:
|
||||
munmap((void *)dev->regs, dev->regs_size);
|
||||
fail_mmap_regs:
|
||||
if (dev->ram)
|
||||
munmap((void *)dev->ram, dev->ram_size);
|
||||
dev->ram = NULL;
|
||||
if (dev->app_regs)
|
||||
munmap((void *)dev->app_regs, dev->app_regs_size);
|
||||
dev->app_regs = NULL;
|
||||
if (dev->regs)
|
||||
munmap((void *)dev->regs, dev->regs_size);
|
||||
dev->regs = NULL;
|
||||
fail_ioctl:
|
||||
fail_fstat:
|
||||
close(dev->fd);
|
||||
dev->fd = -1;
|
||||
close(dev->app_fd);
|
||||
dev->app_fd = -1;
|
||||
close(dev->ram_fd);
|
||||
dev->ram_fd = -1;
|
||||
fail_open:
|
||||
return -1;
|
||||
}
|
||||
@ -227,6 +366,10 @@ struct mqnic *mqnic_open(const char *dev_name)
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
dev->fd = -1;
|
||||
dev->app_fd = -1;
|
||||
dev->ram_fd = -1;
|
||||
|
||||
// absolute path
|
||||
if (mqnic_try_open(dev, "%s", dev_name) == 0)
|
||||
goto open;
|
||||
@ -322,8 +465,17 @@ void mqnic_close(struct mqnic *dev)
|
||||
if (dev->rb_list)
|
||||
free_reg_block_list(dev->rb_list);
|
||||
|
||||
munmap((void *)dev->regs, dev->regs_size);
|
||||
if (dev->ram)
|
||||
munmap((void *)dev->ram, dev->ram_size);
|
||||
if (dev->app_regs)
|
||||
munmap((void *)dev->app_regs, dev->app_regs_size);
|
||||
if (dev->regs)
|
||||
munmap((void *)dev->regs, dev->regs_size);
|
||||
|
||||
close(dev->fd);
|
||||
close(dev->app_fd);
|
||||
close(dev->ram_fd);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
|
@ -125,10 +125,18 @@ struct mqnic_if {
|
||||
|
||||
struct mqnic {
|
||||
int fd;
|
||||
int app_fd;
|
||||
int ram_fd;
|
||||
|
||||
size_t regs_size;
|
||||
volatile uint8_t *regs;
|
||||
|
||||
size_t app_regs_size;
|
||||
volatile uint8_t *app_regs;
|
||||
|
||||
size_t ram_size;
|
||||
volatile uint8_t *ram;
|
||||
|
||||
struct reg_block *rb_list;
|
||||
struct reg_block *fw_id_rb;
|
||||
struct reg_block *if_rb;
|
||||
|
Loading…
x
Reference in New Issue
Block a user