Many drivers in the current Linux version are based on the device model, and LEDs are no exception.
Simply put, the device model means that the system considers that all devices are attached to the bus, and the corresponding driver is required for the device to work. The device model generates a virtual file system, sysfs, which provides users with a method to access the kernel device from the user space. The path of the device in Linux is/sys. If you want to write a program to access sysfs, you can operate the files in the/sys directory like reading and writing common files.
For the development board based on S3C2440, linux-3.6.6 automatic LED driver only need to change the connection led I/O port, and high and low level response can be. The four LEDs of my Development Board are connected to the 5-8 pins of port B. When the output power is low, the four LEDs are usually lit up, which is consistent with the LED driver of Linux, so no changes are required.
Use menuconfig to configure the kernel. Add the following content to the LED module:
Device Drivers --->
[*] Led support --->
<*> Led class support
<*> Led support for Samsung s3c24xx gpio LEDs
Compile the kernel, download the compiled kernel to the Development Board, and run:
[Root @ zhaocj/] # ls
Bin etc lib proc sys USR
Dev home linuxrc sbin temp
[Root @ zhaocj/] # cd sys
[Root @ zhaocj/sys] # ls
Block class devices FS Module
Bus Dev firmware kernel power
Go to the Sys directory, and we can see that there are some subdirectories under this directory.
[Root @ zhaocj/sys] # cd class
[Root @ zhaocj class] # ls
Backlight hidraw LEDs RTC VC
BDI hwmon mem sound video_output
Block i2c-adapter MISC spi_master vtconsole
Firmware i2c-dev (mmc_host spidev watchdog)
Gpio input MTD tty
Graphics LCD net UDC
Go to the class directory, and we will see some devices under this directory. LEDs is the LED we want to operate this time.
[Root @ zhaocj class] # cd LEDs
[Root @ zhaocj LEDs] # ls
Backlight led1 led2 led3 led4
Under the LEDs directory, the four LEDs are displayed, which are the four LEDs on the Development Board. In addition, the backlight directory is about the backlight of the LCD and has nothing to do with the LED.
[Root @ zhaocj LEDs] # cd led1
[Root @ zhaocj led1] # ls
Brightness max_brightness Subsystem
Device power uevent
The brightness file is an LED device that can be used to control LEDs.
[Root @ zhaocj led1] # Cat brightness
0
It can be seen that the current status of led1 is disabled. (0 indicates disabled, 1 indicates enabled)
[Root @ zhaocj led1] # Cat> brightness <EOF
> 1
> EOF
# [Root @ zhaocj led1] #
Write 1 to brightness to open the LED. Then led1 will be lit up.
Of course, we can also write user programs to control the four LEDs on the Development Board.
/**********************
* ** LEDs. c **************
**********************/
# Include <stdint. h>
# Include <string. h> # include <fcntl. h> # include <unistd. h> # include <stdio. h> # include <Linux/input. h> # include <unistd. h> int main (INT argc, char * argv []) {int FD, no;/* determines which led to control, open the corresponding file */NO = (INT) argv [1] [3]-48; Switch (NO) {Case 1: FD = open ("/sys/class/LEDs/led1/brightness", o_rdwr); break; Case 2: FD = open ("/sys/class/LEDs/led2/brightness", o_rdwr); break; Case 3: FD = open ("/sys/class/LEDs/led3/bright Ness ", o_rdwr); break; Case 4: FD = open ("/sys/class/LEDs/led4/brightness ", o_rdwr); break; default: Return-1 ;} if (FD <0) {printf ("can not open file. \ n "); Return-1;}/* Turn on or off the LED Operation */If (! Strcmp (argv [2], "on") write (FD, "1", 1); else if (! Strcmp (argv [2], "off") write (FD, "0", 1); close (FD); Return 0 ;}
The above program is only used for simple testing. Compile the file:
Arm-Linux-gcc-O LEDs. c
Download the LEDs file to the temp directory and run:
[Root @ zhaocj/temp] #./LEDs led2 on
Then light up led2.
[Root @ zhaocj/temp] #./LEDs led2 off
Disable led2.
Next I will analyze the LED sub-system that comes with Linux.
In the mach-zhaocj2440.c file, an LED device is created as follows:
/* LEDs */
Static struct s3c24xx_led_platdata zhaocj2440_led1_pdata = {
. Name = "led1 ",
. Gpio = s3c2410_gpb (5 ),
. Flags = s3c24xx_ledf_actlow | s3c24xx_ledf_tristate,
. Def_trigger = "Heartbeat ",
};
Static struct s3c24xx_led_platdata zhaocj2440_led2_pdata = {
. Name = "led2 ",
. Gpio = s3c2410_gpb (6 ),
. Flags = s3c24xx_ledf_actlow | s3c24xx_ledf_tristate,
. Def_trigger = "NAND-disk ",
};
Static struct s3c24xx_led_platdata zhaocj2440_led3_pdata = {
. Name = "led3 ",
. Gpio = s3c2410_gpb (7 ),
. Flags = s3c24xx_ledf_actlow | s3c24xx_ledf_tristate,
. Def_trigger = "mmc0 ",
};
Static struct s3c24xx_led_platdata zhaocj2440_led4_pdata = {
. Name = "led4 ",
. Gpio = s3c2410_gpb (8 ),
. Flags = s3c24xx_ledf_actlow | s3c24xx_ledf_tristate,
. Def_trigger = "",
};
It defines four led data named led1 ~ Led4, which is what we see in the LEDs directory. The pins connected to them are 5 ~ 8. This is defined by the s3c2410_gpb () Macro. The identifier s3c24xx_ledf_actlow indicates that the low level is valid, and the three States represented by s3c24xx_ledf_tristate are invalid. In addition, def_trigger indicates the trigger control. For example, when we perform read/write operations on NAND, led2 will not stop flashing. This function is not used here, so we will ignore it for the moment.
Static struct platform_device zhaocj2440_led1 = {
. Name = "s3c24xx_led ",
. ID = 1,
. Dev = {
. Platform_data = & zhaocj2440_led1_pdata,
},
};
Static struct platform_device zhaocj2440_led2 = {
. Name = "s3c24xx_led ",
. ID = 2,
. Dev = {
. Platform_data = & zhaocj2440_led2_pdata,
},
};
Static struct platform_device zhaocj2440_led3 = {
. Name = "s3c24xx_led ",
. ID = 3,
. Dev = {
. Platform_data = & zhaocj2440_led3_pdata,
},
};
Static struct platform_device zhaocj2440_led4 = {
. Name = "s3c24xx_led ",
. ID = 4,
. Dev = {
. Platform_data = & zhaocj2440_led4_pdata,
},
};
A bus platform device is created above. The device names of the four LEDs are both s3c24xx_led, and the sub-device IDs are from 1 to 4. The device data is the four LEDs defined above. Then add the four LED devices to the device array of the Development Board, that is:
Static struct platform_device * zhaocj2440_devices [] _ initdata = {
......
& Zhaocj2440_led1,
& Zhaocj2440_led2,
& Zhaocj2440_led3,
& Zhaocj2440_led4,
......
};
Finally, during the initialization of the development board system, register the devices in the device array to the system bus one by one, that is:
Static void _ init zhaocj2440_init (void)
{
......
Platform_add_devices (zhaocj2440_devices, array_size (zhaocj2440_devices ));
......
}
In this way, the LED device is created.
A device cannot work, and any device must be driven accordingly. For the LED Based on s3c24xx, its driver is created in the Leds-s3c24xx.c file under the drivers/LEDs directory, namely:
Static struct platform_driver s3c24xx_led_driver = {
. Probe = s3c24xx_led_probe,
. Remove = s3c24xx_led_remove,
. Driver = {
. Name = "s3c24xx_led ",
. Owner = this_module,
},
};
How does the device and driver match? That is, how does a device find its driver? It depends on name. We will find that both platform_device and platform_driver have element names. If their content is consistent, the device and driver will be paired successfully. For LEDs, their names are all s3c24xx_led. After the device and driver match, run the function specified by probe. In short, it completes initialization. To remove a device, run the function specified by remove. The task is to cancel the device. It is especially important for devices that support hot swapping.
Now let's talk about the s3c24xx_led_probe function:
Static int s3c24xx_led_probe (struct platform_device * Dev)
{
Structs3c24xx_led_platdata * pdata = Dev-> Dev. platform_data;
Structs3c24xx_gpio_led * led;
Intret;
/* Allocate memory for the LED */
Led = devm_kzarloc (& Dev-> Dev, sizeof (struct s3c24xx_gpio_led ),
Gfp_kernel );
If (LED = NULL ){
Dev_err (& Dev-> Dev, "no memory for device \ n ");
Return-enomem;
}
/* Save the LED device structure */
Platform_set_drvdata (Dev, LED );
/* Assign a value to the LED struct. Among them, s3c24xx_led_set is the function responsible for operating the LED */
LED-> cdev. brightness_set = s3c24xx_led_set;
LED-> cdev. default_trigger = pdata-> def_trigger;
LED-> cdev. Name = pdata-> name;
LED-> cdev. Flags | = led_core_suspendresume;
LED-> pdata = pdata;
/* Assign an I/O pin to the LED */
Ret = devm_gpio_request (& Dev-> Dev, pdata-> gpio, "s3c24xx_led ");
If (Ret <0)
Returnret;
/* No point in having a pull-up if we are always driving */
/* No need to pull up */
Initi_gpio_setpull (pdata-> gpio, initi_gpio_pull_none );
/* Set the IO pin to input */
If (pdata-> flags & s3c24xx_ledf_tristate)
Gpio_direction_input (pdata-> gpio );
Else
Gpio_direction_output (pdata-> gpio,
Pdata-> flags & s3c24xx_ledf_actlow? 1: 0 );
/* Register our new LED Device */
/* Register a new LED device object
The function is defined in the Led-class.c file under the drivers/LEDs directory */
Ret = led_classdev_register (& Dev-> Dev, & LED-> cdev );
If (Ret <0)
Dev_err (& Dev-> Dev, "led_classdev_register failed \ n ");
Return ret;
}
From the above analysis, we can see that the s3c24xx_led_probe function is mainly used to initialize the LED device. The s3c24xx_led_set function is used to enable and disable led tasks. In this function, gpio_set_value (Pd-> gpio, State) is the specific task to set 1 or 0 for the corresponding PIN.
The Led-class.c file under the drivers/LEDs directory is the underlying core file of the LED subsystem, which is mainly responsible for creating the LED class, and creating the device node, the led_classdev_register function mentioned above is defined in this file. In order to better understand the LED subsystem, let's analyze this file briefly.
During subsystem initialization, The leds_init function is called. Its first code is as follows:
Leds_class = class_create (this_module, "LEDs ");
Create a LEDs class, that is, the LEDs directory we see under the sys/class directory. In addition
Leds_class-> dev_attrs = led_class_attrs;
Is the attribute assigned to this class. Let's take a look at the first code of the led_class_attrs structure:
_ ATTR (brightness, 0644, led_brightness_show, led_brightness_store)
Here, brightness is the device file name for the specific led operation. 0644 is the permission for this file. led_brightness_show is the function called to read the file, and led_brightness_store is the function called to write the file. Reading a file is to read the led status (whether to turn it off or off). Writing a file is to turn it on or off.
Finally, analyze the led_classdev_register function mentioned above. In this function, the device_create function is used to create a device node, that is, the led1 ~ is generated under the LEDs directory ~ Led4 directory. Another important task is to add the device node to the linked list of LEDs.
Here is the analysis of the Linux led subsystem. As long as I understand this subsystem, I can write any driver for gpio read/write operations.