Hi I am sure the issue can be solved by something super simple I overlooked. This is a snippet from my header file:
#ifndef COMBLOCKDEV_H
#define COMBLOCKDEV_H
#define BUFF_LEN 256 // depends on CB FIFO and RAM max depth but also available Linux RA
struct user_params{
int len;
int data[BUFF_LEN];
int reg;
};
#define COMBLOCK_IOC_MAGIC 'a'
#define COMBLOCK_IOC_RESET _IO(COMBLOCK_IOC_MAGIC, 0)
#define COMBLOCK_IOC_REG_READ _IOR(COMBLOCK_IOC_MAGIC, 1, struct params *)
#endif
My kernel code loads and probes well. Open and release work as intended:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <asm/uaccess.h>
#include "comblock.h"
#define SUCCESS 0
static int device_open(struct inode *inode, struct file *file) {
struct comblock_local * lp;
pr_info("ComBlock_open(%p)\n", file);
lp = container_of(inode->i_cdev, struct comblock_local, cdev);
file->private_data = lp;
return SUCCESS;
}
/* This is called whenever a process attempts to release the device file
We release the module and clear the queue entry.for asynchronous notifications.
*/
static int device_release(struct inode *inode, struct file *file) {
struct comblock_local * lp = (struct comblock_local*) file->private_data;
pr_info("ComBlock_release(%p,%p)\n", inode, file);
if(!lp->async_queue)
device_fasync(-1, file, 0);
return SUCCESS;
}
static long int device_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_param) {
int ret = SUCCESS;
struct comblock_local * lp = file->private_data;
struct user_params params;
printk(KERN_INFO "Comblock: IOCTL command %d", cmd);
printk(KERN_INFO "Comblock: IOCTL command magic %c", COMBLOCK_IOC_MAGIC);
printk(KERN_INFO "Comblock: IOCTL command max %d", COMBLOCK_IOC_MAXNR);
printk(KERN_INFO "Comblock: IOCTL command dir %d", _IOC_DIR(cmd));
if (_IOC_TYPE(cmd) != COMBLOCK_IOC_MAGIC)
return -ENOTTY;
if (_IOC_NR(cmd) > COMBLOCK_IOC_MAXNR)
return -ENOTTY;
if(_IOC_DIR(cmd) & _IOC_READ)
ret = !access_ok((void __user*) ioctl_param, _IOC_SIZE(cmd));
else if(_IOC_DIR(cmd) & _IOC_WRITE)
ret = !access_ok((void __user*) ioctl_param, _IOC_SIZE(cmd));
if(ret)
return -EFAULT;
if(down_interruptible(&lp->sem))
return -ERESTARTSYS;
printk(KERN_INFO "Comblock: Command %d", cmd);
/* Switch according to the ioctl called */
switch (cmd) {
case COMBLOCK_IOC_RESET:
break;
}
return ret;
}
The test application is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "comblock.h"
int main() {
struct params test;
int ret, i;
int dev = open("/dev/comblock_dev0", O_RDWR);
if(dev == -1) {
printf("Opening was not possible! error: %d\n",dev);
return -1;
}
printf("Trying to reset the CB: %d", ioctl(dev, COMBLOCK_IOC_RESET));
return 0;
}
The test application and kernel module use the same header, so I assume the IOCTL commands are valid. The problem is that ioctl()
in the test application always returns -1 and no printk from the kernel module gets printed in the kernel log. So after one test dmesg
shows this:
[11936.248350] Start ComBlock. ver.0.01
[11936.248589] comblock_dev 80000000.comblock: Device Tree Probing
[11936.248632] comblock_dev 80000000.comblock: Registering cdev.
[11936.248681] comblock_dev 80000000.comblock: ComBlock allocate cdev 510 0
[11936.248828] comblock_dev 80000000.comblock: Comblock detected with the following parameters:
[11936.248835] comblock_dev 80000000.comblock: Enables: OReg 1, IReg 1, IFIFO 1, OFIFO 1, RAM 1
[11936.248840] comblock_dev 80000000.comblock: Length: OReg 4, IReg 4, IFIFO 1024, OFIFO 1024, RAM 100
[11938.005460] ComBlock_open(00000000abab92a2)
[11938.005741] ComBlock_release(000000001d0f0893,00000000abab92a2)
I can confirm that open and release work but IOCTL gets ignored. I suspect there is an issue in the middle part but I can't really understand how to debug it.
Edit: Added file_operations struct, platform_driver, of_device_id, and module init function
static struct file_operations comblock_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = device_ioctl,
.open = device_open,
.release = device_release,
.fasync = device_fasync
};
static int comblock_init(void) {
printk(KERN_INFO "Start ComBlock. ver.0.01");
return platform_driver_register(&comblock_driver);
}
#ifdef CONFIG_OF
static struct of_device_id comblock_of_match[] __devinitdata = {
{ .compatible = "ictp,comblock-1.0" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, comblock_of_match);
#else
#define comblock_of_match
#endif
static struct platform_driver comblock_driver = {
.driver = {
.name = DEVICE_NAME,
.owner = THIS_MODULE,
.of_match_table = comblock_of_match,
},
.probe = device_probe,
.remove = device_remove,
};
The module and device are loaded after boot up, here is the probe function:
/* Probe device tree to retrieve parameters */
static int device_probe(struct platform_device *pdev) {
struct resource *axi_l, *axi_f; /* IO mem resources */
struct device *dev = &pdev->dev;
struct comblock_local *lp = NULL;
unsigned int reg_in_enable;
unsigned int reg_in_width;
unsigned int reg_in_length;
unsigned int reg_out_enable;
unsigned int reg_out_width;
unsigned int reg_out_length;
unsigned int fifo_in_enable;
unsigned int fifo_in_length;
unsigned int fifo_in_width;
unsigned int fifo_out_enable;
unsigned int fifo_out_length;
unsigned int fifo_out_width;
unsigned int ram_enable;
unsigned int ram_length;
unsigned int ram_width;
int ret = 0;
int mem_index = 0;
/////////////////////////// Probing Device Tree ///////////////////////////////////////
dev_info(dev, "Device Tree Probing\n");
// load parameters into the structure
{
ret = device_property_count_u32(dev, "reg-in-enable");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-in-enable", ®_in_enable);
if (ret)
return ret;
ret = device_property_count_u32(dev, "reg-in-length");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-in-length", ®_in_length);
if (ret)
return ret;
ret = device_property_count_u32(dev, "reg-in-width");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-in-width", ®_in_width);
if (ret)
return ret;
ret = device_property_count_u32(dev, "reg-out-enable");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-out-enable", ®_out_enable);
if (ret)
return ret;
ret = device_property_count_u32(dev, "reg-out-length");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-out-length", ®_out_length);
if (ret)
return ret;
ret = device_property_count_u32(dev, "reg-out-width");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "reg-out-width", ®_out_width);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-in-enable");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-in-enable", &fifo_in_enable);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-in-length");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-in-length", &fifo_in_length);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-in-width");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-in-width", &fifo_in_width);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-out-enable");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-out-enable", &fifo_out_enable);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-out-length");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-out-length", &fifo_out_length);
if (ret)
return ret;
ret = device_property_count_u32(dev, "fifo-out-width");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "fifo-out-width", &fifo_out_width);
if (ret)
return ret;
ret = device_property_count_u32(dev, "ram-enable");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "ram-enable", &ram_enable);
if (ret)
return ret;
ret = device_property_count_u32(dev, "ram-length");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "ram-length", &ram_length);
if(ret)
return ret;
ret = device_property_count_u32(dev, "ram-width");
if (ret <= 0) {
dev_err(dev, "init property missing or empty");
return ret ? ret : -ENODATA;
}
ret = device_property_read_u32(dev, "ram-width", &ram_width);
if (ret)
return ret;
}
////////////////////////// allocating local data structure /////////////////////////////
lp = (struct comblock_local *) kzalloc(sizeof(struct comblock_local), GFP_KERNEL);
if (!lp) {
dev_err(dev, "Cound not allocate comblock device\n");
return -ENOMEM;
}
lp->reg_in_enable = reg_in_enable;
lp->reg_in_length = reg_in_length;
lp->reg_in_width = reg_in_width;
lp->reg_out_enable = reg_out_enable;
lp->reg_out_length = reg_out_length;
lp->reg_out_width = reg_out_width;
lp->fifo_in_enable = fifo_in_enable;
lp->fifo_in_length = fifo_in_length;
lp->fifo_in_width = fifo_in_width;
lp->fifo_out_enable = fifo_out_enable;
lp->fifo_out_length = fifo_out_length;
lp->fifo_out_width = fifo_out_width;
lp->ram_enable = ram_enable;
lp->ram_length = ram_length;
lp->ram_width = ram_width;
/* register the device memory */
if(lp->reg_in_enable || lp->reg_out_enable || lp->fifo_in_enable || lp->fifo_out_enable) {
axi_l = platform_get_resource(pdev, IORESOURCE_MEM, mem_index);
mem_index ;
if(!axi_l) {
ret= -ENOMEM;
goto out_free_local;
}else if(!request_mem_region(axi_l->start, axi_l->end - axi_l->start 1, DEVICE_NAME)) {
dev_err(dev, "Couldn't lock memory region at %llu\n", axi_l->start);
ret = -EBUSY;
goto out_free_local;
}
lp->axi_l = axi_l;
lp->axi_l_base_addr = ioremap(axi_l->start, axi_l->end - axi_l->start 1);
}
if(lp->ram_enable) {
axi_f = platform_get_resource(pdev, IORESOURCE_MEM, mem_index);
if(!axi_f) {
ret= -ENOMEM;
goto out_free_local;
}else if(!request_mem_region(axi_f->start, axi_f->end - axi_f->start 1, DEVICE_NAME)) {
dev_err(dev, "Couldn't lock memory region at %llu\n", axi_f->start);
ret = -EBUSY;
goto out_free_local;
}
lp->axi_f = axi_f;
lp->axi_f_base_addr = ioremap(axi_f->start, axi_f->end - axi_f->start 1);
}
dev_set_drvdata(dev, lp);
if(!lp->axi_l_base_addr && !lp->axi_f_base_addr) {
dev_err(dev, "Could not allocate iomem\n");
ret = -EIO;
goto out_free_mem_region;
}
///////////////////////// initializing char device /////////////////////////////////
ret = comblock_cdev_init(dev, lp);
if(ret < 0) {
dev_err(dev, "Could not create cdev \n");
goto out_free_iomap;
}
sema_init(&lp->sem, 1);
dev_info(dev, "Comblock detected with the following parameters: \n");
dev_info(dev, "Enables: OReg %d, IReg %d, IFIFO %d, OFIFO %d, RAM %d \n", lp->reg_in_enable, lp->reg_out_enable, lp->fifo_in_enable, lp->fifo_out_enable, lp->ram_enable);
dev_info(dev, "Length: OReg %d, IReg %d, IFIFO %d, OFIFO %d, RAM %d \n", lp->reg_in_length, lp->reg_out_length, lp->fifo_in_length, lp->fifo_out_length, lp->ram_length);
return 0;
out_free_iomap:
if(lp->axi_l_base_addr)
iounmap(lp->axi_l_base_addr);
if(lp->axi_f_base_addr)
iounmap(lp->axi_f_base_addr);
out_free_mem_region:
if(axi_l)
release_mem_region(axi_l->start, axi_l->end - axi_l->start 1);
if(axi_f)
release_mem_region(axi_f->start, axi_f->end - axi_f->start 1);
out_free_local:
kfree(lp);
dev_set_drvdata(dev, NULL);
return ret;
}
Here is the initialization of the chardev:
/*Initialize the chardevice*/
static int comblock_cdev_init(struct device * dev, struct comblock_local * lp) {
dev_t devno;
int ret = 0;
struct device *device = NULL;
dev_info(dev, "Registering cdev.");
if(lp->major) {
devno = MKDEV(lp->major, lp->minor);
ret = register_chrdev_region(devno, comblock_nr_devs, DEVICE_NAME);
} else {
ret = alloc_chrdev_region(&devno, lp->minor, comblock_nr_devs, DEVICE_NAME);
lp->major = MAJOR(devno);
}
dev_info(dev, "ComBlock allocate cdev %d %d", lp->major, lp->minor);
if(ret < 0) {
dev_warn(dev, " can't get major %d\n", lp->major);
return ret;
}
cdev_init(&lp->cdev, &comblock_fops);
lp->cdev.owner = THIS_MODULE;
ret = cdev_add(&lp->cdev, devno, 1);
if(ret) {
dev_notice(dev, "Error %d adding %s%d", ret, DEVICE_NAME, 0);
goto out_creg;
}
comblock_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(comblock_class)) {
ret = PTR_ERR(comblock_class);
goto out_cadd;
}
device = device_create(comblock_class, NULL, /* no parent device */
devno, NULL, /* no additional data */
DEVICE_NAME "%d", lp->minor);
if (IS_ERR(device)) {
ret = PTR_ERR(device);
printk(KERN_WARNING "[target] Error %d while trying to create %s%d", ret, DEVICE_NAME, lp->minor);
goto out_class;
}
return 0;
out_class:
class_destroy(comblock_class);
out_cadd:
cdev_del(&lp->cdev);
out_creg:
unregister_chrdev_region(MKDEV(lp->major, lp->minor), comblock_nr_devs);
return ret;
}
Edit2: Added error to decode the issue in the test app after ioctl call.
ioctl: Inappropriate ioctl for device
I guess this could mean that the magic number in my header could be duplicated? I am currently testing with the COMBLOCK_IOC_RESET parameter which does not takes arguments so it should rule out parameters wrong formatting right?
Edit3: thanks to @rachid-k it was determined that the problem was a 64 kernel with a 32 bit user space. Just check uname -m for kernel and getconf LONG_BIT for userspace? At the end it was solved by changing unlocked_ioctl for compat_ioctl.
Now I have successfully obtained a Segmentation fault. New error means progress. Thanks a lot :)
CodePudding user response:
Edit3: thanks to @rachid-k it was determined that the problem was a 64 bits kernel versus a 32 bits user space.
Just check uname -m
for kernel and getconf LONG_BIT
for user space.
The issue was solved by changing unlocked_ioctl
for compat_ioctl
.
Now I have successfully obtained a Segmentation fault. New error means progress. Thanks a lot :)