linux裝置驅動之led子系統
阿新 • • 發佈:2019-01-28
/include/linux/leds.h
enumled_brightness{
LED_OFF = 0,
LED_HALF = 127,
LED_FULL = 255,
};
led_classdev代表led的例項:
structled_classdev{
const char *name; //名字
int brightness; //當前亮度
int flags; //標誌,目前只支援LED_SUSPENDED
#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
structled_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: intled_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_show和led_brightness_store分別負責顯示和設定亮度,使用者控制元件通過 /sys/class/leds//brightness檢視和設定亮度就是和這兩個函式互動的。 led_trigger_show用於讀取當前觸發器的名字,led_trigger_store用於指定觸發器的名字, 它會尋找所有已註冊的觸發器,找到同名的並設定為當前led的觸發器。 /sys/class/leds//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硬碟,也可以是系統心跳等事件。
LED_OFF = 0,
LED_HALF = 127,
LED_FULL = 255,
};
led_classdev代表led的例項:
structled_classdev{
const char *name; //名字
int brightness; //當前亮度
int flags; //標誌,目前只支援LED_SUSPENDED
#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
}; 觸發器的結構體
structled_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資料結構
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: intled_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_show和led_brightness_store分別負責顯示和設定亮度,使用者控制元件通過 /sys/class/leds//brightness檢視和設定亮度就是和這兩個函式互動的。 led_trigger_show用於讀取當前觸發器的名字,led_trigger_store用於指定觸發器的名字, 它會尋找所有已註冊的觸發器,找到同名的並設定為當前led的觸發器。 /sys/class/leds//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硬碟,也可以是系統心跳等事件。