EDIT #4:
I figured out that I shouldn't be assigning the attr_groups
to the groups
field in the driver
field of the struct platform_driver
structure. Look for the comment /* WRONGO: should not be assigned here. */
below.
I haven't figured out where I should be assigning it yet ...
My code for a platform driver manages to cause a Kernel "OOPS" when reading a sysfs attribute because of a NULL reference. I'm sure it is a simple oversight in the following code, but I can't see it:
...
static int samples_per_frame = SAMPLE_CHANNEL_COUNT;
DEVICE_INT_ATTR(samples_per_frame, S_IRUGO | S_IWUSR, samples_per_frame);
static struct attribute *attrs[] = {
&dev_attr_samples_per_frame.attr.attr, NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
static const struct attribute_group *attr_groups[] = {
&attr_group, NULL,
};
static struct platform_driver platform = {
.remove = my_remove,
.probe = my_probe,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.groups = attr_groups, /* WRONGO: should not be assigned here. */
.of_match_table = of_match,
},
};
...
which, compiles cleanly (Linux 3.12), and when the module is loaded, the install and probe run smoothly, the attribute appears in the sysfs, yet when I try to read it, a kernel "oops" ensues.
/sys/bus/platform/drivers/my_driver# ls
ff250000.my_driver samples_per_frame uevent
/sys/bus/platform/drivers/my_driver#
/sys/bus/platform/drivers/my_driver# cat samples_per_frame
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = bf1e0000
[00000000] *pgd=3f1d8831, *pte=00000000, *ppte=00000000
Internal error: Oops: 17 [#1] SMP ARM
Modules linked in: my_driver(O)
CPU: 1 PID: 194 Comm: cat Tainted: G O 3.12.0 #6
task: bf054000 ti: bf194000 task.ti: bf194000
PC is at device_show_int+0x2c/0x38
LR is at drv_attr_show+0x2c/0x38
pc : [<80308f58>] lr : [<8030b6fc>] psr: a0000013
sp : bf195ee8 ip : 00000d58 fp : bf195ef4
r10: bf1d1380 r9 : 7e946c20 r8 : bf195f78
r7 : bf1d13d8 r6 : bf1d13c0 r5 : bfbc9e00 r4 : 8055d520
r3 : 00000000 r2 : 80620d58 r1 : 00001000 r0 : bf190000
Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment user
Control: 10c5387d Table: 3f1e004a DAC: 00000015
Process cat (pid: 194, stack limit = 0xbf194248)
Stack: (0xbf195ee8 to 0xbf196000)
5ee0: bf195f04 bf195ef8 8030b6fc 80308f38 bf195f44 bf195f08
5f00: 80166118 8030b6dc 8076ed30 bf14b000 bfbd8d80 00001000 bfbd8d88 bfbd8d80
5f20: 00001000 7e946c20 bf195f78 00000000 bf194000 00001000 bf195f74 bf195f48
5f40: 8010722c 80166084 801061e4 801118c0 00000000 00000000 bfbd8d80 7e946c20
5f60: 00000000 00001000 bf195fa4 bf195f78 8010741c 80107190 00000000 00000000
5f80: 00001000 7e946c20 00000003 00000003 8000ec88 00000000 00000000 bf195fa8
5fa0: 8000ea40 801073dc 00001000 7e946c20 00000003 7e946c20 00001000 00000000
5fc0: 00001000 7e946c20 00000003 00000003 00000001 00000001 00000001 00000003
5fe0: 00000000 7e946bfc 0001079d 76f5e1cc 40000010 00000003 00000000 00000000
[<80308f58>] (device_show_int+0x2c/0x38) from [<8030b6fc>] (drv_attr_show+0x2c/0x38)
[<8030b6fc>] (drv_attr_show+0x2c/0x38) from [<80166118>] (sysfs_read_file+0xa0/0x144)
[<80166118>] (sysfs_read_file+0xa0/0x144) from [<8010722c>] (vfs_read+0xa8/0x148)
[<8010722c>] (vfs_read+0xa8/0x148) from [<8010741c>] (SyS_read+0x4c/0x80)
[<8010741c>] (SyS_read+0x4c/0x80) from [<8000ea40>] (ret_fast_syscall+0x0/0x30)
Code: e5913010 e1a0200c e3a01a01 e3482062 (e5933000)
---[ end trace 5cdf9dda0d86a7db ]---
Segmentation fault
Why am I getting a null reference?
EDIT #1:
If I replace the DEVICE_INT_ATTR()
macro with DEVICE_ATTR()
and provide my own show()
& store()
functions, it works fine.
EDIT #2:
It appears the DEVICE_INT_ATTR()
macro sets the var
field to NULL. hmmm....
EDIT #3:
When I create the struct dev_ext_attribute
structure with DEVICE_INT_ATTR()
macro, I can dump the variables. That is; the addresses of structure, the function pointers and the address of the integer I'm trying to expose in sysfs are all correct when viewed with printk()
in my probe function, but when my home spun show()
function looks at the structure, the struture is at a different address and the var
field is NULL.
The problem arose from assigning the attr_groups
structure here:
static struct platform_driver platform = {
.remove = my_remove,
.probe = my_probe,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.groups = attr_groups, /* WRONGO: should not be assigned here. */
.of_match_table = of_match,
},
};
By doing it in struct class
instead, everything works as expected:
static int __init cz_tdm_init(void)
{
int ret;
pr_info("MODNAME=%s", KBUILD_MODNAME);
/* Dynamically allocate a major number and a
* range of DEV_MINOR_NUMBER_COUNT (1) minor numbers. */
if ((ret = alloc_chrdev_region(&first, 0, DEV_MINOR_NUMBER_COUNT, DRIVER_NAME)) < 0) {
ret = -ENODEV;
goto exit;
}
/* Add the char device to the system. */
cdev_init(&cdev, &fops);
if ((ret = cdev_add(&cdev, first, DEV_MINOR_NUMBER_COUNT)) < 0) {
ret = -ENODEV;
goto exit;
}
resources.cdev = 1;
/* Create a class entry in sysfs */
if ((class = class_create(THIS_MODULE, "coveloz")) == NULL) {
pr_err("Couldn't create 'struct class' structure.");
ret = -ENODEV;
goto exit;
}
class->dev_groups = attr_groups; /* RIGHTO */
/* Create the /dev/ file system entry */
/* return value ignored: there's a 'struct class' to 'struct device' mapping */
if (device_create(class, NULL, first, NULL, DRIVER_NAME) == NULL) {
pr_err("Couldn't create entry in '/dev/' file system.");
ret = -ENODEV;
goto exit;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With