opensync: Support Multi-SSID per radio. Support radioX -> wlanX-Y naming convention.

Signed-off-by: Rick Sommerville <rick.sommerville@netexperience.com>
This commit is contained in:
Rick Sommerville
2020-07-20 09:25:47 -04:00
committed by John Crispin
parent 75b5e5a88e
commit 6a6cb55ff0
16 changed files with 106 additions and 276 deletions

View File

@@ -1,64 +0,0 @@
#!/bin/sh
append HOOKS "wlan_ap"
mac=$(cat /sys/class/net/eth0/address | tr -d :)
_wifi_rename() {
local old=$1
local new=$2
local vif=$3
local path
local band=$5
config_get path "${old}" path
[ "${path}" == "$4" ] || return 0
uci -q rename wireless.${old}=${new}
uci -q set wireless.${new}.freq_band=${band}
uci -q set wireless.${new}.disabled=0
uci -q rename wireless.default_${old}=${vif}
uci -q set wireless.${vif}.device=${new}
uci -q set wireless.${vif}.ifname=${vif}
uci -q set wireless.${vif}.index=0
uci -q set wireless.${vif}.ssid="Maverick-${mac:6}"
}
wifi_rename() {
local radio=$1
local vif=$2
local path=$3
local band=$4
[ -z "$(uci -q get wireless.${vif}.device)" ] || return 0
config_foreach _wifi_rename wifi-device "${radio}" "${vif}" "${path}" "${band}"
}
run_wlan_ap() {
config_load wireless
case "$(board_name)" in
linksys,ea8300)
wifi_rename wifi0 home_ap_24 'platform/soc/a000000.wifi' '2.4G'
wifi_rename wifi1 home_ap_l50 'platform/soc/a800000.wifi' '5GL'
wifi_rename wifi2 home_ap_u50 'soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0' '5GU'
;;
edgecore,ecw5410)
wifi_rename wifi0 home_ap_24 'soc/1b900000.pci/pci0002:00/0002:00:00.0/0002:01:00.0' '2.4G'
wifi_rename wifi1 home_ap_50 'soc/1b700000.pci/pci0001:00/0001:00:00.0/0001:01:00.0' '5G'
;;
edgecore,ecw5211)
wifi_rename wifi0 home_ap_24 'platform/soc/a000000.wifi' '2.4G'
wifi_rename wifi1 home_ap_50 'platform/soc/a800000.wifi' '5G'
;;
tp-link,ap2220|\
tp-link,ec420-g1)
wifi_rename wifi0 home_ap_24 'platform/soc/a000000.wifi' '2.4G'
wifi_rename wifi1 home_ap_50 'soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0' '5G'
;;
*)
;;
esac
uci commit wireless
}

View File

@@ -1,43 +0,0 @@
#!/bin/sh
append HOOKS "wlan_ap"
_wifi_rename() {
local old=$1
local new=$2
local vif=$3
local path
config_get path "${old}" path
[ "${path}" == "$4" ] || return 0
uci -q rename wireless.${old}=${new}
uci -q rename wireless.default_${old}=${vif}
uci -q set wireless.${vif}.device=${new}
uci -q set wireless.${vif}.ifname=${vif}
uci -q set wireless.${vif}.index=0
}
wifi_rename() {
local radio=$1
local vif=$2
local path=$3
[ -z "$(uci -q get wireless.${vif}.device)" ] || return 0
config_foreach _wifi_rename wifi-device "${radio}" "${vif}" "${path}" "${band}"
}
run_wlan_ap() {
config_load wireless
case "$(board_name)" in
linksys,ea8300)
wifi_rename wifi0 home_ap_24 'platform/soc/a000000.wifi'
wifi_rename wifi1 home_ap_l50 'platform/soc/a800000.wifi'
wifi_rename wifi2 home_ap_u50 'soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0'
;;
*)
;;
esac
uci commit wireless
}

View File

