11-Openwrt hotplug system

hotplug為linux的一個熱拔插系統(tǒng)盒至,在很多應用都有用到椒功,如網(wǎng)口的拔插华烟,USB的拔插心墅,按鍵的觸發(fā)...非常廣泛的應用酿矢,下面舉幾個例子來進行理解這個過程

1.gpio-button-hotplug(內(nèi)核層發(fā)送hotplug到procd)

gpio-button-hotplug為kernel的一個package榨乎,位于package/kernel/gpio-button-hotplug/

要把CONFIG_PACKAGE_kmod-gpio-button-hotplug=y選項打開

1.1驅動

"gpio-keys"和"gpio-keys-polled"就是兩個platform設備,如下:

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

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

static int __init gpio_button_init(void)
{
    int ret;

    ret = platform_driver_register(&gpio_keys_driver);
    if (ret)
        return ret;

    ret = platform_driver_register(&gpio_keys_polled_driver);
    if (ret)
        platform_driver_unregister(&gpio_keys_driver);

    return ret;
}

static void __exit gpio_button_exit(void)
{
    platform_driver_unregister(&gpio_keys_driver);
    platform_driver_unregister(&gpio_keys_polled_driver);
}

module_init(gpio_button_init);
module_exit(gpio_button_exit);

與以前的做法一樣瘫筐,device和driver蜜暑,driver這邊有了,device要么arch里面要么dts里面策肝。

看下DTS里面的配置

gpio-keys-polled {
    compatible = "gpio-keys-polled";
    #address-cells = <1>;
    #size-cells = <0>;
    poll-interval = <20>;

    power {
        label = "power";
        gpios = <&gpio0 24 1>;  //GPIO24 line is low, key is pressed
        linux,code = <116>;     //KEY_POWER
    };
    reset {
        label = "reset";
        gpios = <&gpio1 11 1>;  //GPIO43 line is low, key is pressed
        linux,code = <0x198>;   //KEY_RESTART
    };
};

上面的這些參數(shù)数冬,在gpio_keys_get_devtree_pdata函數(shù)里面都會進行解析判斷格式是否正確钧排,錯誤在啟動注冊時就會直接提示錯誤信息。

另一種就是在arch里面注冊,
在zkernel/3.10.49/arch/mips/mtk/dev-gpio-buttons.c里面提供了注冊接口蚂子,調(diào)用即可绅这。

#define ZRMT7621_KEYS_POLL_INTERVAL         20
#define ZRMT7621_KEYS_DEBOUNCE_INTERVAL     (3 * ZRMT7621_KEYS_POLL_INTERVAL)

static struct gpio_keys_button zrmt7621_gpio_buttons[] __initdata = {
    {
        .desc       = "reset",
        .type       = EV_KEY,
        .code       = KEY_RESTART,
        .debounce_interval = ZRMT7621_KEYS_DEBOUNCE_INTERVAL,
        .gpio       = ZRMT7621_GPIO_BUTTON_RESET,
        .active_low = 1,
    },
};


ramips_register_gpio_buttons(-1, ZRMT7621_KEYS_POLL_INTERVAL,
         ARRAY_SIZE(zrmt7621_gpio_buttons),
                    zrmt7621_gpio_buttons);

在上面的button結構體里面要定義好對應的type和code赔桌,

這邊故意將linux,code里面的數(shù)值寫成數(shù)值瓢剿,是為了讓我們看到更底層的定義,在gpio-button-hotplug.c里面有如下定義帘睦,

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

include/dt-bindings/input/linux-event-codes.h里面有如下定義袍患,所以就知道最終的數(shù)值了。

#define KEY_RESTART     0x198

#define KEY_INSERT      110
#define KEY_DELETE      111
#define KEY_MACRO       112
#define KEY_MUTE        113
#define KEY_VOLUMEDOWN  114
#define KEY_VOLUMEUP    115
#define KEY_POWER       116 /* SC System Power Down */
#define KEY_KPEQUAL     117
#define KEY_KPPLUSMINUS 118
#define KEY_PAUSE       119
#define KEY_SCALE       120 /* AL Compiz Scale (Expose) */

驅動加載成功則會有如下信息:

root@zihome:/sys/devices/platform/gpio-keys-polled# ls
driver     modalias   subsystem  uevent
1.2 key的應用層處理

當按鍵時竣付,則觸發(fā)button_hotplug_event函數(shù)(gpio-button-hotplug.c):調(diào)用button_hotplug_create_event產(chǎn)生uevent事件诡延,調(diào)用button_hotplug_fill_even填充事件(JSON格式),并最終調(diào)用broadcast_uevent發(fā)出uevent廣播信息古胆,后由內(nèi)核netlink_broadcast函數(shù)(linux-3.10.49/net/netlink/af_netlink.c)

netlink的實現(xiàn)原理可以看下面這篇文件的介紹肆良,為socket通信,內(nèi)核發(fā)出socket廣播逸绎,上層應用(procd)只需要監(jiān)聽這個socket事件即可惹恃。
https://blog.csdn.net/Swallow_he/article/details/84073545

