linux驱动-led子系统

        led子系统实际是linux编写好的一个led驱动框架.对于驱动开发者来说只需要按照规定填充结构体然后调用api注册驱动,就可以完成一个led驱动的编写,并且这样写的led驱动为上层提供了统一的接口或者称为标准linux接口.方便了应用开发人员也减少了驱动开发人员的工作量.

        

`        有些led是gpio控制的.有些led是pwm控制,可以控制led的亮度.所以在此基础上又引出了GPIO_LED子系统和GPIO_pwm子系统.当然也可以直接使用led子系统,忽略gpio_pwm子系统和gpio_子系统.

led子系统

        led子系统驱动的源码位于dirvers/leds目录下, 可以看到有很多的文件,但是很多是 在led子系统上编写的led驱动,不是led子系统的源码.

        ls led-  列出的以led-开头的文件菜式led子系统实现代码. 

 部分文件的功能如下:
  1. led-core.c: led抽象功能实现代码.led灯的亮灭, 闪烁,呼吸.
  2. led-class.c: 调用class_create函数在/sys/class/leds目录创建用户空间控制接口.
  3. led-triggers.c: led 抽象触发功能实现代码
  4. led-class-flash.c: 闪烁功能实现

先不看源码,先看下如何编写一个简单的led驱动,仅仅实现亮灭控制.

基于led子系统驱动实现

        选一个最简单的led驱动,上源码(kernel/drivers/leds/leds-rb532.c),如下:

/*
 * LEDs driver for the "User LED" on Routerboard532
 *
 * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
 *
 * Based on leds-cobalt-qube.c by Florian Fainelly and
 * rb-diag.c (my own standalone driver for both LED and
 * button of Routerboard532).
 */

#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_device.h>

#include <asm/mach-rc32434/gpio.h>
#include <asm/mach-rc32434/rb.h>

static void rb532_led_set(struct led_classdev *cdev,
			  enum led_brightness brightness)
{
	if (brightness)
		set_latch_u5(LO_ULED, 0);

	else
		set_latch_u5(0, LO_ULED);
}

static enum led_brightness rb532_led_get(struct led_classdev *cdev)
{
	return (get_latch_u5() & LO_ULED) ? LED_FULL : LED_OFF;
}

static struct led_classdev rb532_uled = {
	.name = "uled",
	.brightness_set = rb532_led_set,
	.brightness_get = rb532_led_get,
	.default_trigger = "nand-disk",
};

static int rb532_led_probe(struct platform_device *pdev)
{
	return led_classdev_register(&pdev->dev, &rb532_uled);
}

static int rb532_led_remove(struct platform_device *pdev)
{
	led_classdev_unregister(&rb532_uled);
	return 0;
}

static struct platform_driver rb532_led_driver = {
	.probe = rb532_led_probe,
	.remove = rb532_led_remove,
	.driver = {
		.name = "rb532-led",
	},
};

module_platform_driver(rb532_led_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("User LED support for Routerboard532");
MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
MODULE_ALIAS("platform:rb532-led");

代码很简单, 定义并初始化了一个static struct led_classdev 结构体,然后调用led_classdev_register()注册这个结构体并为这个结构体指定了父设备.

led_classdev

看下struct led_classdev结构体:

struct led_classdev {
	const char		*name;
	enum led_brightness	 brightness;
	enum led_brightness	 max_brightness;
	int			 flags;

	/* Lower 16 bits reflect status */
#define LED_SUSPENDED		BIT(0)
#define LED_UNREGISTERING	BIT(1)
	/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME	BIT(16)
#define LED_SYSFS_DISABLE	BIT(17)
#define LED_DEV_CAP_FLASH	BIT(18)
#define LED_HW_PLUGGABLE	BIT(19)
#define LED_PANIC_INDICATOR	BIT(20)
#define LED_BRIGHT_HW_CHANGED	BIT(21)
#define LED_RETAIN_AT_SHUTDOWN	BIT(22)

	/* set_brightness_work / blink_timer flags, atomic, private. */
	unsigned long		work_flags;

#define LED_BLINK_SW			0
#define LED_BLINK_ONESHOT		1
#define LED_BLINK_ONESHOT_STOP		2
#define LED_BLINK_INVERT		3
#define LED_BLINK_BRIGHTNESS_CHANGE 	4
#define LED_BLINK_DISABLE		5

	/* Set LED brightness level
	 * Must not sleep. Use brightness_set_blocking for drivers
	 * that can sleep while setting brightness.
	 */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/*
	 * Set LED brightness level immediately - it can block the caller for
	 * the time required for accessing a LED device register.
	 */
	int (*brightness_set_blocking)(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 milliseconds
	 * and if both are zero then a sensible default should be chosen.
	 * The call should adjust the timings in that case and if it can't
	 * match the values specified exactly.
	 * Deactivate blinking again when the brightness is set to LED_OFF
	 * via the brightness_set() callback.
	 */
	int		(*blink_set)(struct led_classdev *led_cdev,
				     unsigned long *delay_on,
				     unsigned long *delay_off);

	struct device		*dev;
	const struct attribute_group	**groups;

	struct list_head	 node;			/* LED Device list */
	const char		*default_trigger;	/* Trigger to use */

	unsigned long		 blink_delay_on, blink_delay_off;
	struct timer_list	 blink_timer;
	int			 blink_brightness;
	int			 new_blink_brightness;
	void			(*flash_resume)(struct led_classdev *led_cdev);

	struct work_struct	set_brightness_work;
	int			delayed_set_value;

#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;
	/* true if activated - deactivate routine uses it to do cleanup */
	bool			activated;
#endif

#ifdef CONFIG_LEDS_BRIGHTNESS_HW_CHANGED
	int			 brightness_hw_changed;
	struct kernfs_node	*brightness_hw_changed_kn;
#endif

	/* Ensures consistent access to the LED Flash Class device */
	struct mutex		led_access;
};

代码注册的很清楚,大致看下:

  • char* name : 设备的名字.
  • enum led_brightness brightness: 应该是默认的亮度.
  • enum led_brightness max_birghtness: led的最大亮度.
  • int flags: 标记led当前的状态.状态可以用它下面的那些帮助宏设置.
  • brightness_set: 函数指针,设置亮度的实现函数, 不能有休眠,如果必须休眠则应该使用类似工作队列.
  • brightness_set_sync: 同步设置led的亮度.
  • brightness_get: 获取led当前亮度.
  • blink_set: 设置led 闪烁. 设置闪烁后如果再次调用brightness_set(),会停止闪烁.
  • struct device *dev: 设备结构体示例.
  • const struct attribute_group **grpups: 这个设备的属性列表. 这个是在标准接口的基础上额外添加的属性.
  • struct list_head node: 链表节点, 用于把所有的led device 放在一个链表里.
  • default_tirgger: 默认的触发方式.
  • blink_delay_on, blink_delay_off: 默认的亮灭时间.
  • struct timer_list blink_timer: 这是实现闪烁要用到的定时器.
  • blink_brightenss: 应该是闪烁情况下led 的亮度.
  • set_brightness_work: 设置亮度要用到的工作.
  • delayed_set_value: 难道是延时设置亮度.
  • struct mutes led_access: led互斥访问.

        可以看到这个结构体是一个led设备的抽象,一个结构体实例代表了一个led设备. 并且led子系统支持的功能还很多,比如基础功能,设置led亮度,以及led的最大亮度,可以设置led灯的触发比如实现按键按下led就亮以下的功能,设置闪烁功能.

led_classdev_register,

用于注册一个led 设备,注册成功后上层可以使用标准接口控制led灯. 

/**
 * 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)

led_classdev_register(),函数是在其他驱动中调用的, 它可能是一个混杂设备,平台设备等等. 第一个参数parent就是用于指定谁创建了这个led设备.第二个参数是一个 struct led_classdev 类型的结构体,该结构体描述了一个led设备,该结构体的示例代表了一个led设备.当不再需要这个led设备时需要使用led_classdev_unregister()释放掉这个设备.

如果不使用触发和闪烁功能,基于led子系统实现一个led驱动还是挺简单的.接下来看下源码实现:

led子系统源码实现.

        

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值