Led驱动架构理解

      作为一个驱动工程师,每每遇到问题,总是抓耳挠腮,查找许久。是否有一些本质的特性,能让工作变得轻松? 如果有,那可能是对驱动本质的理解,对器件工作特性的熟悉,首先了解本质基于2.6.3内核的led驱动框架来分析,并记录。

      以下是浏览led驱动的记录,首先按照驱动模块理解的惯例,先看看init函数

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

    

函数 class_create() 创建一个新的类在"/sys"目录下,类名叫 ”leds”,并返回这个新生成的类。然后在对该类的的电源管理函数、类的属性做初始化。关于类的初始化,led 的定义是:

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,
};

正如注释所陈述的那样,结构体 “device_attribute” 是用来导出设备属性的

/* interface for exporting device attributes */
struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

对于一切设备皆文件的linux来说,设备的属性无非就是读写,所以,导出设备属性的结构体有读函数 [ssize_t  (*show)(struct device *dev, struct_device_attribute *attr, chat *buf)]  和写函数 [ssize_t  (*store)(struct device *dev, struct_device_attribute *attr, chat *buf, size_t count)]。其中 dev,对应要被操作的dev, attr 对应要读写的属性,buf对应需要设备做什么,具体的功能还需要设备驱动开发者实现。比如,输入1 或者on 对应led的亮,这里,1/on 就是 buf 的内容,dev 是led, 亮是attr。

结构体 struct attribute 是 用户的操作权限,由于linux是多文件系统(尽管嵌入式很多设备都是单用户——root),所有这个属性规定者不同用户、用户组对设备的访问权限。

     看完init,接着看看exit函数

static void __exit leds_exit(void)
{
	class_destroy(leds_class);
}

即为注销已经注册了的led类。

 

    设备驱动接口函数:

(1)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
};

  设备结构体体,是内核驱动开发者(开发总线、驱动架构者)开放给设备驱动工程师的设备接口,用以抽象出一个设备在内核中,或者用以连接设备驱动程序到内核驱动中的,从而使得应用开发者编写应用程序时,能准确无误的调用到驱动中相关的函数,从而操作硬件。比如 struct led_class中, 函数指针 “void        (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness)   ”是led驱动实现后,当用户空间操作前文所述的 store(写方法时),就会调用,调用关系如下:

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 (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 inline void led_set_brightness(struct led_classdev *led_cdev,
                    enum led_brightness value)
{
    if (value > led_cdev->max_brightness)
        value = led_cdev->max_brightness;
    led_cdev->brightness = value;
    if (!(led_cdev->flags & LED_SUSPENDED))
        led_cdev->brightness_set(led_cdev, value);
}

关于 trigger 还没有接触过,猜想它的应用场景是诸如 当有设备接入,显示状态这一类的,比如网口的灯那种。

(2) 注册设备

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);
	if (IS_ERR(led_cdev->dev))
		return PTR_ERR(led_cdev->dev);

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

#ifdef CONFIG_LEDS_TRIGGERS
	led_trigger_set_default(led_cdev);
#endif

	printk(KERN_DEBUG "Registered led device: %s\n",
			led_cdev->name);

	return 0;
}

首先调用 device_create ()创建设备、注册在sys下并返回。

(3)led设备注销

void led_classdev_unregister(struct led_classdev *led_cdev)
{
#ifdef CONFIG_LEDS_TRIGGERS
	down_write(&led_cdev->trigger_lock);
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
	up_write(&led_cdev->trigger_lock);
#endif

	device_unregister(led_cdev->dev);

	down_write(&leds_list_lock);
	list_del(&led_cdev->node);
	up_write(&leds_list_lock);
}

基本上来说,led的框架就这些,主要的是设备驱动的实现、读写方法的定义。深入分析就是内核机制的东西了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值