上述廣播,被procd進程中的hotplug_handler (procd/plug/hotplug.c) 收到棺牧,并根據(jù)etc/hotplug.json中預先定義的JSON內(nèi)容匹配條件巫糙,定位到對應的執(zhí)行函數(shù),具體為:

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

最終會執(zhí)行/etc/rc.button/里面的對應的腳本颊乘,如reset/power参淹,腳本的名字要跟button_map結構里面的一致。

root@LEDE:/# cat etc/rc.button/power 
#!/bin/sh

[ "${ACTION}" = "released" ] || exit 0

exec /sbin/poweroff

return 0
root@LEDE:/# cat etc/rc.button/reset 
#!/bin/sh

. /lib/functions.sh

OVERLAY="$( grep ' /overlay ' /proc/mounts )"

case "$ACTION" in
pressed)
        [ -z "$OVERLAY" ] && return 0

        return 5
;;
timeout)
        . /etc/diag.sh
        set_state failsafe
;;
released)
        if [ "$SEEN" -lt 1 ]
        then
                echo "REBOOT" > /dev/console
                sync
                reboot
        elif [ "$SEEN" -gt 5 -a -n "$OVERLAY" ]
        then
                echo "FACTORY RESET" > /dev/console
                jffs2reset -y && reboot &
        fi
;;
esac

return 0

2.WAN口網(wǎng)線是否插入檢測(phy內(nèi)核發(fā)出)

內(nèi)核檢測到WAN口變化后會創(chuàng)建hotplug消息(broadcast_uevent)乏悄,發(fā)送給procd浙值,再轉發(fā)到對應的模塊

static void phy_hotplug_work(struct work_struct *work)
{
    struct bh_event *event = container_of(work, struct bh_event, work);
    int ret = 0;

    event->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 = phy_hotplug_fill_event(event);
    if (ret)
        goto out_free_skb;

    if (event->type) {
        printk(KERN_NOTICE "phy: port%u %s(irq)\n", event->port_num, event->action);
    } else {
        printk(KERN_NOTICE "phy: port%u %s(dev)\n", event->port_num, event->action);
    }

    NETLINK_CB(event->skb).dst_group = 1;
    broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);

 out_free_skb:
    if (ret) {
        kfree_skb(event->skb);
    }
 out_free_event:
    kfree(event);
}

發(fā)出后就會觸發(fā)以下腳本,在腳本里面添加我們需要的內(nèi)容

vim /etc/hotplug.d/phy/00-wan

case "$wan_ifname" in                                                
"eth"*)                                                              
        if [ "$wan_port" = "$PORTNUM" ]; then                        
                logger -t "phy" "$PORTNUM $ACTION"                   
                mkdir -p /tmp/status >/dev/null 2>&1                 
                case "$ACTION" in                                    
                "linkup")                                            
                        echo "1" >/tmp/status/wan_port_status        
                        ubus call zboard set_wan "{\"status\":1,\"port\":$wan_port}"
                        dhcp_handle_up "wan" "$wan_ifname"                          
                        ;;                                                          
                "linkdown")                                                         
                        echo "0" >/tmp/status/wan_port_status                       
                        ubus call zboard set_wan "{\"status\":0,\"port\":$wan_port}"
                        dhcp_handle_down "wan" "$wan_ifname"                        
                        ;;                                                          
                esac                                                                
                                                                                    
                phy_hotplug "wan" $ACTION                                           
        fi                                                                          
        ;;                                                                          
*)                                                                                  
        if [ "$wan_port" = "$PORTNUM" ]; then                                       
                logger -t "phy" "$PORTNUM $ACTION for wisp"                         
                mkdir -p /tmp/status >/dev/null 2>&1                                
                case "$ACTION" in                                                   
                "linkup")                                                           
                        echo "1" >/tmp/status/wan_port_status                       
                        ubus call zboard set_wan "{\"status\":1,\"port\":$wan_port}"
                        ;;                                                          
                "linkdown")                                                         
                        echo "0" >/tmp/status/wan_port_status                       
                        ubus call zboard set_wan "{\"status\":0,\"port\":$wan_port}"
                        ;;                                                          
                esac                                                                
        fi                                                                          
        ;;                                                                          
esac                

3.網(wǎng)絡檢測添加LED閃爍(/sbin/hotplug-call)

3.1 zdetect網(wǎng)絡檢測模塊

在zrouter/zpackages/zihome/utils/zdetect/src/zdetect.c里面會發(fā)送hotplug event當網(wǎng)絡變化的時候:

static void inet_hotplug(const char* action)
{
    char *argv[3];
    int pid;

    pid = fork();
    if (pid < 0) {
        dbg_printf(MSG_INFO, "hotplug_event fork failed!");
        return;
    } else if (pid == 0) {
        setenv("ACTION", action, 1); 

        argv[0] = HOTPLUG_PATH;
        argv[1] = "inet";
        argv[2] = NULL;
        execvp(argv[0], argv);
        exit(127);
    }   
}
3.2 收到inet模塊hotplug消息后