@@ -9,3 +9,12 @@
static bool
wm2_vconf_changed(const struct schema_Wifi_VIF_Config *conf,
const struct schema_Wifi_VIF_State *state,
struct schema_Wifi_VIF_Config_flags *changedf)
{
int changed = 0;
memset(changedf, 0, sizeof(*changedf));
changed |= (changedf->_uuid = strcmp(conf->_uuid.uuid, state->vif_config.uuid));
+ LOGI("%s: %s %s", conf->if_name, conf->ssid, state->ssid);
CMP(CHANGED_INT, enabled);

View File

@@ -25,6 +25,8 @@ extern int vif_is_ready(const char *name);
extern int blobmsg_add_hex16(struct blob_buf *buf, const char *name, uint16_t val);
extern int blobmsg_get_hex16(struct blob_attr *a);
extern char* vif_ifname_to_sectionname( const char* ifname, char* sectionname );
extern char* vif_sectionname_to_ifname( const char* sectionname, char* ifname );
extern bool vif_state_to_conf(struct schema_Wifi_VIF_State *vstate, struct schema_Wifi_VIF_Config *vconf);
extern bool radio_state_to_conf(struct schema_Wifi_Radio_State *rstate, struct schema_Wifi_Radio_Config *rconf);

View File

@@ -67,7 +67,6 @@ static bool radio_state_update(struct uci_section *s, struct schema_Wifi_Radio_C
{
struct blob_attr *tb[__WDEV_ATTR_MAX] = { };
struct schema_Wifi_Radio_State rstate;
char *freq_band = NULL;
char phy[6];
LOGN("%s: get state", rstate.if_name);
@@ -86,19 +85,6 @@ static bool radio_state_update(struct uci_section *s, struct schema_Wifi_Radio_C
SCHEMA_SET_STR(rstate.if_name, s->e.name);
if (tb[WDEV_ATTR_FREQ_BAND])
freq_band = blobmsg_get_string(tb[WDEV_ATTR_FREQ_BAND]);
if (freq_band && (!strcmp(freq_band, "5G") || !strcmp(freq_band, "5GU"))) {
STRSCPY(rstate.hw_config_keys[0], "dfs_enable");
snprintf(rstate.hw_config[0], sizeof(rstate.hw_config[0]), "1");
STRSCPY(rstate.hw_config_keys[1], "dfs_ignorecac");
snprintf(rstate.hw_config[1], sizeof(rstate.hw_config[0]), "0");
STRSCPY(rstate.hw_config_keys[2], "dfs_usenol");
snprintf(rstate.hw_config[2], sizeof(rstate.hw_config[0]), "1");
rstate.hw_config_len = 3;
}
if (!tb[WDEV_ATTR_PATH] ||
phy_from_path(blobmsg_get_string(tb[WDEV_ATTR_PATH]), phy)) {
LOGE("%s has no phy", rstate.if_name);
@@ -167,6 +153,16 @@ static bool radio_state_update(struct uci_section *s, struct schema_Wifi_Radio_C
else if (!phy_get_band(phy, rstate.freq_band))
rstate.freq_band_exists = true;
if (strcmp(rstate.freq_band, "2.4G")) {
STRSCPY(rstate.hw_config_keys[0], "dfs_enable");
snprintf(rstate.hw_config[0], sizeof(rstate.hw_config[0]), "1");
STRSCPY(rstate.hw_config_keys[1], "dfs_ignorecac");
snprintf(rstate.hw_config[1], sizeof(rstate.hw_config[0]), "0");
STRSCPY(rstate.hw_config_keys[2], "dfs_usenol");
snprintf(rstate.hw_config[2], sizeof(rstate.hw_config[0]), "1");
rstate.hw_config_len = 3;
}
if (!phy_get_mac(phy, rstate.mac))
rstate.mac_exists = true;
@@ -233,7 +229,8 @@ bool target_radio_config_set2(const struct schema_Wifi_Radio_Config *rconf,
static void periodic_task(void *arg)
{
static int counter = 0;
struct uci_element *e = NULL;
struct uci_element *e = NULL, *tmp = NULL;
char ifname[20];
if ((counter % 15) && !reload_config)
goto done;
@@ -247,18 +244,18 @@ static void periodic_task(void *arg)
LOGT("periodic: start state update ");
uci_load(uci, "wireless", &wireless);
uci_foreach_element(&wireless->sections, e) {
uci_foreach_element_safe(&wireless->sections, tmp, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "wifi-device"))
radio_state_update(s, NULL);
}
uci_foreach_element(&wireless->sections, e) {
uci_foreach_element_safe(&wireless->sections, tmp, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "wifi-iface"))
if (vif_find(s->e.name))
if (vif_find(vif_sectionname_to_ifname(s->e.name, ifname)))
vif_state_update(s, NULL);
}
uci_unload(uci, wireless);
@@ -273,17 +270,17 @@ bool target_radio_config_init2(void)
{
struct schema_Wifi_Radio_Config rconf;
struct schema_Wifi_VIF_Config vconf;
struct uci_element *e = NULL;
struct uci_element *e = NULL, *tmp = NULL;
uci_load(uci, "wireless", &wireless);
uci_foreach_element(&wireless->sections, e) {
uci_foreach_element_safe(&wireless->sections, tmp, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "wifi-device"))
radio_state_update(s, &rconf);
}
uci_foreach_element(&wireless->sections, e) {
uci_foreach_element_safe(&wireless->sections, tmp, e) {
struct uci_section *s = uci_to_section(e);
if (!strcmp(s->type, "wifi-iface"))
@@ -299,14 +296,10 @@ bool target_radio_init(const struct target_radio_ops *ops)
uci = uci_alloc_context();
target_map_init();
target_map_insert("home-ap-24", "home_ap_24");
target_map_insert("home-ap-50", "home_ap_50");
target_map_insert("home-ap-l50", "home_ap_l50");
target_map_insert("home-ap-u50", "home_ap_u50");
target_map_insert("wifi0", "phy1");
target_map_insert("wifi1", "phy2");
target_map_insert("wifi2", "phy0");
target_map_insert("radio0", "phy0");
target_map_insert("radio1", "phy1");
target_map_insert("radio2", "phy2");
radio_ops = ops;

View File

@@ -82,7 +82,7 @@ static void vif_add_station(struct wifi_station *sta, char *ifname, int assoc)
SCHEMA_SET_STR(client.mac, mac);
if (assoc)
SCHEMA_SET_STR(client.state, "active");
radio_ops->op_client(&client, target_unmap_ifname(ifname), assoc);
radio_ops->op_client(&client, ifname, assoc);
}
static void vif_add_sta_rate_rule(uint8_t *addr, char *ifname)

View File

@@ -46,7 +46,7 @@ bool target_is_radio_interface_ready(char *phy_name)
bool target_is_interface_ready(char *if_name)
{
return iface_is_up(target_map_ifname(if_name));
return iface_is_up(if_name);
}
/******************************************************************************
@@ -92,7 +92,7 @@ bool target_stats_clients_get(radio_entry_t *radio_cfg, radio_essid_t *essid,
ds_dlist_t *client_list, void *client_ctx)
{
struct nl_call_param nl_call_param = {
.ifname = target_map_ifname(radio_cfg->if_name),
.ifname = radio_cfg->if_name,
.type = radio_cfg->type,
.list = client_list,
};
@@ -156,7 +156,7 @@ bool target_stats_survey_get(radio_entry_t *radio_cfg, uint32_t *chan_list,
ds_dlist_t *survey_list, void *survey_ctx)
{
struct nl_call_param nl_call_param = {
.ifname = target_map_ifname(radio_cfg->if_name),
.ifname = radio_cfg->if_name,
.type = radio_cfg->type,
.list = survey_list,
};
@@ -196,7 +196,7 @@ bool target_stats_scan_start(radio_entry_t *radio_cfg, uint32_t *chan_list, uint
target_scan_cb_t *scan_cb, void *scan_ctx)
{
struct nl_call_param nl_call_param = {
.ifname = target_map_ifname(radio_cfg->if_name),
.ifname = radio_cfg->if_name,
};
bool ret = true;
@@ -214,7 +214,7 @@ bool target_stats_scan_start(radio_entry_t *radio_cfg, uint32_t *chan_list, uint
bool target_stats_scan_stop(radio_entry_t *radio_cfg, radio_scan_type_t scan_type)
{
struct nl_call_param nl_call_param = {
.ifname = target_map_ifname(radio_cfg->if_name),
.ifname = radio_cfg->if_name,
};
bool ret = true;
@@ -229,7 +229,7 @@ bool target_stats_scan_get(radio_entry_t *radio_cfg, uint32_t *chan_list, uint32
radio_scan_type_t scan_type, dpp_neighbor_report_data_t *scan_results)
{
struct nl_call_param nl_call_param = {
.ifname = target_map_ifname(radio_cfg->if_name),
.ifname = radio_cfg->if_name,
.type = radio_cfg->type,
.list = &scan_results->list,
};

View File

@@ -117,8 +117,8 @@ static int nl80211_assoclist_recv(struct nl_msg *msg, void *arg)
client_entry->info.type = nl_call_param->type;
memcpy(client_entry->info.mac, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
memcpy(client_entry->info.essid,
target_unmap_ifname(nl_call_param->ifname),
sizeof(client_entry->info.ifname));
nl_call_param->ifname,
sizeof(client_entry->info.essid));
if (sinfo[NL80211_STA_INFO_TX_BYTES])
client_entry->stats.bytes_tx = nla_get_u32(sinfo[NL80211_STA_INFO_TX_BYTES]);
@@ -492,7 +492,7 @@ int nl80211_scan_trigger(struct nl_call_param *nl_call_param, uint32_t *chan_lis
if (!msg)
return -1;
LOGE("%s: not setting dwell time\n", nl_call_param->ifname);
LOGN("%s: not setting dwell time\n", nl_call_param->ifname);
//nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION, dwell_time);
freq = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
for (i = 0; i < chan_num; i ++)

View File

@@ -52,14 +52,10 @@ bool target_init(target_init_opt_t opt, struct ev_loop *loop)
{
wifihal_evloop = loop;
target_map_init();
target_map_insert("home-ap-24", "home_ap_24");
target_map_insert("home-ap-50", "home_ap_50");
target_map_insert("home-ap-l50", "home_ap_l50");
target_map_insert("home-ap-u50", "home_ap_u50");
target_map_insert("wifi0", "phy1");
target_map_insert("wifi1", "phy2");
target_map_insert("wifi2", "phy0");
target_map_insert("radio0", "phy0");
target_map_insert("radio1", "phy1");
target_map_insert("radio2", "phy2");
switch (opt) {
case TARGET_INIT_MGR_SM:

View File

@@ -144,7 +144,7 @@ static void ubus_reconnect_cb(void *arg)
evsched_task_reschedule_ms(EVSCHED_SEC(1));
return;
}
LOG(ERR, "ubus: connected");
LOG(INFO, "ubus: connected");
ubus.connection_lost = ubus_connection_lost_cb;

View File

@@ -264,6 +264,29 @@ int blobmsg_get_hex16(struct blob_attr *a)
return (int) strtoul(val, NULL, 16);
}
char* delimiter_change( const char* inStr, char* outStr, char fromChar, char toChar)
{
char *pos;
sprintf(outStr, "%s", inStr);
pos = strchr(outStr, fromChar);
while (pos) {
*pos = toChar;
pos = strchr(pos, fromChar);
}
return outStr;
}
char* vif_ifname_to_sectionname( const char* ifname, char* sectionname )
{
return( delimiter_change( ifname, sectionname, '-', '_' ));
}
char* vif_sectionname_to_ifname( const char* sectionname, char* ifname )
{
return( delimiter_change( sectionname, ifname, '_', '-' ));
}
bool vif_state_to_conf(struct schema_Wifi_VIF_State *vstate,
struct schema_Wifi_VIF_Config *vconf)
{

View File

@@ -20,6 +20,7 @@
#include "vlan.h"
#include "nl80211.h"
#include "utils.h"
#include "phy.h"
#define MODULE_ID LOG_MODULE_ID_VIF
#define UCI_BUFFER_SIZE 80
@@ -339,6 +340,7 @@ bool vif_state_update(struct uci_section *s, struct schema_Wifi_VIF_Config *vcon
struct schema_Wifi_VIF_State vstate;
char mac[ETH_ALEN * 3];
char *ifname, radio[IF_NAMESIZE];
char band[8];
memset(&vstate, 0, sizeof(vstate));
schema_Wifi_VIF_State_mark_all_present(&vstate);
@@ -347,11 +349,16 @@ bool vif_state_update(struct uci_section *s, struct schema_Wifi_VIF_Config *vcon
uci_to_blob(&b, s, &wifi_iface_param);
blobmsg_parse(wifi_iface_policy, __WIF_ATTR_MAX, tb, blob_data(b.head), blob_len(b.head));
if (!tb[WIF_ATTR_DEVICE] || !tb[WIF_ATTR_IFNAME]) {
LOGE("%s: invalid radio/ifname", s->e.name);
if (!tb[WIF_ATTR_DEVICE] || !tb[WIF_ATTR_IFNAME] || !tb[WIF_ATTR_SSID]) {
char name[64];
LOGN("%s: deleting invalid radio/ifname", s->e.name);
snprintf(name, sizeof(name), "wireless.%s",s->e.name);
uci_section_del(uci,"wifi-iface", name);
uci_commit_all(uci);
reload_config = 1;
return false;
}
ifname = target_unmap_ifname(blobmsg_get_string(tb[WIF_ATTR_IFNAME]));
ifname = blobmsg_get_string(tb[WIF_ATTR_IFNAME]);
strncpy(radio, blobmsg_get_string(tb[WIF_ATTR_DEVICE]), IF_NAMESIZE);
vstate._partial_update = true;
@@ -412,7 +419,9 @@ bool vif_state_update(struct uci_section *s, struct schema_Wifi_VIF_Config *vcon
else
LOGW("%s: failed to get SSID", s->e.name);
if (strstr(s->e.name, "50"))
phy_get_band(target_map_ifname(radio), band);
LOGD("Find min_hw_mode: Radio: %s Phy: %s Band: %s", radio, target_map_ifname(radio), band );
if (strstr(band, "5"))
SCHEMA_SET_STR(vstate.min_hw_mode, "11ac");
else
SCHEMA_SET_STR(vstate.min_hw_mode, "11n");
@@ -459,7 +468,7 @@ bool vif_state_update(struct uci_section *s, struct schema_Wifi_VIF_Config *vcon
vif_state_to_conf(&vstate, vconf);
radio_ops->op_vconf(vconf, radio);
}
LOGN("%s: updating vif state", radio);
LOGN("%s: updating vif state %s", radio, vstate.ssid);
radio_ops->op_vstate(&vstate, radio);
return true;
@@ -471,15 +480,31 @@ bool target_vif_config_set2(const struct schema_Wifi_VIF_Config *vconf,
const struct schema_Wifi_VIF_Config_flags *changed,
int num_cconfs)
{
char *ifname = target_map_ifname((char *)vconf->if_name);
char radio_section_name[8]="";
int radio_index;
char vif_section_name[20];
int vid = 0;
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "ifname", vconf->if_name);
sscanf(vconf->if_name, "wlan%d", &radio_index);
snprintf(radio_section_name, sizeof(radio_section_name), "radio%d", radio_index);
blobmsg_add_string(&b, "device", radio_section_name);
blobmsg_add_string(&b, "mode", "ap");
if (changed->enabled && vconf->enabled)
blobmsg_add_bool(&b, "disabled", 0);
else
blobmsg_add_bool(&b, "disabled", 1);
else {
char uci_name[40];
if (vid) {
vlan_del((char *)vconf->if_name);
}
vif_ifname_to_sectionname(vconf->if_name, vif_section_name);
snprintf(uci_name, sizeof(uci_name),"wireless.%s", vif_section_name);
uci_section_del(uci, "wifi-iface", uci_name);
}
if (changed->ssid)
blobmsg_add_string(&b, "ssid", vconf->ssid);
@@ -549,13 +574,14 @@ bool target_vif_config_set2(const struct schema_Wifi_VIF_Config *vconf,
if (changed->custom_options)
vif_config_custom_opt_set(&b, vconf);
blob_to_uci_section(uci, "wireless", ifname, "wifi-iface",
vif_ifname_to_sectionname(vconf->if_name, vif_section_name);
blob_to_uci_section(uci, "wireless", vif_section_name, "wifi-iface",
b.head, &wifi_iface_param);
if (vid)
vlan_add(ifname, vconf->vlan_id, vid);
vlan_add((char *)vconf->if_name, vconf->vlan_id, vid);
else
vlan_del(ifname);
vlan_del((char *)vconf->if_name);
uci_commit_all(uci);
reload_config = 1;

View File

@@ -35,10 +35,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define TARGET_OVSDB_SOCK_PATH "/var/run/openvswitch/db.sock"
#define TARGET_LOGREAD_FILENAME "messages"
void target_ifname_map_init();
bool target_map_cloud_to_iw(const char *ifname, char *iw_name, size_t length);
bool target_map_cloud_to_phy(const char *ifname, char *phy_name, size_t length);
typedef struct
{
DPP_TARGET_CLIENT_RECORD_COMMON_STRUCT;

View File

@@ -39,72 +39,6 @@ static char devInfoSerialNumber[DEV_INFO_RECORD_SZ];
static bool devInfoModelNumber_saved = false;
static bool devInfoSerialNumber_saved = false;
typedef struct
{
char *cloud_ifname;
char *iw_ifname;
char *iw_phyname;
} ifmap_t;
ifmap_t stats_ifmap[] = {
{ "home-ap-24", "wlan1", "phy1" },
{ "home-ap-u50", "wlan0", "phy0" },
{ NULL, NULL, NULL }
};
void target_ifname_map_init()
{
target_map_init();
//Radio mappings
target_map_insert("wifi0", "radio1");
target_map_insert("wifi1", "radio2");
target_map_insert("wifi2", "radio0");
//VIF mappings
target_map_insert("home-ap-u50", "default_radio0");
target_map_insert("home-ap-24", "default_radio1");
target_map_insert("home-ap-l50", "default_radio2");
}
bool target_map_cloud_to_iw(const char *ifname, char *iw_name, size_t length)
{
ifmap_t *mp;
mp = stats_ifmap;
while (mp->cloud_ifname)
{
if (!strcmp(mp->cloud_ifname, ifname))
{
strscpy(iw_name, mp->iw_ifname, length);
return true;
}
mp++;
}
return false;
}
bool target_map_cloud_to_phy(const char *ifname, char *phy_name, size_t length)
{
ifmap_t *mp;
mp = stats_ifmap;
while (mp->cloud_ifname)
{
if (!strcmp(mp->cloud_ifname, ifname))
{
strscpy(phy_name, mp->iw_phyname, length);
return true;
}
mp++;
}
return false;
}
char *get_devinfo_record( char * tag, char * payload, size_t payloadsz )
{
FILE *devInfoFn = NULL;

View File

@@ -35,8 +35,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define TARGET_OVSDB_SOCK_PATH "/var/run/openvswitch/db.sock"
#define TARGET_LOGREAD_FILENAME "messages"
void target_ifname_map_init();
typedef struct
{
DPP_TARGET_CLIENT_RECORD_COMMON_STRUCT;

View File

@@ -1,40 +0,0 @@
From bc7466b28af94327fa01ab9f2c375696f17f3f33 Mon Sep 17 00:00:00 2001
From: John Crispin <john@phrozen.org>
Date: Wed, 1 Jul 2020 11:47:40 +0200
Subject: [PATCH] base-files: add support for wifi config hooks
This new feature allows adding hooks that will be called after a new wireless
config is added to uci.
Signed-off-by: John Crispin <john@phrozen.org>
---
package/base-files/files/sbin/wifi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/package/base-files/files/sbin/wifi b/package/base-files/files/sbin/wifi
index f7a10de215..8e644cf1a2 100755
--- a/package/base-files/files/sbin/wifi
+++ b/package/base-files/files/sbin/wifi
@@ -161,6 +161,11 @@ wifi_config() {
echo "$driver: Hardware detection not supported" >&2
fi
); done
+ for hook in $HOOKS; do (
+ if eval "type run_$hook" 2>/dev/null >/dev/null; then
+ eval "run_$hook" || echo "$hook: Hook failed" >&2
+ fi
+ ); done
}
start_net() {(
@@ -230,6 +235,7 @@ scan_wifi() {
DEVICES=
DRIVERS=
+HOOKS=
include /lib/wifi
scan_wifi
--
2.25.1