象棋小子 1048272975
Linux内核支持多种驱动框架,其中就支持led这样的设备模型。Linux内核实现了一个虚拟的文件系统sysfs,用于提供一个从用户空间访问内核设备的方法。笔者此处就基于sysfs文件系统的led驱动做一个简单的介绍。
在/drivers/leds目录下实现了Linux内核的led驱动框架。leds目录下Kconfig对led驱动进行配置,如配置led驱动支持、led class支持、特定平台支持等。led驱动可分为平台相关和无关这两部分,平台无关的部分无需任何移植,平台相关的部分需根据特定平台实现led驱动框架的硬件层访问接口。
平台无关部分
# LED Core
obj-$(CONFIG_NEW_LEDS) +=led-core.o
obj-$(CONFIG_LEDS_CLASS)+= led-class.o
obj-$(CONFIG_LEDS_CLASS_FLASH)+= led-class-flash.o
obj-$(CONFIG_LEDS_TRIGGERS)+= led-triggers.o
针对s3c2416特定平台的驱动
obj-$(CONFIG_LEDS_S3C24XX)+= leds-s3c24xx.o
Linux内核把平台驱动程序分成两部分,一部分为设备,另一部分为驱动。Linux内核已经支持多个平台驱动,对于未实现的平台,这是需要移植的部分。
设备信息包含了设备的名字、独有的资源等等一些驱动程序的硬件或自定义信息。通过platform_add_devices(platform_device_register)函数将定义的平台设备注册到内核中,注册成功后,在/sys/devices/platform出现相应的设备目录。
/* LED devices */
static structs3c24xx_led_platdata home_pdata_led4 = {
.gpio =S3C2410_GPE(13),
.flags =S3C24XX_LEDF_TRISTATE,
.name ="led4",
.def_trigger ="timer",
};
static structs3c24xx_led_platdata home_pdata_led5 = {
.gpio =S3C2410_GPE(11),
.flags =S3C24XX_LEDF_TRISTATE,
.name ="led5",
.def_trigger ="nand-disk",
};
static struct s3c24xx_led_platdatahome_pdata_led6 = {
.gpio =S3C2410_GPL(13),
.flags =S3C24XX_LEDF_TRISTATE,
.name ="led6",
};
static structs3c24xx_led_platdata home_pdata_led7 = {
.gpio =S3C2410_GPE(12),
.flags =S3C24XX_LEDF_TRISTATE,
.name ="led7",
};
static structplatform_device home_led4 = {
.name ="s3c24xx_led",
.id = 0,
.dev = {
.platform_data = &home_pdata_led4,
},
};
static structplatform_device home_led5 = {
.name ="s3c24xx_led",
.id = 1,
.dev = {
.platform_data = &home_pdata_led5,
},
};
static structplatform_device home_led6 = {
.name ="s3c24xx_led",
.id = 2,
.dev = {
.platform_data = &home_pdata_led6,
},
};
static structplatform_device home_led7 = {
.name ="s3c24xx_led",
.id = 3,
.dev = {
.platform_data = &home_pdata_led7,
},
};
对于s3c2416平台来说,led驱动在leds-s3c24xx.c文件中。
static structplatform_driver s3c24xx_led_driver = {
.probe =s3c24xx_led_probe,
.remove = s3c24xx_led_remove,
.driver = {
.name ="s3c24xx_led",
},
};
module_platform_driver(s3c24xx_led_driver);
module_platform_driver()为宏定义,会自动去替换module_init()和module_exit()功能,该驱动中module_init()和module_exit()宏分别调用platform_driver_register()和platform_driver_unregister()。platform_driver_register()为驱动注册函数,当驱动加载的时候,就会执行该函数。s3c24xx_led_driver结构体中定义了驱动的name,驱动注册时,内核会遍历比较驱动name与注册的平台设备name,若一致,就将两者绑定,调用探测函数s3c24xx_led_probe()。探测函数s3c24xx_led_probe()通过调用led_classdev_register()这个函数注册一个新的led设备类对象,实现在目录/sys/class/leds创建子目录led_cdev->name和属性brightness、max_brightness等。
在/sys/class/leds目录下有相应的led设备子目录,假设进入led4目录,会看到有brightness、max_brightness等属性文件。通过改变brightness属性文件的值,即可控制led灯的亮灭。
在控制台对brightness文件写入0或1控制led灯的亮灭。
echo 1 >/sys/class/leds/led4/brightness // 亮灯
echo 0 >/sys/class/leds/led4/brightness // 灭灯
也可以通过应用程序访问sysfs中的led设备文件控制led等的亮灭。以led_test.c文件为例如下:
#include"fcntl.h"
#include"unistd.h"
#include"sys/types.h"
#include"stdio.h"
#include"stdlib.h"
void led_control(intindex, int on)
{
int fd;
char buf[1024];
char path[] = "/sys/class/leds/led4/brightness";
if (index == 4) { // led 4
fd = open(path, O_RDWR);
if(fd < 0) {
printf("Open failed\n");
exit(1);
}
sprintf(buf, "%d", on);
if (write(fd, buf, strlen(buf)) != strlen(buf)) {
close(fd);
printf("Write failed\n");
exit(1);
}
close(fd);
}
}
int main(void)
{
while (1) {
led_control(4, 1);
sleep(1);
led_control(4, 0);
sleep(1);
}
}
用arm-linux-gcc静态编译,使之生成arm CPU可执行的指令,并且可脱离任何库独立运行,arm-linux-gcc -static -o led_test led_test.c,生成led_test可执行文件。复制可执行文件到根文件系统,目标板启动后在目录输入./led_test即可执行。
https://pan.baidu.com/s/1slczwhJ
bootloader源码以及使用说明
https://pan.baidu.com/s/1eRDJtNs
Qt5.8官网源码
https://pan.baidu.com/s/1nuGmSqt
本系列例程的根文件系统
https://pan.baidu.com/s/1i5btLGT
opev3.2.0官网源码
https://pan.baidu.com/s/1pLpuHw3
yaffs官网源码
https://pan.baidu.com/s/1bpkZynt
busybox-1.26.2官网源码
https://pan.baidu.com/s/1i4EtjfR
tslib官网源码
https://pan.baidu.com/s/1i5MGRhb
mplayer-1.3.0官网源码
https://pan.baidu.com/s/1sl0fXlr
基于S3C2416修改的linux-4.10.10源码