14.07/package/base-files/files/etc/hotplug.d/inet/00-inet

#!/bin/sh

case "$ACTION" in
        "online")
        logger -t "inet" "detect online"
        zihome_led yellow on
        ;;
        "offline")
        logger -t "inet" "detect offline"
        zihome_led yellow 1000 1000
        ;;
        *) ;;
esac
3.3 調(diào)用led執(zhí)行腳本

14.07/package/base-files/files/sbin/zihome_led

#!/bin/sh

. /lib/functions/leds.sh

led_path="zihome:""$1"

led

4.iface(netifd)

每次網(wǎng)絡接口啟動(up)或者關閉(down)的時候檩小,所有在/etc/hotplug.d/iface/目錄中的腳本都會以字母順序執(zhí)行开呐。根據(jù)一個不成文的規(guī)則,會在每個腳本的前面加上一個數(shù)字前綴來設置正確的運行順序。這就是為什么腳本名稱都像:/etc/hotplug.d/iface/<nn>-<scriptname>的原因负蚊。

變量名稱 描述
ACTION "ifup" 或者 "ifdown"
INTERFACE 網(wǎng)絡接口的名稱神妹,如"wan"
DEVICE 物理設備的名稱颓哮,如"br-lan"

https://blog.csdn.net/vivianliulu/article/details/79629836

static void call_hotplug(void)
{
    const char *device = NULL;
    if (list_empty(&pending))
        return;

    current = list_first_entry(&pending, struct interface, hotplug_list);
    current_ev = current->hotplug_ev;
    list_del_init(&current->hotplug_list);

    if ((current_ev == IFEV_UP || current_ev == IFEV_UPDATE) && current->l3_dev.dev)
        device = current->l3_dev.dev->ifname;

    D(SYSTEM, "Call hotplug handler for interface '%s', event '%s' (%s)\n",
    current->name, eventnames[current_ev], device ? device : "none");
    run_cmd(current->name, device, current_ev, current->updated);
}
static void run_cmd(const char *ifname, const char *device, enum interface_event event,
        enum interface_update_flags updated)
{
    char *argv[3];
    int pid;

    pid = fork();
    if (pid < 0)
        return task_complete(NULL, -1);

    if (pid > 0) {
        task.pid = pid;
        uloop_process_add(&task);
        return;
    }

    setenv("ACTION", eventnames[event], 1);
    setenv("INTERFACE", ifname, 1);
    if (device)
        setenv("DEVICE", device, 1);

    if (event == IFEV_UPDATE) {
        if (updated & IUF_ADDRESS)
            setenv("IFUPDATE_ADDRESSES", "1", 1);
        if (updated & IUF_ROUTE)
            setenv("IFUPDATE_ROUTES", "1", 1);
        if (updated & IUF_PREFIX)
            setenv("IFUPDATE_PREFIXES", "1", 1);
        if (updated & IUF_DATA)
            setenv("IFUPDATE_DATA", "1", 1);
    }

    argv[0] = hotplug_cmd_path;
    argv[1] = "iface";
    argv[2] = NULL;
    execvp(argv[0], argv);
    exit(127);
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末家妆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子冕茅,更是在濱河造成了極大的恐慌伤极,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姨伤,死亡現(xiàn)場離奇詭異哨坪,居然都是意外死亡,警方通過查閱死者的電腦和手機乍楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門当编,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人徒溪,你說我怎么就攤上這事忿偷。” “怎么了臊泌?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵鲤桥,是天一觀的道長。 經(jīng)常有香客問我渠概,道長茶凳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任播揪,我火速辦了婚禮贮喧,結果婚禮上,老公的妹妹穿的比我還像新娘猪狈。我一直安慰自己塞淹,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布罪裹。 她就那樣靜靜地躺著饱普,像睡著了一般。 火紅的嫁衣襯著肌膚如雪状共。 梳的紋絲不亂的頭發(fā)上套耕,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音峡继,去河邊找鬼冯袍。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的康愤。 我是一名探鬼主播儡循,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼征冷!你這毒婦竟也來了择膝?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤检激,失蹤者是張志新(化名)和其女友劉穎肴捉,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叔收,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡齿穗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饺律。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窃页。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖复濒,靈堂內(nèi)的尸體忽然破棺而出脖卖,到底是詐尸還是另有隱情,我是刑警寧澤芝薇,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布胚嘲,位于F島的核電站,受9級特大地震影響洛二,放射性物質發(fā)生泄漏馋劈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一晾嘶、第九天 我趴在偏房一處隱蔽的房頂上張望妓雾。 院中可真熱鬧,春花似錦垒迂、人聲如沸械姻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽楷拳。三九已至,卻和暖如春吏奸,著一層夾襖步出監(jiān)牢的瞬間欢揖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工奋蔚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留她混,地道東北人烈钞。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像坤按,于是被迫代替她去往敵國和親毯欣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容