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.
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
I would like to expose some settings of my device via sysfs. If I understand it right, a driver can have multiple devices, so there should be one instance of the settings variable per device. This should be easy enough using DEVICE_ATTR macro.
Checking the sources I noticed there is also DEVICE_INT_ATTR and other with different type. I wonder what is the intended usage, as they use device_show_int functions that get pointer to device, but don't actually use it:
ssize_t device_store_int(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct dev_ext_attribute *ea = to_ext_attr(attr);
int ret;
long new;
ret = kstrtol(buf, 0, &new);
if (ret)
return ret;
if (new > INT_MAX || new < INT_MIN)
return -EINVAL;
*(int *)(ea->var) = new;
/* Always return full write size even if we didn't consume all */
return size;
}
EXPORT_SYMBOL_GPL(device_store_int);
I searched kernel sources for those macros, and it seems that they work with a global variable. For example DEVICE_INT_ATTR is used in drivers/base/core.c for mca_cfg.tolerant:
static DEVICE_INT_ATTR(tolerant, 0644, mca_cfg.tolerant);
but the mca_cfg varaible is actually global, not tied to a device:
struct mca_config mca_cfg __read_mostly = {
.bootlog = -1,
/* ... */
.tolerant = 1,
.monarch_timeout = -1
};
which makes it look like a driver (not device) attribute.
I also checked commit that adds these macros but it did not help me much.
You correctly take that DEVICE_INT_ATTR and other macros from that family are for "global" attributes, which store and show methods doesn't use dev parameter.
If you want to define attribute, which can be bound to several devices, then you could write your own store and show methods which gets information about the value from dev.
E.g. by having device
struct my_device
{
struct device base;
int repetition;
};
you could expose its repetition field in the attribute using following show method:
// Shows value of 'repetition' field of my_device.
static ssize_t repetition_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real device structure.
struct my_device* my_dev = container_of(dev, struct my_device, base);
return sprintf(buf, "%d\n", my_dev->repetition);
}
Structure of such attribute could be initialized using __ATTR macro:
static struct device_attribute repetition_attr =
__ATTR(repetition, S_IRUGO, repetition_show, NULL);
Making "generic" attributes
Assume your device struct contains many int fields, which you want to expose via attributes:
struct my_device
{
struct device base;
int repetition;
int counter;
int value;
};
In that case you could generalize attribute definition, so you don't need to create many show (and store) functions.
E.g. you could store offset of the exposed field in your attribute structure:
struct device_bounded_attr
{
struct device_attribute base_attr;
size_t field_offset;
};
// Initializer for struct device_bounded_attr
//
// - real_device_type - type of the actual device structure
// - device_member - member of type 'struct device' in the actual device structure
// - field_member - member in actual device structure which you want to expose as attribute.
#define BOUNDED_ATTR(name, mode, show, store, real_device_type, device_member, field_member) { \
.base_attr = __ATTR(name, mode, show, store), \
.field_offset = offsetof(real_device_type, field_member) - offsetof(real_device_type, device_member)
}
Using this field, you could rewrite show method as follows:
// Shows value of integer field, stored in device.
static ssize_t bounded_attr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
// Obtain pointer to the real attribute structure.
struct device_bounded_attr* bounded_attr = container_of(attr, struct device_bounded_attr, base_attr);
// Having offset of the field, calculate pointer to it
int field_ptr* = (int*)(((char*)dev) + bounded_attr->field_offset);
return sprintf(buf, "%d\n", *field_ptr);
}
So attributes can be declared as follows:
static struct device_bounded_attr repetition_attr =
BOUNDED_ATTR(repetition, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, repetition);
static struct device_bounded_attr counter_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, counter);
static struct device_bounded_attr value_attr =
BOUNDED_ATTR(counter, S_IRUGO, bounded_attr_show, NULL, struct my_device, base, value);
I'm working on an embedded project. Our board is using Linux kernel v3.16.7. I'm working on supporting a couple of peripheral LEDs that monitor activity. I've successfully modified the boot procedure to load the drivers and create sysfs entries in /sys/class/leds/, which is great. I've also attached a oneshot trigger to the leds so I can echo 1 > shot from within /sys/class/leds/actled1\:green/ and the led blinks. Exactly what I want.
However, I want to configure the delays for each LED when I instantiate the driver during boot, and I'm not clear on how to do that. The driver creates sysfs entries in /sys/class/leds/actled1\:green/ called delay_on and delay_off, and I can write to them from userspace to configure the delays, but it should be possible to set their initial values from from kernel space during instantiation. I also want to be able to set the invert parameter (which is just another sysfs entry just like the delays).
How can I configure the parameters of an led trigger when I instantiate the driver from kernel space?
Below is how I instantiate the LED GPIOs. First I set up the structs required:
static struct gpio_led my_leds[] __initdata = {
{
.name = "actled1:green",
.default_trigger = "oneshot"
.gpio = ACTIVITY_LED_GPIO_BASE + 0,
.active_low = true,
},
{
.name = "actled2:red",
.default_trigger = "oneshot"
.gpio = ACTIVITY_LED_GPIO_BASE + 1,
.active_low = true,
},
};
static struct gpio_led_platform_data my_leds_pdata __initdata = {
.num_leds = ARRAY_SIZE(my_leds),
.leds = my_leds,
};
Then, I call this function to create the platform devices:
static int __init setup_my_leds (void)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_alloc("leds-gpio", -1);
if (!pdev) {
return -ENOMEM;
}
ret = platform_device_add_data(pdev,
&my_leds_pdata,
sizeof(my_leds_pdata));
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
ret = platform_device_add(pdev);
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
return 0;
}
The definition for the gpio_led struct is in include/linux/leds.h line 327, and the definition for gpio_led_platform_data is in line 341 of the same file. The definition of platform_device_add_data is in drivers/base/platform.c line 284.
It may be useful to look at the source for the oneshot trigger (drivers/leds/trigger/ledtrig-oneshot.c) in order to answer the question. Also relevant is the "leds-gpio" driver (drivers/leds/leds-gpio.c).
I suspect the answer is somewhere in drivers/base/platform.c and the associated documentation, but I'm not seeing any functions that deal with the data I need.
To address some of the information that I inadvertently left out:
the boot loader sets the kernel arguments, and we can't modify the boot loader. that's fine; the values i want to set are constants and i can hard-code them in.
the driver is baked into the kernel at compile time (and, i presume, loaded by the bootloader) rather than loading a .ko with modprobe later.
i would love a general way to set arbitrary trigger parameters, not only oneshot's delay_on / delay_off. for example, oneshot's invert parameter.
i'm totally fine modifying oneshot / creating new triggers. in fact, once i get it working with oneshot, i'll need to create a new trigger that expands upon oneshot (which is also the reason i need to set arbitrary parameters).
There are a few issues and I think I've found the solutions, but even though you provided a good deal of info, there were some things missing, so I'll enumerate for all possible scenarios, so be patient ...
(1) Getting the initial values you want to set. I presume you have already figured this out, but ... You can get these from kernel cmdline parsing (e.g. you add the values to /boot/grub2/grub.cfg as myleds.delay_on=.... If you're loading via modprobe, you set a module parameter. These could also be a config file as in myleds.config_file=/etc/sysconfig/myleds.conf
(2) You could set them inside your setup_my_leds [except for the recalcitrance of oneshot_trig_activate--which we'll deal with soon enough]. From drivers/base/platform.c:
/**
* arch_setup_pdev_archdata - Allow manipulation of archdata before its used
* #pdev: platform device
*
* This is called before platform_device_add() such that any pdev_archdata may
* be setup before the platform_notifier is called. So if a user needs to
* manipulate any relevant information in the pdev_archdata they can do:
*
* platform_device_alloc()
* ... manipulate ...
* platform_device_add()
*
* And if they don't care they can just call platform_device_register() and
* everything will just work out.
*/
So, with that in mind, let's change your setup function slightly:
static int __init setup_my_leds (void)
{
struct platform_device *pdev;
int ret;
// get initial values you want to set, possibly storing away for later use
my_leds_get_init_values(...);
pdev = platform_device_alloc("leds-gpio", -1);
if (!pdev) {
return -ENOMEM;
}
// Choice (1): set your initial values in my_leds_pdata here
my_leds_set_init_values(&my_leds_pdata);
// NOTE: just does kmemdup and sets pdev->dev.platform_data
ret = platform_device_add_data(pdev,
&my_leds_pdata,
sizeof(my_leds_pdata));
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
// Choice (2): set your initial values in pdev->dev.platform_data here
my_leds_set_init_values(pdev->dev.platform_data);
ret = platform_device_add(pdev);
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
return 0;
}
(3) Unfortunately, since you're using .default_trigger = "oneshot", the above data will get blasted by oneshot_trig_activate in drivers/leds/trigger/ledtrig-oneshot.c. So, we need to deal with that.
Option (A): Assuming you can rebuild the whole kernel as you choose, just modify oneshot_trig_activate in ledtrig-oneshot.c and remove the the lines that use DEFAULT_DELAY. This is only really useful if you know that it's not used by anything else in your system that might need the default values.
Option (B): If you're not allowed to modify ledtrig-oneshot.c, but are allowed to add new triggers to drivers/leds/trigger, copy the file to (e.g.) ledtrig-oneshot2.c and do the changes there. You'll need to change the .name to .name = "oneshot2". The easy way [in vi, of course :-)] is :%s/oneshot/oneshot2/g. You'll also need to add a new entry in the Kconfig and Makefile for this. Then, change your struct definition to use the new driver: .default_trigger = "oneshot2"
Option (C): Assuming you can't [or don't want to] touch the drivers/leds/trigger directory, copy ledtrig-oneshot.c to your driver directory [renaming as appropriate]. Do the edits from option (B) above there. With some trickery in your Makefile, you can get it to build both my_led_driver.ko and ledtrig-oneshot2.ko. You'll need modify your Kconfig, possibly adding a depends on LED_TRIGGERS for the led trigger driver. You could also put the two into separate subdirectories and the individual Makefile/Kconfig might be simpler: my_led/my_driver and my_led/my_trigger
Option (C) would be more work up front, but might be cleaner and more portable in the long run. Of course, you could do option (A) for proof-of-concept, then do option (B), and do the "final ship" as option (C).
An alternate way for when you set the initial values: Remember the comment for my_leds_get_init_values was possibly storing away for later use. You could change oneshot2_trig_activate to call it instead of using DEFAULT_DELAY. I don't like this quite as much and prefer the solutions that simply neuter oneshot_trig_activate's offensive behavior. But, with some testing, you may find that this is the way you have to do it.
Hopefully, the above will work. If not, edit your question with additional info and/or restrictions [and send me a comment], and I'll be glad to update my answer [I've been doing drivers for 40+].
UPDATE: Okay, herein is a fully annotated and modified LED trigger driver that you can use as a drop in replacement for drivers/led/trigger/ledtrig-oneshot.c.
Because the invert parameter can not be passed directly through any standard struct you have access to in your setup function [i.e. it's stored in a private struct inside the trigger driver], remove the "Choice (1)" and "Choice (2)". We'll set them all at once inside the [modified] oneshot_trig_activate.
Also, the init parameters you want must be set up and stored as globals by the my_leds_get_init_values so the trigger driver can find them. That is, there is no way to do this cleanly (e.g. with a pointer to a private struct that gets passed around) as the structs you have access to in setup don't have a field for this. See the top part of the trigger driver for discussion on this.
My first step was to annotate the base driver with descriptive comments. There were no comments in it, except for K&R style for copyright and a single one-liner. My annotations are ANSI ("//") comments.
If I were taking over the driver, I would add these and leave them in. However, my level of comments might be considered "over-commenting" according to the kernel style guide and might be considered "cruft", particularly for a driver that is this straightforward.
Next step was to add the necessary changes. All places that have additions/changes are marked with a comment block that starts with "C:". These are the important places to look. Note that these comments are legitimate candidates to leave in. In other more complex drivers, the level of commenting is up to the author. The "C:" is just to highlight the places for you.
With the annotations, a straight line read through might be easier now. Also, a diff -u might also help. If you've got everything under git, so much the better.
Because of all this, I'd remove the "Option (A)" [direct modification of the original file] and do "Option (B)" or "Option (C)" only.
The trigger driver uses all static definitions, so the global edit I suggested before is not needed. I did do .name = "myled_oneshot";, so you'll need to match that with .default_trigger = "myled_oneshot";. Feel free to use my_leds_whatever to be consistent with your existing naming convention. When I do this for myself, I usually use my initials, so it becomes ce_leds_whatever--YMMV
Anyway, here's the entire modified trigger driver. Note that I've done the editing, but I've not tried to compile/build it.
/*
* One-shot LED Trigger
*
* Copyright 2012, Fabio Baltieri <fabio.baltieri#gmail.com>
*
* Based on ledtrig-timer.c by Richard Purdie <rpurdie#openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include "../leds.h"
// C: we need to get access to the init data populated by the setup function
// we have the "clean way" with a struct definition inside a header file and
// the "dirty way" using three separate int globals
// in either case, the externs referenced here must be defined in the "my_leds"
// driver as global
// C: the "clean way"
// (1) requires that we have a path to the .h (e.g. -I<whatever)
// (2) this would be easier/preferable for the "Option (C)"
// (3) once done, easily extensible [probably not a consideration here]
#ifdef MYLED_USESTRUCT
#include "whatever/myled_init.h"
extern struct myled_init myled_init;
// C: the "ugly way"
// (1) no need to use a separate .h file
// (2) three separate global variables is wasteful
// (3) more than three, and we really should consider the "struct"
#else
extern int myled_init_delay_on;
extern int myled_init_delay_off;
extern int myled_init_invert;
#endif
#define DEFAULT_DELAY 100
// oneshot trigger driver private data
struct oneshot_trig_data {
unsigned int invert; // current invert state
};
// arm oneshot sequence from sysfs write to shot file
static ssize_t led_shot(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
led_blink_set_oneshot(led_cdev,
&led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
oneshot_data->invert);
/* content is ignored */
return size;
}
// show invert state for "cat invert"
static ssize_t led_invert_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
return sprintf(buf, "%u\n", oneshot_data->invert);
}
// set invert from sysfs write to invert file (e.g. echo 1 > invert)
static ssize_t led_invert_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
oneshot_data->invert = !!state;
if (oneshot_data->invert)
led_set_brightness_async(led_cdev, LED_FULL);
else
led_set_brightness_async(led_cdev, LED_OFF);
return size;
}
// show delay_on state for "cat delay_on"
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}
// set delay_on from sysfs write to delay_on file (e.g. echo 20 > delay_on)
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_on = state;
return size;
}
// show delay_off state for "cat delay_off"
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}
// set delay_off from sysfs write to delay_off file (e.g. echo 20 > delay_off)
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_off = state;
return size;
}
// these are the "attribute" definitions -- one for each sysfs entry
// pointers to these show up in the above functions as the "attr" argument
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
static DEVICE_ATTR(shot, 0200, NULL, led_shot);
// activate the trigger device
static void oneshot_trig_activate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data;
int rc;
// create an instance of the private data we need
oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
if (!oneshot_data)
return;
// save the pointer in the led class struct so it's available to other
// functions above
led_cdev->trigger_data = oneshot_data;
// attach the sysfs entries
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out_trig_data;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
rc = device_create_file(led_cdev->dev, &dev_attr_invert);
if (rc)
goto err_out_delayoff;
rc = device_create_file(led_cdev->dev, &dev_attr_shot);
if (rc)
goto err_out_invert;
// C: this is what the driver used to do
#if 0
led_cdev->blink_delay_on = DEFAULT_DELAY;
led_cdev->blink_delay_off = DEFAULT_DELAY;
#endif
led_cdev->activated = true;
// C: from here to the return is what the modified driver must do
#ifdef MYLED_USESTRUCT
led_cdev->blink_delay_on = myled_init.delay_on;
led_cdev->blink_delay_off = myled_init.delay_off;
oneshot_data->invert = myled_init.invert;
#else
led_cdev->blink_delay_on = myled_init_delay_on;
led_cdev->blink_delay_off = myled_init_delay_off;
oneshot_data->invert = myled_init_invert;
#endif
// C: if invert is off, nothing to do -- just like before
// if invert is set, we implement this as if we just got an instantaneous
// write to the sysfs "invert" file (which would call led_invert_store
// above)
// C: this is a direct rip-off of the above led_invert_store function which
// we can _not_ call here directly because we don't have access to the
// data it needs for its arguments [at least, not conveniently]
// so, we extract the one line we actually need
if (oneshot_data->invert)
led_set_brightness_async(led_cdev, LED_FULL);
return;
// release everything if an error occurs
err_out_invert:
device_remove_file(led_cdev->dev, &dev_attr_invert);
err_out_delayoff:
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out_trig_data:
kfree(led_cdev->trigger_data);
}
// deactivate the trigger device
static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
// release/destroy all the sysfs entries [and free the private data]
if (led_cdev->activated) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
device_remove_file(led_cdev->dev, &dev_attr_invert);
device_remove_file(led_cdev->dev, &dev_attr_shot);
kfree(oneshot_data);
led_cdev->activated = false;
}
/* Stop blinking */
led_set_brightness(led_cdev, LED_OFF);
}
// definition/control for trigger device registration
// C: changed the name to "myled_oneshot"
static struct led_trigger oneshot_led_trigger = {
.name = "myled_oneshot",
.activate = oneshot_trig_activate,
.deactivate = oneshot_trig_deactivate,
};
// module init function -- register the trigger device
static int __init oneshot_trig_init(void)
{
return led_trigger_register(&oneshot_led_trigger);
}
// module exit function -- unregister the trigger device
static void __exit oneshot_trig_exit(void)
{
led_trigger_unregister(&oneshot_led_trigger);
}
module_init(oneshot_trig_init);
module_exit(oneshot_trig_exit);
MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri#gmail.com>");
MODULE_DESCRIPTION("One-shot LED trigger");
MODULE_LICENSE("GPL");
As you can see in ledtrig-oneshot.c, the delay is always initialized with DEFAULT_DELAY. Unfortunately, if you want to be able to configure a different value at startup, this is a mechanism you will have to implement..
As Craig answered it should be from kernel command line options, but there could be a problem with embedded systems where the boot-loader passes the command line parameters and the boot-loaders cannot be modified, they are usually OTP . In that case I see only 2 options
hard coding in the kernel init function
as mac address is stored in eeprom for the nic driver to read, if the values can be stored in a flash (nor) and the value read on boot. This can be done after creating the mtd partitions during kernel boot.
I trying to pass data from the kernel space to the user space and all I see is 'killed' in the console, and when I try to look at dmesg I see:
unable to handle kernel paging request
My kernel module init function:
static int __init module_init_function(void) {
struct file_operations fops = {
.owner = THIS_MODULE
};
struct class *m_c;
struct device *dev;
DEVICE_ATTR(fw_dev, 0777, show_func, store_func);
/* Create the user interface device */
major = register_chrdev(0, "fw_status", &fops);
m_c = class_create(THIS_MODULE, "fw_class");
dev = device_create(m_c, NULL, MKDEV(major, 0), NULL, "fw_dev");
device_create_file(dev, &dev_attr_fw_dev);
return 0;
}
And here is my show function:
static ssize_t show_func(struct device *dev, struct device_attribute *attr, char *buf) {
return snprintf(buf,PAGE_SIZE, "%d,%d", accepted_packets, dropped_packets);
}
What am I doing wrong?
DEVICE_ATTR macro creates a device_attribute in the scope it's called. Since it's in your init function, that device_attribute is lost after the module is initialized. (On a sidenote, it would still be deleted even if it was static inside init function. Since your init function has __init, the function will be purged from memory after module initialization)
Try calling DEVICE_ATTR globally.
Same goes for file_operations, it should also be global. Kernel stores them as a pointer and does not copy the entire structure, so that you can modify it later.
You can browse kernel source to see how other modules are implemented. A quick search reveals that DEVICE_ATTR is always used globally.
Also, you probably do not need snprintf(9), "%d,%d" will not exceed buf's size anyway.
I am writing a simple char driver which accesses a PCI card. It is registered to sysfs with the help of a new class. Now I would like to access multiple parameters (i.e. version, status, control...) of the device in a convenient way. I thought of registering multiple attributes to the device (via device_create_file()).
To do so I create my own device structure foo_dev for which I allocate memory and store all device informations in it (i.e. struct device). Once the attribute gets called I wanted to recover my structure by using container_of() as shown in my code (stripped of return verification for readability):
static const ssize_t foo_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct foo_dev *foo_dev = container_of(dev, struct foo_dev,
dev);
mutex_lock(&mutex);
u32 data = ioread32(foo_dev->bar + 0x2020);
mutex_unlock(&mutex);
return sprintf(buf, "%d\n", data);
}
The problem: As soon as I write to the device, the kernel aborts with a Bad IO access at port 0x2020 (return inl(port)) coming from the ioread32() call. Having investigated further and printed other informations stored in foo_dev I see that the structure is completely empty - container_of() apparently does not reconstruct my original structure. For completeness here the device initialization in the probe() function:
...
foo_dev->dev = device_create(fooClass, NULL, foo_dev->devNbr,
foo_dev, DEVICE_NAME);
cdev_init(&foo_dev->cdev, &foo_fops);
rv = cdev_add(&foo_dev->cdev, foo_dev->devNbr, 1);
rv = pci_enable_device(dev);
...
device_create_file(foo_dev->dev, &dev_attr_bar);
...
What do I probably wrong? How can I investigate further on what I actually receive as struct dev in foo_show()?
container_of() does not work with an embedded pointer.
It works only for a structure that is directly embedded in another structure:
struct foo_dev {
...
struct device dev;
...
};
(You then have to use device_initialize().)
Having a closer look at device_create() one can see that the initialized device actually gets a pointer on its parent structure via dev_set_drvdata(). Instead of using container_of() in the attribute routine one can then recover the foodev structure with dev_get_drvdata(). The routine becomes:
static const ssize_t foo_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct foo_dev *foo_dev = dev_get_drvdata(dev);
mutex_lock(&mutex);
u32 data = ioread32(foo_dev->bar + 0x2020);
mutex_unlock(&mutex);
return sprintf(buf, "%d\n", data);
}
I've got a custom piece of FPGA logic which I've implemented a functioning char driver for, and I'm trying to get it to work as a network driver as well now. I'm following along as best I can using the LDD book, snull code, and loopback.c, dummy.c, and other drivers from the kernel as examples.
The code I've got below (obviously ripped out of the larger file) is the networking driver code I have so far; I can successfully insmod, and "ifconfig optical0" shows that my MTU & flags are correct so I know at least it's being registered.
Now I'm trying to implement the struct net_device's private field (void* priv) to store stats and other things in, and it's resulting in segfaults. Here's the code so far:
struct serdes_device {
struct serdes_regs __iomem *regs;
struct serdes_dma *tx_dma,
*rx_dma;
struct device *dev;
struct net_device *netdev;
struct resource serdes_res;
struct cdev cdev;
int major, minor;
int tx_kbps;
int rx_kbps;
};
struct optical_priv {
struct serdes_device *serdes_dev;
struct net_device_stats stats;
};
static const struct net_device_ops optical_netdev_ops = {
.ndo_get_stats = optical_stats,
.ndo_start_xmit = optical_xmit,
/* and so on, not including the ops functions for brevity */
};
void optical_setup(struct net_device *dev)
{
dev->netdev_ops = &optical_netdev_ops;
dev->destructor = free_netdev;
dev->tx_queue_len = 1;
dev->type = ARPHRD_NONE;
dev->hard_header_len = SERDES_FRAME_HEADER_LENGTH;
dev->mtu = SERDES_FRAME_TOTAL_LENGTH;
dev->addr_len = 0;
dev->flags &= ~IFF_BROADCAST;
/* more flags & features cut */
}
static int optical_init(struct net_device *netdev)
{
int err;
struct optical_priv *priv;
netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);
if (!netdev)
return -ENOMEM;
err = register_netdev(netdev);
if (err < 0)
goto err;
priv = netdev_priv(netdev);
printk(KERN_WARNING "priv is at address 0x%p\n", priv);
return 0;
err:
free_netdev(netdev);
return err;
}
static int serdes_of_probe(struct platform_device *op)
{
struct serdes_device *serdes_dev;
struct optical_priv *priv;
int err = 0;
serdes_dev = kzalloc(sizeof(struct serdes_device), GFP_KERNEL);
/* A bunch of unrelated openfirmware & cdev code removed. */
err = optical_init(serdes_dev->netdev);
if (err < 0)
dev_err(serdes_dev->dev, "Error %d initing optical link\n", err);
priv = netdev_priv(serdes_dev->netdev);
if (!priv)
dev_err(serdes_dev->dev, "priv is null... \n");
dev_info(serdes_dev->dev, "priv is at 0x%p\n", priv);
dev_info(&op->dev, "Done probing.\n");
return 0;
out_free_serdes:
kfree(serdes_dev);
out_return:
return err;
}
So as I understand it, I'm allocating space for and initializing my serdes device (which I know works as a char driver). Then I call optical_init which allocs, registers, and configures serdes_dev->netdev. alloc_netdev is supposed to allocate room for the (void *)priv field as well which is why sizeof(struct optical_priv) is passed in.
The output after insmod of the two print statements in there look like this:
priv is at address 0xdd2de500
priv is at 0x00000500
Done probing.
and obviously trying to access priv in any way after that causes a segfault. I think my confusion is about why netdev_priv() returns the two different values when called from the two different functions-- shouldn't it be allocated and correct anytime after alloc_netdev?
e: I think I'm also confusing myself by looking for examples in too many drivers, from too many eras, steeped in the history of hardware and kernel APIs past... if anyone has a good basic driver recommendation for me to start with, I'm more than happy reading code to learn.
I think that the problem is here:
err = optical_init(serdes_dev->netdev);
You are passing an invalid pointer to a netdev structure. Then in optical_init() you change the local value of the pointer with:
netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);
Now you have a valid pointer to a struct net_device. But this pointer value does not apply to the serdes_dev->netdev variable but only locally in optical_init(). In consequence of this, also the pointer to priv is invalid. Here a little example that show the issue:
#include <stdio.h>
#include <stdlib.h>
struct test {
int first;
int *second;
};
void allocate_memory(int *ptr)
{
ptr = malloc(10);
printf("while allocating %p\n", ptr);
}
int main(void) {
struct test *p1;
p1= malloc(sizeof(struct test));
printf("before %p\n", p1->second);
allocate_memory(p1->second);
printf("after %p\n", p1->second);
free(p1);
free(p2);
return EXIT_SUCCESS;
}
The output is:
before (nil)
while allocating 0x23ee030
after (nil)