Home > Blockchain >  char driver device issue when using macros
char driver device issue when using macros

Time:07-29

Context

I was mandated to make a char driver for a specific hardware package. This driver will need to have two devices living under the same class. For now, I'm not really minding the whole file_operations struct as I'm mainly having problem with the sysfs and attribute definition. I'm working on a VMware and the driver must be able to run on linux v5.10.X.

What has been done

So far, I have initialized my driver (outside of any functions) as such:

static struct class * dev_class; 
static struct device * some_dev_1;
static struct device * some_dev_2; 
static struct cdev some_cdev_1;
static struct cdev some_cdev_2;
static dev_t dev_num_1 = MKDEV(MAJOR_NUMBER_1, 0); /* Major is 235 */
static dev_t dev_num_2 = MKDEV(MAJOR_NUMBER_2, 0); /* Major is 240 */

So I have two devices (for sysfs) with their cdev (for user interface). Inside the device_bringup function (see below) , I wanted to fill up the attribute_group ** groups structure with my show/store method as seen in the struct documentation. I tried following this tutorial and this one, which yielded the following code:

int device_bringup(char * device_1_name, char * device_2_name){
    int return_value = SUCCESS;

    some_dev_1 = device_create(dev_class, NULL, dev_num_1, NULL, device_1_name); /* Create/Registers it on sysfs */
    some_dev_2 = device_create(dev_class, NULL, dev_num_2, NULL, device_2_name);

    if(some_dev_1 == NULL){
        printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #1 !\n");
        return_value = init_error_manager(1);
    }

    printk(KERN_ALERT KBUILD_MODNAME " Device #1 was created !\n");

    if(some_dev_2 == NULL){
        printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #2 !\n");
        return_value = init_error_manager(2);
    }
    printk(KERN_ALERT KBUILD_MODNAME " Device #2 was created !\n");


    DEVICE_ATTR(first, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_first */
    DEVICE_ATTR(second, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_second */

    struct attribute * some_dev_1_attrs[] = { /* see https://docs.kernel.org/driver-api/driver-model/device.html for info */
        &dev_attr_first.attr, 
        &dev_attr_second.attr, 
        NULL
    };

    struct attribute * some_dev_2_attrs[] = { 
        &dev_attr_first.attr, 
        &dev_attr_second.attr, 
        NULL
    };

    ATTRIBUTE_GROUPS(some_dev_1); /* Create the some_dev_1_groups */
    ATTRIBUTE_GROUPS(some_dev_2); /* Create the some_dev_2_groups */
    
    some_dev_1->groups = some_dev_1_groups;
    some_dev_2->groups = some_dev_2_groups;

    return return_value;
}

The problem

After following both tutorials up there, my kernel cannot build and I receive the following error:

error: initializer element is not constant ATTRIBUTE_GROUPS(some_dev_1);

error: initializer element is not constant ATTRIBUTE_GROUPS(some_dev_2);

I then read on the problem, which seems to sprout from the fact that some_dev_1 and some_dev_2 device pointers are not initialized. However, after reading some stackoverflow threads, I've tried doing:

static struct device * some_dev_1 = kmalloc(sizeof(some_dev_1));

then

static struct device * some_dev_1 = &(struct device){.name = "test"};

and even

static struct device * some_dev_1 = NULL;

However, none of these fixed the issue. I'm pretty sure the problem is easily fixed, but I can only seem to run in circles around it.

CodePudding user response:

Solution

Turns out that the problem came from the fact that the macro ATTRIBUTE_GROUPS(some_dev_1) isn't really using the some_dev_1 defined outside the scope. It merely uses it as a string for the name of the groups.

Using device_create_with_groups without assigning the attribute group directly to the device is what made it work for me. I also had to make the DEVICE_ATTR and the arrays of attribute static in order for them to be available outside of the scope. This is the final, working code:

int device_bringup(char * device_1_name, char * device_2_name){
    int return_value = SUCCESS;

    static DEVICE_ATTR(first, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_tape_deck_first */
    static DEVICE_ATTR(second, 0664, sysfs_show, sysfs_store); /* Name should be dev_attr_tape_deck_second */

    static struct attribute * some_dev_1_attrs[] = {
        &dev_attr_first.attr, 
        &dev_attr_second.attr, 
        NULL
    };

    static struct attribute * some_dev_2_attrs[] = { 
        &dev_attr_first.attr, 
        &dev_attr_second.attr, 
        NULL
    };

    ATTRIBUTE_GROUPS(some_dev_1); 
    ATTRIBUTE_GROUPS(some_dev_2); 
    some_dev_1 = device_create_with_groups(dev_class, NULL, dev_num_1, NULL, some_dev_1_groups, device_1_name);
    some_dev_2 = device_create_with_groups(dev_class, NULL, dev_num_2, NULL, some_dev_2_groups, device_2_name);

    if(IS_ERR_OR_NULL(some_dev_1)){ 
        printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #1 !\n");
        return_value = PTR_ERR(some_dev_1);
    }

    printk(KERN_ALERT KBUILD_MODNAME " Device #1 was created !\n");

    if(IS_ERR_OR_NULL(some_dev_2)){
        printk(KERN_ALERT KBUILD_MODNAME " Cannot create the Device #2 !\n");
        return_value = PTR_ERR(some_dev_2);
    }
    printk(KERN_ALERT KBUILD_MODNAME " Device #2 was created !\n");

    return return_value;
}

This is not the final version of it as I'll have to make the function more atomic and not create two devices in the same function, but only one per call. However, the code above is what fixed my problem. I hope it'll help anyone encountering this sort of issue.

  • Related