1

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", &reg_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", &reg_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", &reg_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", &reg_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", &reg_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", &reg_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 :)

6
  • 1
    Please, show also the code which assignes fields to 'struct file_operations' and which creates the device. Commented Sep 15, 2022 at 20:26
  • 1
    And what is the value of errno when ioctl() returns -1? Commented Sep 16, 2022 at 8:36
  • 1
    I did the sane thing and added a call to perror after ioct resulting in this message : "ioctl: Inappropriate ioctl for device" Commented Sep 16, 2022 at 8:38
  • 1
    Are both the kernel and user space running in 64 or 32 bits or is there a discrepancy? The ioctl error seems to be ENOTTY. Commented Sep 16, 2022 at 8:56
  • 1
    So I guess it is a 64 kernel with a 32 bit userspace. In this case I should use compat_ioctl right? Commented Sep 16, 2022 at 9:04

1 Answer 1

1

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 :)

Sign up to request clarification or add additional context in comments.

4 Comments

You can validate your answer no?
I can confirm that using compat_ioctl now allows me to execute the code in the device_ioctl function.
I was speaking about the "acceptation" of your answer (check it to put it in green) ;-)
Ah yes, stackoverflow wont allow me until tomorrow. But will surely do ASAP.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.