linux2.6.32内核中LED类的构架已经完善,移植很简单,只用添加相关资源就可以了。
在mach-fz2440.c中添加相关头文件
/* for LEDS */
#include <linux/leds.h>
添加平台资源初始化文件
/* LED */
static struct gpio_led fz2440_leds[] = {
[0] = {
.name = "led1",
.gpio = S3C2410_GPB(5), //led对应gpio
.active_low = 1, //低电平有效(点亮)
.default_trigger = "heartbeat", //默认触发
.default_state = LEDS_GPIO_DEFSTATE_OFF, //默认状态
},
[1] = {
.name = "led2",
.gpio = S3C2410_GPB(6),
.active_low = 1,
.default_trigger = "nand-disk",
.default_state = LEDS_GPIO_DEFSTATE_OFF,
},
[2] = {
.name = "led3",
.gpio = S3C2410_GPB(7),
.active_low = 1,
.default_trigger = "default-on",
.default_state = LEDS_GPIO_DEFSTATE_OFF,
},
[3] = {
.name = "led4",
.gpio = S3C2410_GPB(8),
.active_low = 1,
.default_trigger = "timer",
.default_state = LEDS_GPIO_DEFSTATE_OFF,
},
};
static struct gpio_led_platform_data s3c_gpio_led_pdata = {
.num_leds = ARRAY_SIZE(fz2440_leds),
.leds = fz2440_leds,
};
static struct platform_device s3c_device_led = {
.name = "leds-gpio",
.id = -1,
.dev = {
.platform_data = &s3c_gpio_led_pdata,
},
};
static struct platform_device __initdata *fz2440_devices[] = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_nand,
&s3c_device_rtc,
&s3c_device_uda1341,
&s3c_device_adc,
&s3c_device_hwmon,
&s3c_device_ts,
&s3c_device_led, //led平台设备文件
};
编译内核下载,看到"heartbeat"灯在闪烁,"default-on"等一直在亮着。
查看系统类设备
root@FZ:/sys/class/leds# ls
led1 led2 led3 led4
root@FZ:/sys/class/leds# cd led2
root@FZ:/sys/devices/platform/leds-gpio/leds/led2# ls
brightness max_brightness subsystem uevent
device power trigger
让led2亮
root@FZ:/sys/devices/platform/leds-gpio/leds/led2# echo 1 > brightness
让led2灭
root@FZ:/sys/devices/platform/leds-gpio/leds/led2# echo 0 > brightness
写一个简单的应用测试程序
/*
* leds.c - The first kernel app programming
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int main(int argc, char *argv[])
{
FILE *fp;
int led_no;
if(argc != 3)
{
printf("Usage: %s <led_no> <on/off>\n",argv[0]);
exit(0);
}
led_no = strtoul(argv[1],0,0);
switch (led_no)
{
case 2:
fp = fopen("/sys/class/leds/led2/brightness","w");
break;
case 3:
fp = fopen("/sys/class/leds/led3/brightness","w");
break;
case 4:
fp = fopen("/sys/class/leds/led4/brightness","w");
break;
default:
printf("err: no target led!\n");
break;
}
if(fp == NULL)
{
printf("err: cant open the file\n");
exit(0);
}
if(!strcmp(argv[2],"on")){
fputc('1',fp);
printf("led%d on!\n",led_no);
}else if(!strcmp(argv[2],"off")){
fputc('0',fp);
printf("led%d off!\n",led_no);
}
fclose(fp);
return 0;
}
编译
arm-linux-gcc -o leds leds.c
运行
./leds 3 on
看到led3点亮了
LED_OFF = 0,
LED_HALF = 127,
LED_FULL = 255,
};
struct led_classdev {
const char *name; //名字
int brightness; //当前亮度
int flags; //标志,目前只支持 LED_SUSPENDED
#define LED_SUSPENDED (1 << 0)
/*设置led的亮度,不可以睡眠,有必要的话可以使用工作队列*/
void (*brightness_set)(struct led_classdev *led_cdev,
enum led_brightness brightness);
/* 获取亮度 */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
/* 激活硬件加速的闪烁 */
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);
struct device *dev;
struct list_head node; /* 所有已经注册的led_classdev使用这个节点串联起来 */
const char *default_trigger; /* 默认触发器 */
#ifdef CONFIG_LEDS_TRIGGERS //如果配置内核时使能了触发器功能,才会编译下面一段
/* 这个读写子轩锁保护下面的触发器数据 */
struct rw_semaphore trigger_lock;
struct led_trigger *trigger; //触发器指针
struct list_head trig_list; //触发器使用的链表节点,用来连接同一触发器上的所有led_classdev
void *trigger_data; //触发器使用的私有数据
#endif
};
struct led_trigger {
const char *name; //触发器名字
void (*activate)(struct led_classdev *led_cdev); //激活ledled。led_classdev和触发器建立连接时会调用这个方法。
void (*deactivate)(struct led_classdev *led_cdev); //取消激活。led_classdev和触发器取消连接时会调用这个方法。
/* 本触发器控制之下的led链表 */
rwlock_t leddev_list_lock; //保护链表的锁
struct list_head led_cdevs; //链表头
/* 连接下一个已注册触发器的链表节点 ,所有已注册的触发器都会被加入一个全局链表*/
struct list_head next_trig;
};
const char *name;
char *default_trigger;
int flags;
};
struct led_platform_data {
int num_leds;
struct led_info *leds;
};
const char *name;
char *default_trigger;
unsigned gpio;
u8 active_low;
};
struct gpio_led_platform_data {
int num_leds;
struct gpio_led *leds;
int (*gpio_blink_set)(unsigned gpio,
unsigned long *delay_on,
unsigned long *delay_off);
};
led_classdev接口分析/driver/rtc/led-class.c
{
int rc;
"%s", led_cdev->name);
if (IS_ERR(led_cdev->dev))
return PTR_ERR(led_cdev->dev);
/* register the attributes */
rc = device_create_file(led_cdev->dev, &dev_attr_brightness);//在sys/class/rtc/下创建一个led的属性文件。
if (rc)
goto err_out;
/* add to the list of leds */
down_write(&leds_list_lock);
list_add_tail(&led_cdev->node, &leds_list);//将新的led加入链表,全局链表是leds_list
up_write(&leds_list_lock);
led_update_brightness(led_cdev);//获取led当前的亮度更新led_cdev的brightness成员
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);//初始化led_cdev的触发器自旋锁
rc = device_create_file(led_cdev->dev, &dev_attr_trigger);//在sys/class/led中为触发器创建属性文件
if (rc)
goto err_out_led_list;
led_trigger_set_default(led_cdev); //为led_cdev设置默认的触发器
#endif
printk(KERN_INFO "Registered led device: %s/n",
led_cdev->name);
return 0;
#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
device_remove_file(led_cdev->dev, &dev_attr_brightness);
list_del(&led_cdev->node);
#endif
err_out:
device_unregister(led_cdev->dev);
return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
内核中led触发器实例
gpio-led框架
const char *name; //名字
char *default_trigger; //默认触发器的名字
unsigned gpio; //使用的gpio编号
u8 active_low; //如果为真则逻辑1代表低电平
};
struct gpio_led_platform_data {
int num_leds; //gpio led的数量
struct gpio_led *leds; //指向要注册的gpio_led数组
int (*gpio_blink_set)(unsigned gpio, //硬件闪烁加速设置,可以为NULL
unsigned long *delay_on,
unsigned long *delay_off);
};
default-on触发器
{
led_set_brightness(led_cdev, LED_FULL);
}
心跳灯触发器
IDE硬盘指示灯触发器
void led_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off){
del_timer_sync(&led_cdev->blink_timer);
if (led_cdev->blink_set &&
!led_cdev->blink_set(led_cdev, delay_on, delay_off))
return;
/* blink with 1 Hz as default if nothing specified */
if (!*delay_on && !*delay_off)
*delay_on = *delay_off = 500;
led_set_software_blink(led_cdev, *delay_on, *delay_off);
}