日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用

發布時間:2024/10/12 linux 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先看一下Openwrt系統中關于按鍵功能的使用和修改,以18.06版本為例

按鍵功能實現在腳本中, 比如18.06/package/base-files/files/etc/rc.button/reset

#!/bin/sh. /lib/functions.shOVERLAY="$( grep ' /overlay ' /proc/mounts )"case "$ACTION" in pressed)[ -z "$OVERLAY" ] && return 0return 5 ;; timeout). /etc/diag.shset_state failsafe ;; released)if [ "$SEEN" -lt 1 ]thenecho "REBOOT" > /dev/consolesyncrebootelif [ "$SEEN" -ge 5 -a -n "$OVERLAY" ]thenecho "FACTORY RESET" > /dev/consolejffs2reset -y && reboot &fi ;; esacreturn 0

主要有2個參數, ACTION和SEEN,分別代表按鍵動作(按下/抬起)和按鍵持續時間

關于按鍵GPIO的修改位于dts中,比如target/linux/ramips/dts/GL-MT300N-V2.dts

gpio-keys {compatible = "gpio-keys-polled";#address-cells = <1>;#size-cells = <0>;poll-interval = <20>;vccin {label = "BTN_0";gpios = <&gpio0 6 0>;linux,code = <BTN_0>;};electricity {label = "BTN_1";gpios = <&gpio0 11 0>;linux,code = <BTN_1>;};reset {label = "reset";gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;linux,code = <KEY_RESTART>;};};

poll-interval = <20>; 表示輪詢檢測,防抖時間

label = "reset"; 代表功能實現腳本的名稱,對應18.06/package/base-files/files/etc/rc.button/reset

gpios = <&gpio1 7 GPIO_ACTIVE_LOW>; 表示使用GPIO1分組,第7個引腳; 低電平生效

linux,code = <KEY_RESTART>; 按鍵事件代碼,對于linux標準輸入輸出系統,參考Linux內核頭文件input/input.h

compatible = "gpio-keys-polled"; 表示加載驅動gpio-keys-polled

位于18.06/package/kernel/gpio-button-hotplug/src/gpio-button-hotplug.c

它具體實現主要內容如下:

1. 注冊2種類型驅動,輪詢檢測和中斷檢測

2. 使用netlink與用戶空間通訊

3. 自定義上報事件內容, 比如前面功能實現腳本中用到的ACTION以及SEEN等

分析內核驅動,首先要看全局結構體定義,這幾乎是分析linux所有驅動的共性

2個主要結構

struct gpio_keys_platform_data *pdata; 滿足linux內核驅動模型, platform總線驅動設備描述結構,主要存放的是dts中關于設備的描述信息

struct gpio_keys_button_dev *bdev;? 驅動私有描述結構

其二者關系為,

1. 取出pdata信息初始化bdev

2. 把bdev設置為pdata的私有數據,與其他驅動接口同步,platform_set_drvdata(pdev, bdev);

以上的操作為linux驅動的標準執行過程,有興趣的同學可以深入研究下Linux內核驅動模型

對于驅動來說,最重要的是驅動自己的私有結構,即gpio_keys_button_dev

struct gpio_keys_button_dev {int polled; //是否輪詢struct delayed_work work; //用于輪詢時的循環檢測struct device *dev; //設備,來自pdevstruct gpio_keys_platform_data *pdata; //platform總線設備描述struct gpio_keys_button_data data[0];//事件上報描述結構 };

事件描述gpio_keys_button_data

struct gpio_keys_button_data {struct delayed_work work; //執行上報動作struct bh_priv bh;int last_state; //狀態記錄int count; //按鍵計時int threshold; //防抖域值int can_sleep; //是否支持睡眠struct gpio_keys_button *b; //中斷設備描述 };

介紹完驅動結構描述,下面看其使用

首先看輪詢檢測:

static struct platform_driver gpio_keys_polled_driver = {.probe = gpio_keys_polled_probe,.remove = gpio_keys_remove,.driver = {.name = "gpio-keys-polled",.owner = THIS_MODULE,.of_match_table = of_match_ptr(gpio_keys_polled_of_match),}, };

主要實現函數gpio_keys_polled_probe

static int gpio_keys_polled_probe(struct platform_device *pdev) {struct gpio_keys_platform_data *pdata;struct gpio_keys_button_dev *bdev;int ret;int i;//從platform總線設備描述gpio_keys_platform_data獲取信息, //初始化驅動私有結構gpio_keys_button_devret = gpio_keys_button_probe(pdev, &bdev, 1);if (ret)return ret;//初始化工作隊列INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll);pdata = bdev->pdata;if (pdata->enable)pdata->enable(bdev->dev);for (i = 0; i < pdata->nbuttons; i++) //逐個檢測button狀態,準備上報事件數據gpio_keys_button_datagpio_keys_polled_check_state(&bdev->data[i]);//循環檢測button狀態gpio_keys_polled_queue_work(bdev);return ret; }

具體事件上報位于button_hotplug_create_event

static int button_hotplug_create_event(const char *name, unsigned int type,unsigned long seen, int pressed) {struct bh_event *event;BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n",name, seen, pressed);event = kzalloc(sizeof(*event), GFP_KERNEL);if (!event)return -ENOMEM;// 填充事件信息event->name = name;event->type = type;event->seen = seen;event->action = pressed ? "pressed" : "released";// 在工作隊列中上報事件INIT_WORK(&event->work, (void *)(void *)button_hotplug_work);schedule_work(&event->work);return 0; }

上報事件的netlink實現button_hotplug_work

static void button_hotplug_work(struct work_struct *work) {struct bh_event *event = container_of(work, struct bh_event, work);int ret = 0;//分配skbevent->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);if (!event->skb)goto out_free_event;//填充內容ret = bh_event_add_var(event, 0, "%s@", event->action);if (ret)goto out_free_skb;ret = button_hotplug_fill_event(event);if (ret)goto out_free_skb;//發送netlink消息NETLINK_CB(event->skb).dst_group = 1;broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);out_free_skb:if (ret) {BH_ERR("work error %d\n", ret);kfree_skb(event->skb);}out_free_event:kfree(event); }

整個流程總結: 周期循環調用gpio_keys_polled_probe檢測按鍵狀態,滿足條件發送netlink消息

gpio_keys_polled_probe

????????--> gpio_keys_polled_check_state

????????????????--> button_hotplug_create_event

????????????????????????-->button_hotplug_work

????????????????????????????????--> broadcast_uevent

有了以上的分析基礎,中斷檢測流程就變得簡單了

static struct platform_driver gpio_keys_driver = {.probe = gpio_keys_probe,.remove = gpio_keys_remove,.driver = {.name = "gpio-keys",.owner = THIS_MODULE,.of_match_table = of_match_ptr(gpio_keys_of_match),}, };

主要流程如下:

gpio_keys_probe

? --> devm_request_threaded_irq? 注冊中斷

??? --> button_handle_irq 中斷處理函數

??????? --> button_hotplug_create_event

??????????? -->button_hotplug_work

??????????????? -->broadcast_uevent

關于Netlink

Netlink套接字是用以實現用戶進程內核進程通信的一種特殊的進程間通信(IPC) ,也是網絡應用程序與內核通信的最常用的接口。

在Linux 內核中,使用netlink 進行應用與內核通信的應用有很多,如

  • 路由 daemon(NETLINK_ROUTE)
  • 用戶態 socket 協議(NETLINK_USERSOCK)
  • 防火墻(NETLINK_FIREWALL)
  • netfilter 子系統(NETLINK_NETFILTER)
  • 內核事件向用戶態通知(NETLINK_KOBJECT_UEVENT)
  • 通用netlink(NETLINK_GENERIC)

Netlink 是一種在內核與用戶應用間進行雙向數據傳輸的非常好的方式,用戶態應用使用標準的 socket API 就可以使用 netlink 提供的強大功能,內核態需要使用專門的內核 API 來使用 netlink。

Openwrt系統中netlink消息接收,位于procd-2018-03-28-dfb68f85/plug/hotplug.c

procd是openwrt系統的init進程,負責內核netlink消息接收處理,watchdog以及執行一些循環任務等等

void hotplug(char *rules) {struct sockaddr_nl nls = {};int nlbufsize = 512 * 1024;rule_file = strdup(rules);nls.nl_family = AF_NETLINK;nls.nl_pid = getpid();nls.nl_groups = -1;//創建netlink socketif ((hotplug_fd.fd = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT)) == -1) {ERROR("Failed to open hotplug socket: %m\n");exit(1);}if (bind(hotplug_fd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) {ERROR("Failed to bind hotplug socket: %m\n");exit(1);}if (setsockopt(hotplug_fd.fd, SOL_SOCKET, SO_RCVBUFFORCE, &nlbufsize, sizeof(nlbufsize)))ERROR("Failed to resize receive buffer: %m\n");json_script_init(&jctx);queue_proc.cb = queue_proc_cb; //循環接收netlink消息uloop_fd_add(&hotplug_fd, ULOOP_READ); }

調用功能實現腳本時機, procd收到netlink消息后會根據配置文件來搜尋相關功能實現腳本

?按鍵事件的配置文件為 procd/etc/hotplug.json

[ "if",[ "and",[ "has", "BUTTON" ],[ "eq", "SUBSYSTEM", "button" ]],[ "button", "/etc/rc.button/%BUTTON%" ]],

? 可解讀為: procd收到來自BUTTON的消息,最終執行/etc/rc.button/下的腳本,腳本名稱為事件中的button字段取值, 在驅動中以數組方式定義,如下:

static struct bh_map button_map[] = {BH_MAP(BTN_0, "BTN_0"),BH_MAP(BTN_1, "BTN_1"),BH_MAP(BTN_2, "BTN_2"),BH_MAP(BTN_3, "BTN_3"),BH_MAP(BTN_4, "BTN_4"),BH_MAP(BTN_5, "BTN_5"),BH_MAP(BTN_6, "BTN_6"),BH_MAP(BTN_7, "BTN_7"),BH_MAP(BTN_8, "BTN_8"),BH_MAP(BTN_9, "BTN_9"),BH_MAP(KEY_BRIGHTNESS_ZERO, "brightness_zero"),BH_MAP(KEY_CONFIG, "config"),BH_MAP(KEY_COPY, "copy"),BH_MAP(KEY_EJECTCD, "eject"),BH_MAP(KEY_HELP, "help"),BH_MAP(KEY_LIGHTS_TOGGLE, "lights_toggle"),BH_MAP(KEY_PHONE, "phone"),BH_MAP(KEY_POWER, "power"),BH_MAP(KEY_RESTART, "reset"),BH_MAP(KEY_RFKILL, "rfkill"),BH_MAP(KEY_VIDEO, "video"),BH_MAP(KEY_WIMAX, "wwan"),BH_MAP(KEY_WLAN, "wlan"),BH_MAP(KEY_WPS_BUTTON, "wps"), };

BH_MAP第一個參數BTN_0或KEY_POWER對應input子系統的input.h中標準定義;

第二個參數"BTN_0"或"reset"對應/etc/rc.button/下的腳本名稱

至此,openwrt按鍵檢測整個過程分析完畢

最后總結:

1. Openwrt系統按鍵內核驅動分為兩種, 循環檢測和中斷檢測, 在dts中配置

2. 按鍵驅動通過netlink方式發送按鍵事件到用戶空間

3. 在用戶空間, init進程procd統一接收處理來自內核的netlink消息,同時根據配置文件,調用/etc/rc.button/下的腳本, 此處需注意腳本的可執行權限

4. dts中關于按鍵的配置, linux,code為input子系統標準事件定義,即input.h中的定義

總結

以上是生活随笔為你收集整理的Openwrt按键检测分析-窥探Linux内核与用户空间通讯机制netlink使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。