Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debuging a simple char driver in Linux failing on device_create()

I wrote a simple char driver and would now like to register it automatically in udev using classes. My code consists of the init function called when the driver is loaded and probe function called when the driver is loading its devices (and of course their counter-equivalent exit and remove). The problem: Once a new device is added my probe function fails when executing the device_create command. Now I am wondering why:

How can I possibly get more information about why this command fails (besides that it fails)? Any argument missing (like is there a problem with my global declaration of fooClass, should I move it to the probe function instead, which does not make sens in my eyes but is shown in many examples)? Or any other overseen error?

Following my code of which I stripped most return verification (like IS_ERR()) and clean up functions for readability. This two variables are defined globally:

static int foo_majNbr;
static struct class *fooClass;

init function:

static int __init foo_init(void)
{
    int rv;
    dev_t devNbr;

    /* Registering driver */
    rv = pci_register_driver(&foo_driver);
        /* ----> see answer below for correct order <---- */

    /* Create device class */
    fooClass = class_create(THIS_MODULE, CLASS_NAME);

    /* Allocate device number, just one device for the moment */
    rv = alloc_chrdev_region(&devNbr, 0, 1, DEVICE_NAME);
    foo_majNbr = MAJOR(devNbr);

    ...
}

and the probe function:

static int __devinit foo_probe(struct pci_dev *dev,
    const struct pci_device_id *devId)
{
    struct foo_dev *foo_dev = 0;
    int rv = 0;

    /* Allocate memory in Kernel (for parameters) */
    foo_dev = kzalloc(sizeof(*foo_dev), GFP_KERNEL);
    foo_dev->pci_dev = dev;
    pci_set_drvdata(dev, foo_dev);

    foo_dev->devNbr = MKDEV(foo_majNbr, 1);

    /* Add class to device */
    foo_dev->dev = device_create(fooClass, NULL, foo_dev->devNbr,
        foo_dev, DEVICE_NAME);
    if (IS_ERR(foo_dev->dev)) {
        /* ----> INDICATES FAILURE HERE <---- */
    }

    /* Add char device */
    cdev_init(&foo_dev->cdev, &foo_fops);
    rv = cdev_add(&foo_dev->cdev, foo_dev->devNbr, 1);

    /* Enabling device */
    rv = pci_enable_device(dev);

    ...
}
like image 297
lorenzli Avatar asked Sep 01 '14 14:09

lorenzli


1 Answers

You should print the error number at least to know the reason.

pr_err("%s:%d error code %d\n", __func__, __LINE__, PTR_ERR(foo_dev->dev));
  • PTR_ERR(): convert an invalid pointer to an error code
  • ERR_PTR(): convert an error code to an invalid pointer
  • IS_ERR(): check if a pointer is invalid and containing an error code
  • IS_ERR_OR_NULL(): like above, plus it checks if the pointer is NULL

in the file Linux/include/uapi/asm-generic/errno-base.h you can find the most common errors. If this error code doesn't help you, you can go to the source code of device_create() an find out where your kind of error is generated and understand why.

Maybe print also the arguments of device_create() before call it.

I know, this is not the kind of answer that magically solve you problem :) but it is a way to proceed and find out the reason.

like image 90
Federico Avatar answered Sep 19 '22 11:09

Federico