(linux)LED子系统

数据结构(/include/linux/leds.h)

enum led_brightness {
     LED_OFF          = 0,
     LED_HALF     = 127,
     LED_FULL     = 255,
};

led_classdev代表led的实例:

struct led_classdev {
     const char        *name;               //名字
     int               brightness;          //当前亮度
     int               flags;               //标志,目前只支持 LED_SUSPENDE

    #define  LED_SUSPENDED          (1 << 0)

     /*设置led的亮度,不可以睡眠,有必要的话可以使用工作队列*/
     void (*brightness_set)(struct led_classdev *led_cdev, enum led_brightness brightness);
     /* 获取亮度 */
     enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);
     /* 激活硬件加速的闪烁 */
     int (*blink_set)(struct led_classdev *led_cdev,
                         unsigned long *delay_on,
                         unsigned long *delay_off);

     struct device        *dev;
     struct list_head     node;              /* 所有已经注册的led_classdev使用这个节点串联起来 */
     const char          *default_trigger;   /* 默认触发器 */

    #ifdef CONFIG_LEDS_TRIGGERS        //如果配置内核时使能了触发器功能,才会编译下面一段
     /* 这个读写子轩锁保护下面的触发器数据 */
     struct rw_semaphore  trigger_lock;
     struct led_trigger   *trigger;          //触发器指针
     struct list_head     trig_list;  //触发器使用的链表节点,用来连接同一触发器上的所有led_classdev
     void                 *trigger_data;    //触发器使用的私有数据
    #endif
};

触发器的结构体

#define TRIG_NAME_MAX 50
struct led_trigger {
     const char     *name;          //触发器名字
     void  (*activate)(struct led_classdev *led_cdev);//激活ledled。led_classdev和触发器建立连接时会调用这个方法。
     void  (*deactivate)(struct led_classdev *led_cdev);//取消激活。led_classdev和触发器取消连接时会调用这个方法。

     /* 本触发器控制之下的led链表 */
     rwlock_t          leddev_list_lock; //保护链表的锁
     struct list_head  led_cdevs;    //链表头
     /* 连接下一个已注册触发器的链表节点 ,所有已注册的触发器都会被加入一个全局链表*/
     struct list_head  next_trig;
};

平台设备相关的led数据结构

struct led_info {
     const char     *name;
     char           *default_trigger;
     int             flags;
};

struct led_platform_data {
     int                  num_leds;
     struct led_info     *leds;
};

平台设备相关的gpio led数据结构

struct gpio_led {
     const char   *name;
     char         *default_trigger;
     unsigned      gpio;
     u8            active_low;
};

struct gpio_led_platform_data {
     int              num_leds;
     struct gpio_led  *leds;
     int              (*gpio_blink_set)(unsigned gpio,
                         unsigned long *delay_on,
                         unsigned long *delay_off);
};

led_classdev接口分析/driver/rtc/led-class.c

注册struct led_classdev:

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
     int rc;

     /* 创建一个struct device,他的父设备是parent,drvdata是led_cdev,名字是led_cdev->name,类别是 leds_class*/

     led_cdev->dev = device_create_drvdata(leds_class, parent, 0, led_cdev,
                               "%s", led_cdev->name);
     if (IS_ERR(led_cdev->dev))
          return PTR_ERR(led_cdev->dev);

     /* register the attributes */
     rc = device_create_file(led_cdev->dev, &dev_attr_brightness);//在sys/class/rtc/下创建一个led的属性文件。
     if (rc)
          goto err_out;

     /* add to the list of leds */
     down_write(&leds_list_lock);
     list_add_tail(&led_cdev->node, &leds_list);//将新的led加入链表,全局链表是leds_list
     up_write(&leds_list_lock);

     led_update_brightness(led_cdev);//获取led当前的亮度更新led_cdev的brightness成员

#ifdef CONFIG_LEDS_TRIGGERS
     init_rwsem(&led_cdev->trigger_lock);//初始化led_cdev的触发器自旋锁

     rc = device_create_file(led_cdev->dev, &dev_attr_trigger);//在sys/class/led中为触发器创建属性文件
     if (rc)
          goto err_out_led_list;

     led_trigger_set_default(led_cdev); //为led_cdev设置默认的触发器
#endif

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

     return 0;

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
     device_remove_file(led_cdev->dev, &dev_attr_brightness);
     list_del(&led_cdev->node);
#endif
err_out:
     device_unregister(led_cdev->dev);
     return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);

注销struct led_classdev:

void led_classdev_unregister(struct led_classdev *led_cdev);

注销所做的工作和注册相反。

将led挂起:将led的flag设为LED_SUSPENDED,关闭led.

void led_classdev_suspend(struct led_classdev *led_cdev)

从挂起中恢复:

void led_classdev_resume(struct led_classdev *led_cdev)

sysfs中的属性文件:

/driver/rtc/led-class.c会首先创建一个leds类,生成/sys/class/leds目录。

在led_classdev_register中生成了两个sysfs属性文件,它们使用的属性参数如下:

static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);

led_brightness_showled_brightness_store分别负责显示和设置亮度,用户控件通过

/sys/class/leds/<device>/brightness查看和设置亮度就是和这两个函数交互的。

led_trigger_show用于读取当前触发器的名字,led_trigger_store用于指定触发器的名字,

它会寻找所有已注册的触发器,找到同名的并设置为当前led的触发器。

/sys/class/leds/<device>/trigger用于用户空间查看和设置触发器。

led_classdev全局链表:

