android button 라이트 프로 세 스 분석 (1) - driver

25463 단어
led - class
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 를 일 으 킬 수 있 기 때문에 작업 대기 열 을 만들어 밝기 통 제 를 실현 해 야 합 니 다.

좋은 웹페이지 즐겨찾기