The times are different, and even LEDs have become sub-systems. I have analyzed the general model provided by the kernel and haven't written an article for a long time!
The code is located in drivers/leds. Let's take a look at the Makefile model file:
# LED Core
Obj-$ (CONFIG_NEW_LEDS) + = led-core.o
Obj-$ (CONFIG_LEDS_CLASS) + = led-class.o
Obj-$ (CONFIG_LEDS_TRIGGERS) + = led-triggers.o
Let's look at the led-core file. This file is extremely personalized and contains four lines of subject content.
DECLARE_RWSEM (leds_list_lock );
EXPORT_SYMBOL_GPL (leds_list_lock );
LIST_HEAD (leds_list); // link the global linked list of all LEDs
EXPORT_SYMBOL_GPL (leds_list );
Let's take a look at the led-class.c function. Here we will first introduce the core structure of the led.
Struct led_classdev {
Const char * name; // name
Int brightness; // The brightness value, which can also be used to indicate the switching feature.
Int max_brightness; // maximum allowed Brightness Value
Int flags; // flag
/* Lower 16 bits reflect status */
# Define led_suincluded (1 <0)
/* Upper 16 bits reflect control information */
# Define LED_CORE_SUSPENDRESUME (1 <16)
/* Set LED brightness level */
/* Must not sleep, use a workqueue if needed */
Void (* brightness_set) (struct led_classdev * led_cdev, // core callback function. When you set the brightness attribute file in the led interface under/sys/class/leds, calls back this function.
Enum led_brightness brightness );
/* Get LED brightness level */
Enum led_brightness (* brightness_get) (struct led_classdev * led_cdev); // core callback function, called when the current value of the led is obtained
/* Activate hardware accelerated blink, delays are in
* Miliseconds and if none is provided then a sensible default
* Shocould be chosen. The call can adjust the timings if it cant
* Match the values specified exactly .*/
Int (* blink_set) (struct led_classdev * led_cdev,
Unsigned long * delay_on,
Unsigned long * delay_off );
Struct device * dev; // embedded Standard device Model
Struct list_head node;/* LED Device list * // The mount point of the global led Device mentioned above
Const char * default_trigger;/* Trigger to use */
# Ifdef CONFIG_LEDS_TRIGGERS
/* Protects the trigger data below */
Struct rw_semaphore trigger_lock;
Struct led_trigger * trigger;
Struct list_head trig_list;
Void * trigger_data;
# Endif
};
Now let's talk about the initialization function:
Static int _ init leds_init (void)
{
Leds_class = class_create (THIS_MODULE, "leds ");
If (IS_ERR (leds_class ))
Return PTR_ERR (leds_class );
Leds_class-> suspend = led_suspend;
Leds_class-> resume = led_resume;
Leds_class-> dev_attrs = led_class_attrs; // attribute file, interface under sys. Take a closer look.
Return 0;
}
The function generates the leds sub-directory under the/sys/class directory. All devices registered according to the led model will appear in this directory.
Static struct device_attribute led_class_attrs [] = {
_ ATTR (brightness, 0644, led_brightness_show, led_brightness_store ),
_ ATTR (max_brightness, 0644, led_max_brightness_show,
Led_max_brightness_store ),
# Ifdef CONFIG_LEDS_TRIGGERS
_ ATTR (trigger, 0644, led_trigger_show, led_trigger_store ),
# Endif
_ ATTR_NULL,
};
It can be seen that the general setting function of the attribute function is led_brightness_store, and the function bit led_brightness_show is obtained, which is similar to the following:
Static ssize_t led_brightness_store (struct device * dev,
Struct device_attribute * attr, const char * buf, size_t size)
{
Struct led_classdev * led_cdev = dev_get_drvdata (dev); // obtain the led device information
Ssize_t ret =-EINVAL;
Char * after;
Unsigned long state = simple_strtoul (buf, & after, 10 );
Size_t count = after-buf;
If (isspace (* after ))
Count ++;
If (count = size ){
Ret = count;
If (state = LED_OFF)
Led_trigger_remove (led_cdev );
Led_set_brightness (led_cdev, state );
}
Return ret;
}
Follow up on the led_set_brightness function:
Static inline void led_set_brightness (struct led_classdev * led_cdev,
Enum led_brightness value)
{
If (value> led_cdev-> max_brightness)
Value = led_cdev-> max_brightness;
Led_cdev-> brightness = value;
If (! (Led_cdev-> flags & led_susponded )){
# Ifdef CONFIG_HAS_EARLYSUSPEND
If (queue_brightness_change (led_cdev, value )! = 0)
# Endif
Led_cdev-> brightness_set (led_cdev, value );
}
}
It can be seen that it is indeed the callback led_cdev-> brightness_set to complete the setting of this attribute.
Let's take a look at the interface left for us and register the function:
Int led_classdev_register (struct device * parent, struct led_classdev * led_cdev)
{
Led_cdev-> dev = device_create (leds_class, parent, 0, led_cdev,
"% S", led_cdev-> name); // generate directory items for this device under the leds directory
If (IS_ERR (led_cdev-> dev ))
Return PTR_ERR (led_cdev-> dev );
# Ifdef CONFIG_LEDS_TRIGGERS
Init_rwsem (& led_cdev-> trigger_lock );
# Endif
/* Add to the list of leds */
Down_write (& leds_list_lock );
List_add_tail (& led_cdev-> node, & leds_list); // Add the device to the global linked list.
Up_write (& leds_list_lock );
If (! Led_cdev-> max_brightness)
Led_cdev-> max_brightness = LED_FULL; // if the maximum value is not set, set it to 255.
Led_update_brightness (led_cdev );
# Ifdef CONFIG_LEDS_TRIGGERS
Led_trigger_set_default (led_cdev );
# Endif
Printk (KERN_DEBUG "Registered led device: % s ",
Led_cdev-> name );
Return 0;
}
Summary: The led sub-system is briefly analyzed. The code is not much and it is easy to see. The probe function of the main end should be configured with the I/O port function, and the led_cdev function should be filled in, then you can register ^. ^ ~