led_classdev_register注册的struct led_classdev会被加入leds_list链表,这个链表定义在driver/leds/led-core.c.

led_trigger接口分析/driver/leds/led-triggers.c

注册触发器

int led_trigger_register(struct led_trigger *trigger);

这个函数注册的trigger会被加入全局链表trigger_list,这个链表头是在/driver/leds/led-triggers.c定义的。

此外,这个函数还会遍历所有的已注册的 led_classdev,如果有哪个led_classdev的默认触发器和自己同名,则调用led_trigger_set将自己设为那个led的触发器。

led_classdev注册的时候也会调用led_trigger_set_default来遍历所有已注册的触发器,找到和led_classdev.default_trigger同名的触发器则将它设为自己的触发器。

注销触发器

void led_trigger_unregister(struct led_trigger *trigger);

这个函数做和注册相反的工作,并把所有和自己建立连接的led的led_classdev.trigger设为NULL。

设置触发器上所有的led为某个亮度

void led_trigger_event(struct led_trigger *trigger, enum led_brightness brightness);

注册触发器的简单方法

指定一个名字就可以注册一个触发器,注册的触发器通过**tp返回,但是这样注册的触发器没有active和deactivede。

void led_trigger_register_simple(const char *name, struct led_trigger **tp);

相对应的注销函数为:

void led_trigger_unregister_simple(struct led_trigger *trigger);

触发器和led的连接

void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger);//建立连接。建立连接的时候会调用触发器的activate方法
void led_trigger_remove(struct led_classdev *led_cdev);//取消连接。取消连接的时候会调用触发器的deactivate方法
void led_trigger_set_default(struct led_classdev *led_cdev);//在所有已注册的触发器中寻找led_cdev的默认触发器并调用 led_trigger_set建立连接

最后总结一下led、led_classdev、led_trigger的关系:

也就是说trigger好比是控制LED类设备的算法,这个算法决定着LED什么时候亮什么时候暗。LED trigger类设备可以是现实的硬件设备,比如IDE硬盘,也可以是系统心跳等事件。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux GPIO子系统是一个用于控制嵌入式系统中通用输入/输出(GPIO)的软件子系统。它提供了一种标准的接口,使得应用程序可以通过文件系统接口来访问GPIO。这个子系统可以用于控制各种设备,例如LED、按钮、传感器等等。如果你需要更多的信息,可以查看Linux内核文档。 ### 回答2: Linux GPIO子系统是一种用于管理通用输入输出(GPIO)引脚的软件层。GPIO引脚是一种通用可编程引脚,可以在嵌入式系统中用来通过读取输入或设置输出与外部设备进行通信。 Linux GPIO子系统负责将底层硬件 GPIO 引脚的操作抽象为文件系统的接口,使开发者可以通过读写文件的方式来访问和控制 GPIO 引脚。通过该子系统,可以实现对 GPIO 引脚的配置、读取和写入等操作,以满足不同应用下对 GPIO 的需求。 Linux GPIO子系统的核心是GPIO驱动程序,它与底层硬件层进行交互,完成对GPIO引脚的操作。驱动程序将GPIO引脚映射到内存,通过读写该内存地址即可对引脚进行操作。用户通过访问特定目录下的文件来和引脚进行交互,例如将引脚配置为输入模式、输出模式,以及读取或写入引脚的状态。 通过Linux GPIO子系统,开发者可以方便地进行GPIO引脚的控制。可以根据不同的应用需求,灵活配置引脚的输入输出模式,监听引脚上的状态变化,并根据需要对其他外设进行控制。 总之,Linux GPIO子系统为开发者提供了便捷的接口,使得在嵌入式系统中使用GPIO引脚更加简单和灵活。它允许开发者通过读写文件的方式访问和控制GPIO引脚,满足各种不同嵌入式应用对GPIO的需求。 ### 回答3: Linux的GPIO(General Purpose Input/Output)子系统是通过软件对硬件上的通用输入/输出引脚进行控制的一种机制。它使得开发者可以利用这些GPIO引脚实现各种功能,比如控制LED灯、读取外部传感器的数据等。 Linux的GPIO子系统提供了许多功能和接口来管理和操作GPIO。首先,它使用sysfs文件系统来组织GPIO资源的目录树,并通过文件的方式来读取和写入GPIO的状态。在/sys/class/gpio目录下,每个GPIO引脚都会有一个对应的目录,在该目录中的文件可以用于配置GPIO的方向(输入或输出)、读取和写入GPIO的电平状态。开发者可以使用命令行工具或者编程语言(如Python、C等)来操作这些文件,从而控制GPIO引脚的行为。 其次,Linux的GPIO子系统还提供了设备树(Device Tree)来描述硬件平台上的GPIO资源。设备树是一种描述硬件的数据结构,在启动时通过设备树绑定机制将设备树中定义的GPIO资源与内核驱动程序关联起来。这样,开发者就可以通过调用相应的驱动程序来控制GPIO引脚,而不需要手动操作sysfs文件系统。 此外,Linux的GPIO子系统还支持中断机制,可以让GPIO引脚在特定事件发生时触发中断。通过注册中断处理函数,开发者可以实现对GPIO输入信号的快速响应,提高系统的实时性。 总之,Linux的GPIO子系统为开发者提供了一种方便且灵活的方式来控制硬件上的GPIO引脚。通过sysfs文件系统或设备树,开发者可以轻松地配置、读取和控制GPIO的状态,从而实现各种功能和应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值