Android led_class driver

Linux的led class驅動

echo 255 > /sys/class/leds/led1/brightness

cat /sys/class/leds/led1/brightness

cat /sys/class/leds/led1/max_brightness

閃爍

echo timer > /sys/class/leds/led1/trigger

echo 100 > /sys/class/leds/led1/delay_on

echo 200 > /sys/class/leds/led1/delay_off

關閉

echo 0 > /sys/class/leds/led1/delay_on

echo 0 > /sys/class/leds/led1/brightness

怎麼寫驅動:

a1. 分配led_classdev

a2. 設置 :

led_cdev->max_brightness

led_cdev->brightness_set

led_cdev->flags

led_cdev->brightness

led_cdev->name

led_class 子系統簡要分析

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;

return 0;

}

用戶空間 sys 中的接口,可以設置 brightness 、獲取最大背光亮度 max_brightness 、使能閃爍 trigger

static struct device_attribute led_class_attrs[] = {

__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),

__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),

#ifdef CONFIG_LEDS_TRIGGERS

__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),

#endif

__ATTR_NULL,

};

設置背光亮度比較簡單,會調用到具體的 led_class 設備的 set_brightness 函數

簡要分析一下閃爍:

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);

#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);

up_write(&leds_list_lock);

if (!led_cdev->max_brightness)

led_cdev->max_brightness = LED_FULL;

led_update_brightness(led_cdev);

init_timer(&led_cdev->blink_timer);

// 註冊 led_class 設備時會初始化一個定時器

led_cdev->blink_timer.function = led_timer_function;

led_cdev->blink_timer.data = (unsigned long)led_cdev;

#ifdef CONFIG_LEDS_TRIGGERS

led_trigger_set_default(led_cdev);

#endif

return 0;

}

//定時器的功能自然就是為瞭實現led的閃爍瞭

static void led_timer_function(unsigned long data)

{

struct led_classdev *led_cdev = (void *)data;

unsigned long brightness;

unsigned long delay;

brightness = led_get_brightness(led_cdev);

if (!brightness) {

/* Time to switch the LED on. */

brightness = led_cdev->blink_brightness;

delay = led_cdev->blink_delay_on;

} else {

/* Store the current brightness value to be able

* to restore it when the delay_off period is over.

*/

led_cdev->blink_brightness = brightness;

brightness = LED_OFF;

delay = led_cdev->blink_delay_off;

}

led_set_brightness(led_cdev, brightness);

mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));

}

echo timer > /sys/class/leds/led1/trigger // timer對應 ledtrig-timer.c

led_trigger_store // 1. 從trigger_list找出名為"timer"的trigger

list_for_each_entry(trig, &trigger_list, next_trig) {

if (!strcmp(trigger_name, trig->name)) {

// 2. 調用

led_trigger_set(led_cdev, trig);

// 3. 把trigger放入led_classdev的trig_list鏈表裡

list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);

led_cdev->trigger = trigger;

trigger->activate(led_cdev);

// 4. 對於"timer"

timer_trig_activate

// 6. 創建2個文件: delay_on, delay_off

device_create_file

device_create_file

}

}

echo 100 > /sys/class/leds/led1/delay_on

led_delay_on_store

state = simple_strtoul(buf, &after, 10);

led_blink_set // // 讓LED閃爍

led_set_software_blink

mod_timer(&led_cdev->blink_timer, jiffies + 1);

led_cdev->blink_delay_on = state;

echo 200 > /sys/class/leds/led1/delay_off

led_delay_off_store

state = simple_strtoul(buf, &after, 10);

led_blink_set // 讓LED閃爍

led_set_software_blink

mod_timer(&led_cdev->blink_timer, jiffies + 1);

led_cdev->blink_delay_off = state;

通過 led_class 來實現標準的燈光系統驅動,用戶空間可以獲取、更新led亮度值,使能led閃爍。

驅動中隻需要設置“set_brightness”函數,至於閃爍led子系統使用定時器已經實現。

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

struct led_desc {

int gpio;

char *name;

};

static struct led_desc led_gpios[] = {

{EXYNOS4212_GPM4(0), "led1"},

{EXYNOS4212_GPM4(1), "led2"},

{EXYNOS4212_GPM4(2), "led3"},

{EXYNOS4212_GPM4(3), "led4"},

};

struct led_classdev_4412 {

struct led_classdev cdev;

int gpio;

};

static struct led_classdev_4412 *led_devs;

static void brightness_set_4412(struct led_classdev *led_cdev,

enum led_brightness brightness)

{

struct led_classdev_4412 *dev = (struct led_classdev_4412 *)led_cdev;

led_cdev->brightness = brightness;

if (brightness != LED_OFF)

gpio_set_value(dev->gpio, 0);

else

gpio_set_value(dev->gpio, 1);

}

static int leds_init(void)

{

int i;

int ret;

/* 1. alloc led_classdev */

led_devs = kzalloc(sizeof(struct led_classdev_4412) * sizeof(led_gpios)/sizeof(led_gpios[0]), GFP_KERNEL);

if (led_devs == NULL) {

printk("No memory for device\n");

return -ENOMEM;

}

for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)

{

s3c_gpio_cfgpin(led_gpios[i].gpio, S3C_GPIO_OUTPUT);

gpio_set_value(led_gpios[i].gpio, 1);

/* 2. set */

led_devs[i].cdev.max_brightness = LED_FULL;

led_devs[i].cdev.brightness_set = brightness_set_4412;

led_devs[i].cdev.flags = LED_CORE_SUSPENDRESUME;

led_devs[i].cdev.brightness = LED_OFF;

led_devs[i].cdev.name = led_gpios[i].name;

//led_devs[i].cdev.default_trigger = "timer";

led_devs[i].gpio = led_gpios[i].gpio;

/* 3. led_classdev_register */

ret = led_classdev_register(NULL, &led_devs[i].cdev);

if (ret) {

i–;

while (i >= 0) {

led_classdev_unregister(&led_devs[i].cdev);

i–;

}

kfree(led_devs);

return -EIO;

}

}

return 0;

}

static void leds_exit(void)

{

int i;

for (i = 0; i < sizeof(led_gpios)/sizeof(led_gpios[0]); i++)

{

led_classdev_unregister(&led_devs[i].cdev);

}

kfree(led_devs);

}

module_init(leds_init);

module_exit(leds_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("www.100ask.net");

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *