android button 라이트 프로 세 스 분석 (1) - driver
led - class 는 button light driver 의 핵심 층 입 니 다. 코드 는 drivers / leds / 디 렉 터 리 에 위치 하고 데이터 구조 와 인 터 페 이 스 를 제공 합 니 다. 그 중에서 leds 장치 류 를 만 들 었 습 니 다. 시스템 의 모든 led 를 관리 하 는 데 사 용 됩 니 다. 코드 는 다음 과 같 습 니 다.
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;
return 0;
}
static void __exit leds_exit(void)
{
class_destroy(leds_class);
}
subsys_initcall(leds_init);
module_exit(leds_exit);
생 성 에 성공 하면 / sys / class 디 렉 터 리 아래 에서 leds 디 렉 터 리 를 볼 수 있 고 통 일 된 휴면 과 깨 우기 함 수 를 등록 할 수 있 습 니 다./**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
*/
void led_classdev_suspend(struct led_classdev *led_cdev)
{
led_cdev->flags |= LED_SUSPENDED;
led_cdev->brightness_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);
/**
* led_classdev_resume - resume an led_classdev.
* @led_cdev: the led_classdev to resume.
*/
void led_classdev_resume(struct led_classdev *led_cdev)
{
led_cdev->brightness_set(led_cdev, led_cdev->brightness);
led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);
static int led_suspend(struct device *dev, pm_message_t state)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_suspend(led_cdev);
return 0;
}
static int led_resume(struct device *dev)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
led_classdev_resume(led_cdev);
return 0;
}
suspend 는 주로 로 고 를 설정 하고 led 를 닫 습 니 다. resume 은 로 고 를 제거 하고 led 를 회복 하기 전에 밝 기 를 유지 합 니 다.동시에 led - class 는 구조 체 led 를 제공 합 니 다.classdev 는 통 일 된 led 장치 모델 로 서:struct led_classdev {
const char *name;
int brightness;
int max_brightness;
int flags;
/* Lower 16 bits reflect status */
#define LED_SUSPENDED (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,
enum led_brightness brightness);
/* Get LED brightness level */
enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
/* Activate hardware accelerated blink, delays are in
* miliseconds and if none is provided then a sensible default
* should be chosen. The call can adjust the timings if it can't
* match the values specified exactly. */
int (*blink_set)(struct led_classdev *led_cdev,
unsigned long *delay_on,
unsigned long *delay_off);
struct device *dev;
struct list_head node; /* LED Device list */
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
};
그 중에서 밝 기 를 설정 하고 밝 기 를 얻 으 며 반 짝 이 는 led 몇 개의 인터페이스, 그리고 trigger 필드 와 관련 된 링크 노드 를 제공 합 니 다.led - class 에 서 는 brightness, max 도 정의 되 어 있 습 니 다.brightness 두 속성 은 HAL 층 에 사용 가능:
static void led_update_brightness(struct led_classdev *led_cdev)
{
if (led_cdev->brightness_get)
led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}
static ssize_t led_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
/* no lock needed for this */
led_update_brightness(led_cdev);
return sprintf(buf, "%u
", led_cdev->brightness);
}
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);
ssize_t ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && 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;
}
static ssize_t led_max_brightness_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%u
", led_cdev->max_brightness);
}
static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
그 중에서 brightness 는 밝기 값 을 설정 하고 읽 는 데 사 용 됩 니 다. maxbrightness 는 하드웨어 가 지원 하 는 최대 밝기 값 을 읽 는 데 사 용 됩 니 다.led - class 는 등록 led 도 제공 합 니 다.classdev 의 통합 인터페이스:/**
* led_classdev_register - register a new object of led_classdev class.
* @parent: The device to register.
* @led_cdev: the led_classdev structure for this device.
*/
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
int rc;
led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
"%s", led_cdev->name); // leds_class
if (IS_ERR(led_cdev->dev))
return PTR_ERR(led_cdev->dev);
/* brightness */
rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
if (rc)
goto err_out;
#ifdef CONFIG_LEDS_TRIGGERS
init_rwsem(&led_cdev->trigger_lock);
#endif
/* leds_list */
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;
rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
if (rc)
goto err_out_attr_max;
led_update_brightness(led_cdev); //
#ifdef CONFIG_LEDS_TRIGGERS
rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
if (rc)
goto err_out_led_list;
led_trigger_set_default(led_cdev);
#endif
printk(KERN_INFO "Registered led device: %s
",
led_cdev->name);
return 0;
#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
#endif
err_out_attr_max:
device_remove_file(led_cdev->dev, &dev_attr_brightness);
list_del(&led_cdev->node);
err_out:
device_unregister(led_cdev->dev);
return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);
led - driver
플랫폼 의 구동 에서 저 희 는 platform led 장 치 를 모든 led 장치 의 부모 장치 로 등록 합 니 다. 등록 코드 는 다음 과 같 습 니 다.
static struct platform_driver atxxled_driver = {
.probe = atxxled_probe,
.remove = atxxled_remove,
.driver = {
.name = "led",
.owner = THIS_MODULE,
},
};
static int __init atxxled_init(void)
{
return platform_driver_register(&atxxled_driver);
}
static void __exit atxxled_exit(void)
{
platform_driver_unregister(&atxxled_driver);
}
module_init(atxxled_init);
module_exit(atxxled_exit);
그리고 각각 led 를 등록 합 니 다.static void atxxled_set(struct led_classdev *led_cdev, enum ATXX_LED led,
enum led_brightness value)
{
int is_on = (value == LED_OFF) ? 0 : 1;
enum pmu_led_module led_module;
switch (led) {
case RED_LED:
led_module = PMU_LED_MODULE_2;
break;
case GREEN_LED:
led_module = PMU_LED_MODULE_1;
break;
default:
return;
break;
}
pmu_led_on_off_set(led_module, is_on, 0); // led
}
static void atxxled_green_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
atxxled_set(led_cdev, GREEN_LED, value);
}
static void atxxled_red_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
atxxled_set(led_cdev, RED_LED, value);
}
static void atxxled_buttons_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
enum power_supply_mode is_on = (value == LED_OFF) ? PPS_OFF : PPS_ON;
pmu_led_on_off_set(PMU_LED_KEY_1, is_on, 40);
}
static int atxxled_blink_set(struct led_classdev *led_cdev, enum ATXX_LED led,
unsigned long *delay_on, unsigned long *delay_off)
{
enum pmu_led_module led_module;
switch (led) {
case RED_LED:
led_module = PMU_LED_MODULE_2;
break;
case GREEN_LED:
led_module = PMU_LED_MODULE_1;
break;
default:
return 0;
break;
}
pmu_led_blink_set(led_module, delay_on, delay_off); // led
return 0;
}
static int atxxled_green_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
return atxxled_blink_set(led_cdev, GREEN_LED, delay_on, delay_off);
}
static int atxxled_red_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
return atxxled_blink_set(led_cdev, RED_LED, delay_on, delay_off);
}
static int atxxled_buttons_blink_set(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_off)
{
return -1;
}
static struct led_classdev atxx_red_led = {
.name = "red",
.brightness_set = atxxled_red_set,
.blink_set = atxxled_red_blink_set,
.flags = 0,
};
static struct led_classdev atxx_green_led = {
.name = "green",
.brightness_set = atxxled_green_set,
.blink_set = atxxled_green_blink_set,
.flags = 0,
};
static struct led_classdev atxx_buttons_led = {
.name = "button-backlight",
.brightness_set = atxxled_buttons_set, // led
.blink_set = atxxled_buttons_blink_set, // led
.flags = 0,
};
static int atxxled_probe(struct platform_device *pdev)
{
int ret;
ret = led_classdev_register(&pdev->dev, &atxx_buttons_led);
if (ret < 0)
return ret;
ret = led_classdev_register(&pdev->dev, &atxx_red_led);
if (ret < 0)
return ret;
ret = led_classdev_register(&pdev->dev, &atxx_green_led);
if (ret < 0)
led_classdev_unregister(&atxx_red_led);
return ret;
}
led - trigger
trigger 는 트리거 로 직역 합 니 다. 즉, 사용자 가 조작 할 때 led 불 을 켜 거나 꺼 집 니 다. 입력 명령 을 통 해:
cat /sys/class/leds/button-backlight/trigger
시스템 에 등 록 된 trigger 를 얻 을 수 있 습 니 다.
none nand-disk ac-online USB-online Battery-charging-or-full Battery-charging Battery-full mmc0 mmc1 [timer] rfkill0
사용자 공간 은 trigger 파일 에 직접 값 을 써 서 led 의 트리거 방식 을 바 꿀 수 있 습 니 다.trigger 의 데이터 구 조 는 다음 과 같 습 니 다.
struct led_trigger {
/* Trigger Properties */
const char *name;
void (*activate)(struct led_classdev *led_cdev);
void (*deactivate)(struct led_classdev *led_cdev);
/* LEDs under control by this trigger (for simple triggers) */
rwlock_t leddev_list_lock;
struct list_head led_cdevs;
/* Link to next registered trigger */
struct list_head next_trig;
};
각각 trigger 이름, 활성화 및 분리 인터페이스, 읽 기 쓰기 자물쇠, led 장치 체인 헤더, triggerlist 링크 노드.다음은 led - trigger 핵심 부분 소스 코드 에 대한 분석 입 니 다.
static DECLARE_RWSEM(triggers_list_lock); // trigger
static LIST_HEAD(trigger_list); // trigger
//
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
char trigger_name[TRIG_NAME_MAX];
struct led_trigger *trig;
size_t len;
// trigger_name
trigger_name[sizeof(trigger_name) - 1] = '\0';
strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
len = strlen(trigger_name);
if (len && trigger_name[len - 1] == '
')
trigger_name[len - 1] = '\0';
// none, led_cdev->trigger
if (!strcmp(trigger_name, "none")) {
led_trigger_remove(led_cdev);
return count;
}
down_read(&triggers_list_lock);
// trigger_list, trigger trigger_list
// led_trigger_set() trigger
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trigger_name, trig->name)) {
down_write(&led_cdev->trigger_lock);
led_trigger_set(led_cdev, trig);
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
return count;
}
}
up_read(&triggers_list_lock);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(led_trigger_store);
//
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct led_trigger *trig;
int len = 0;
down_read(&triggers_list_lock);
down_read(&led_cdev->trigger_lock);
// trigger, [none]
if (!led_cdev->trigger)
len += sprintf(buf+len, "[none] ");
else
len += sprintf(buf+len, "none ");
// trigger_list, trigger "[xxx]"
// "xxx" trigger
list_for_each_entry(trig, &trigger_list, next_trig) {
if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
trig->name))
len += sprintf(buf+len, "[%s] ", trig->name);
else
len += sprintf(buf+len, "%s ", trig->name);
}
up_read(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
len += sprintf(len+buf, "
");
return len;
}
EXPORT_SYMBOL_GPL(led_trigger_show);
// trigger , led_cdev->trigger_lock
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
{
unsigned long flags;
// trigger, , trigger
if (led_cdev->trigger) {
write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
list_del(&led_cdev->trig_list); // trigger
write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
flags);
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev); // trigger
led_cdev->trigger = NULL; // trigger
led_set_brightness(led_cdev, LED_OFF); //
}
// trigger
if (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
// trigger
list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
// trigger trigger
led_cdev->trigger = trigger;
if (trigger->activate)
trigger->activate(led_cdev);
}
}
EXPORT_SYMBOL_GPL(led_trigger_set);
// trigger
void led_trigger_remove(struct led_classdev *led_cdev)
{
down_write(&led_cdev->trigger_lock);
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_remove);
// trigger
void led_trigger_set_default(struct led_classdev *led_cdev)
{
struct led_trigger *trig;
if (!led_cdev->default_trigger)
return;
down_read(&triggers_list_lock);
down_write(&led_cdev->trigger_lock);
// trigger trigger_list , trigger
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(led_cdev->default_trigger, trig->name))
led_trigger_set(led_cdev, trig);
}
up_write(&led_cdev->trigger_lock);
up_read(&triggers_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);
// trigger trigger_list
int led_trigger_register(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
struct led_trigger *trig;
rwlock_init(&trigger->leddev_list_lock); //
INIT_LIST_HEAD(&trigger->led_cdevs); //
down_write(&triggers_list_lock);
// trigger
list_for_each_entry(trig, &trigger_list, next_trig) {
if (!strcmp(trig->name, trigger->name)) {
up_write(&triggers_list_lock);
return -EEXIST;
}
}
// trigger trigger_list
list_add_tail(&trigger->next_trig, &trigger_list);
up_write(&triggers_list_lock);
// led, led trigger trigger
// led_trigger_set() led trigger
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (!led_cdev->trigger && led_cdev->default_trigger &&
!strcmp(led_cdev->default_trigger, trigger->name))
led_trigger_set(led_cdev, trigger);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
return 0;
}
EXPORT_SYMBOL_GPL(led_trigger_register);
// trigger
void led_trigger_unregister(struct led_trigger *trigger)
{
struct led_classdev *led_cdev;
// trigger trigger_list
down_write(&triggers_list_lock);
list_del(&trigger->next_trig);
up_write(&triggers_list_lock);
// led, led trigger trigger
// led_trigger_set() led trigger
down_read(&leds_list_lock);
list_for_each_entry(led_cdev, &leds_list, node) {
down_write(&led_cdev->trigger_lock);
if (led_cdev->trigger == trigger)
led_trigger_set(led_cdev, NULL);
up_write(&led_cdev->trigger_lock);
}
up_read(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister);
// trigger led
void led_trigger_event(struct led_trigger *trigger,
enum led_brightness brightness)
{
struct list_head *entry;
if (!trigger)
return;
read_lock(&trigger->leddev_list_lock);
// trigger ,
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
led_set_brightness(led_cdev, brightness);
}
read_unlock(&trigger->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_event);
// trigger
void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
struct led_trigger *trigger;
int err;
trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
if (trigger) {
trigger->name = name;
err = led_trigger_register(trigger);
if (err < 0)
printk(KERN_WARNING "LED trigger %s failed to register"
" (%d)
", name, err);
} else
printk(KERN_WARNING "LED trigger %s failed to register"
" (no memory)
", name);
*tp = trigger;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);
// trigger
void led_trigger_unregister_simple(struct led_trigger *trigger)
{
if (trigger)
led_trigger_unregister(trigger);
kfree(trigger);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
핵심 층 은 주로 trigger 를 유지 합 니 다.list 링크 는 시스템 에 등 록 된 모든 trigger 를 저장 합 니 다.모든 trigger 는 자신의 led 장치 링크 를 가지 고 있 으 며, 이 trigger 아래 의 모든 led 밝 기 를 통일 적 으로 제어 하 는 데 사용 된다.또한 모든 led 장 치 는 자신의 trigger 도 메 인 을 가지 고 있 으 며, 해당 trigger 를 가리 키 는 데 사용 되 며, trigger 가 트리거 되면 해당 trigger 의 활성화 함 수 를 호출 합 니 다.
4. ledtrig - timer
ledtrig - timer 트리거 로 led 반 짝 임 을 제어 합 니 다.led - trigtimer 는 led - trigger 핵심 층 호출 을 통 해 / sys / class / leds / button - backlight 에서 delay 를 만 듭 니 다.on、delay_off 속성 파일 로 반 짝 임 시간 을 제어 합 니 다.코드 분석 은 다음 과 같다.
struct timer_trig_data {
int brightness_on; /* LED brightness during "on" period.
* (LED_OFF < brightness_on <= LED_FULL)
*/
unsigned long delay_on; /* milliseconds on */
unsigned long delay_off; /* milliseconds off */
struct timer_list timer;
};
static void led_timer_function(unsigned long data)
{
struct led_classdev *led_cdev = (struct led_classdev *) data;
struct timer_trig_data *timer_data = led_cdev->trigger_data;
unsigned long brightness;
unsigned long delay;
// delay_on delay_off 0 led
if (!timer_data->delay_on || !timer_data->delay_off) {
led_set_brightness(led_cdev, LED_OFF);
return;
}
//
brightness = led_get_brightness(led_cdev);
if (!brightness) {
// led led
brightness = timer_data->brightness_on;
delay = timer_data->delay_on;
} else {
// led led
timer_data->brightness_on = brightness;
brightness = LED_OFF;
delay = timer_data->delay_off;
}
// , soft irq ,
led_set_brightness(led_cdev, brightness);
// delay led ,
mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
}
// delay_on
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
return sprintf(buf, "%lu
", timer_data->delay_on);
}
// delay_on
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && isspace(*after))
count++;
if (count == size) {
if (timer_data->delay_on != state) {
// delay_on
timer_data->delay_on = state;
// timer
del_timer_sync(&timer_data->timer);
// blink_set() led
if (!led_cdev->blink_set ||
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off)) {
// timer led
mod_timer(&timer_data->timer, jiffies + 1);
}
}
ret = count;
}
return ret;
}
// delay_off
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
return sprintf(buf, "%lu
", timer_data->delay_off);
}
// delay_off
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct timer_trig_data *timer_data = led_cdev->trigger_data;
int ret = -EINVAL;
char *after;
unsigned long state = simple_strtoul(buf, &after, 10);
size_t count = after - buf;
if (*after && isspace(*after))
count++;
if (count == size) {
if (timer_data->delay_off != state) {
/* the new value differs from the previous */
timer_data->delay_off = state;
/* deactivate previous settings */
del_timer_sync(&timer_data->timer);
/* try to activate hardware acceleration, if any */
if (!led_cdev->blink_set ||
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off)) {
/* no hardware acceleration, blink via timer */
mod_timer(&timer_data->timer, jiffies + 1);
}
}
ret = count;
}
return ret;
}
// delay_on,delay_off
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
// timer_led_trigger
static void timer_trig_activate(struct led_classdev *led_cdev)
{
struct timer_trig_data *timer_data;
int rc;
// timer_trig_data
timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
if (!timer_data)
return;
// led
timer_data->brightness_on = led_get_brightness(led_cdev);
// 0
if (timer_data->brightness_on == LED_OFF)
timer_data->brightness_on = led_cdev->max_brightness;
// led trigger_data
led_cdev->trigger_data = timer_data;
// timer
init_timer(&timer_data->timer);
timer_data->timer.function = led_timer_function;
timer_data->timer.data = (unsigned long) led_cdev;
// delay_on,delay_off
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
/* If there is hardware support for blinking, start one
* user friendly blink rate chosen by the driver.
*/
// led ,
if (led_cdev->blink_set)
led_cdev->blink_set(led_cdev,
&timer_data->delay_on, &timer_data->delay_off);
return;
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out:
led_cdev->trigger_data = NULL;
kfree(timer_data);
}
// timer_led_trigger
static void timer_trig_deactivate(struct led_classdev *led_cdev)
{
struct timer_trig_data *timer_data = led_cdev->trigger_data;
unsigned long on = 0, off = 0;
// 、timer、
if (timer_data) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
del_timer_sync(&timer_data->timer);
kfree(timer_data);
}
//
if (led_cdev->blink_set)
led_cdev->blink_set(led_cdev, &on, &off);
}
// timer_led_trigger
static struct led_trigger timer_led_trigger = {
.name = "timer",
.activate = timer_trig_activate,
.deactivate = timer_trig_deactivate,
};
static int __init timer_trig_init(void)
{
return led_trigger_register(&timer_led_trigger);
}
static void __exit timer_trig_exit(void)
{
led_trigger_unregister(&timer_led_trigger);
}
이 timer 트리거 의 실현 에는 bug 가 있 습 니 다. soft irq 함수 에서 밝 기 를 설정 하 는 것 입 니 다. 만약 에 플랫폼 이 led 밝 기 를 제어 하 는 것 이 복잡 하 다 면 예 를 들 어 i2c 장 치 를 통 해 커 널 crash 를 일 으 킬 수 있 기 때문에 작업 대기 열 을 만들어 밝기 통 제 를 실현 해 야 합 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.