Get some progress 🙂
PS: This uci system is so hard to understand, it has executables, libraries and also scripts without any documentation…I guess from beginning its designer never think about portable…
The real call wifi up/down is this from /sbin/wifi: ubus_wifi_cmd “$cmd” “$2”
_wifi_updown() {
for device in ${2:-$DEVICES}; do (
config_get disabled "$device" disabled
[ "$disabled" = "1" ] && {
echo "'$device' is disabled"
set disable
}
config_get iftype "$device" type
if eval "type ${1}_$iftype" 2>/dev/null >/dev/null; then
eval "scan_$iftype '$device'"
eval "${1}_$iftype '$device'" || echo "$device($iftype): ${1} failed"
elif [ ! -f /lib/netifd/wireless/$iftype.sh ]; then
echo "$device($iftype): Interface type not supported"
fi
); done
}
wifi_updown() {
cmd=down
[ enable = "$1" ] && {
_wifi_updown disable "$2"
ubus_wifi_cmd "$cmd" "$2"
scan_wifi
cmd=up
}
ubus_wifi_cmd "$cmd" "$2"
_wifi_updown "$@"
}
Rest code…I do not understand yet, maybe separated setting up?
Now check ubus_wifi_cmd
ubus_wifi_cmd() {
local cmd="$1"
local dev="$2"
json_init
[ -n "$2" ] && json_add_string device "$2"
ubus call network.wireless "$1" "$(json_dump)"
}
Once we run wifi, in this function, it is calling
ubus call network.wireless down { }
ubus call network.wireless up { }
This should be different from old version, because I can not find any function named “mac80211_enable” or “enable_mac80211”, so the script enable way is not working anymore.
What is ubus…?
It is a small application used to transfer data between processes by socket/udp protocol.
It has many protocol, and this protocol is hard coded in the ubus source code by C.
root@OpenWrt:/etc# ubus list
dhcp
dnsmasq
hostapd.wlan0
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.wireless
service
system
You see? network.wireless is one of its compose.
At least ubus I can find some tutorials 🙂
I can run ubus monitor to check what is transfer when I call wifi down.
-> 070d3ffa #00000003 status: {"status":0}
-> 3aa4a160 #3aa4a160 hello: {}
<- 3aa4a160 #00000000 lookup: {"objpath":"network.wireless"}
-> 3aa4a160 #00000000 data: {"objpath":"network.wireless","objid":-946844025,"objtype":976669475,"signature":{"up":{},"down":{},"status":{},"notify":{},"get_validate":{}}}
-> 3aa4a160 #00000000 status: {"status":0}
<- 3aa4a160 #c7904e87 invoke: {"objid":-946844025,"method":"down","data":{}}
-> 7d12fdb8 #3aa4a160 invoke: {"objid":-946844025,"method":"down","data":{},"user":"root","group":"root"}
<- 7d12fdb8 #3aa4a160 status: {"status":0,"objid":-946844025}
-> 3aa4a160 #c7904e87 status: {"status":0,"objid":-946844025}
Emm, still no clue where it goes…
Have to search the source code, key word “network.wireless”
Catch it! I get something at “netifd-2018-04-03-xxxx”. So I guess network.wireless is registered in this netifd/ubus.c
static struct ubus_object wireless_object = {
.name = "network.wireless",
.type = &wireless_object_type,
.methods = wireless_object_methods,
.n_methods = ARRAY_SIZE(wireless_object_methods),
};
This interface looks much better, I can guess with its name “wireless_object_methods” must contains the feature to control wireless up and down.
I am right 🙂 In same ubus.c I find its define.
static struct ubus_method wireless_object_methods[] = {
{ .name = "up", .handler = netifd_handle_wdev_up },
{ .name = "down", .handler = netifd_handle_wdev_down },
{ .name = "status", .handler = netifd_handle_wdev_status },
{ .name = "notify", .handler = netifd_handle_wdev_notify },
{ .name = "get_validate", .handler = netifd_handle_wdev_get_validate },
};
Now let’s check what is netifd_handle_wdev_up, in same file too.
static int
netifd_handle_wdev_up(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wireless_device *wdev;
int ret;
wdev = get_wdev(msg, &ret);
if (ret == UBUS_STATUS_NOT_FOUND)
return ret;
if (wdev) {
wireless_device_set_up(wdev);
} else {
vlist_for_each_element(&wireless_devices, wdev, node)
wireless_device_set_up(wdev);
}
return 0;
}
static int
netifd_handle_wdev_down(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wireless_device *wdev;
int ret;
wdev = get_wdev(msg, &ret);
if (ret == UBUS_STATUS_NOT_FOUND)
return ret;
if (wdev) {
wireless_device_set_down(wdev);
} else {
vlist_for_each_element(&wireless_devices, wdev, node)
wireless_device_set_down(wdev);
}
return 0;
}
They are all calling wireless_device_set_up/wireless_device_set_down, in wireless.c
static void
__wireless_device_set_up(struct wireless_device *wdev)
{
if (wdev->disabled)
return;
if (wdev->retry_setup_failed)
return;
if (!wdev->autostart)
return;
if (wdev->state != IFS_DOWN || config_init)
return;
free(wdev->prev_config);
wdev->prev_config = NULL;
wdev->state = IFS_SETUP;
wireless_device_run_handler(wdev, true);
}
check wireless_device_run_handler, same file
static void
wireless_device_run_handler(struct wireless_device *wdev, bool up)
{
const char *action = up ? "setup" : "teardown";
const char *argv[6];
char *config;
int i = 0;
int fds[2] = { -1, -1 };
D(WIRELESS, "Wireless device '%s' run %s handler\n", wdev->name, action);
if (!up && wdev->prev_config) {
config = blobmsg_format_json(wdev->prev_config, true);
free(wdev->prev_config);
wdev->prev_config = NULL;
} else {
prepare_config(wdev, &b, up);
config = blobmsg_format_json(b.head, true);
}
argv[i++] = wdev->drv->script;
argv[i++] = wdev->drv->name;
argv[i++] = action;
argv[i++] = wdev->name;
argv[i++] = config;
argv[i] = NULL;
if (up && pipe(fds) == 0) {
wdev->script_proc_fd.fd = fds[0];
uloop_fd_add(&wdev->script_proc_fd,
ULOOP_READ | ULOOP_EDGE_TRIGGER);
}
netifd_start_process(argv, NULL, &wdev->script_task);
if (fds[1] >= 0)
close(fds[1]);
free(config);
}
Haha, I guess it is running script here.
What is in argv… I do not know, but should be easy, let’s add log to the file.
main.c:netifd_start_process
...
if (pfds[1] > 2)
close(pfds[1]);
+{
+ FILE *fp = fopen("/tmp/netifd.log", "a+");
+ int id = 0;
+ for (; argv[id]; id++)
+ fprintf(fp, "%d: %s\n", id, argv[id]);
+ fclose(fp);
+}
execvp(argv[0], (char **) argv);
...
make package/network/config/netifd
Get this result:
0: ./mac80211.sh
1: mac80211
2: setup
3: radio0
4: {"config":{"channel":"11","hwmode":"11g","path":"platform\/10300000.wmac","htmode":"HT20","disabled":false},"interfaces":{"0":{"bridge":"br-lan","config":{"mode":"ap","ssid":"OpenWrt","encryption":"none","network":["lan"],"mode":"ap"}}}}
The parameters are send to netifd-wireless.sh:init_wireless_driver
name = mac80211
cmd = setup
interface = radio0
data = {…}
init_wireless_driver() {
name="$1"; shift
cmd="$1"; shift
case "$cmd" in
dump)
add_driver() {
eval "drv_$1_cleanup"
json_init
json_add_string name "$1"
json_add_array device
_wdev_common_device_config
eval "drv_$1_init_device_config"
json_close_array
json_add_array iface
_wdev_common_iface_config
eval "drv_$1_init_iface_config"
json_close_array
json_dump
}
;;
setup|teardown)
interface="$1"; shift
data="$1"; shift
export __netifd_device="$interface"
add_driver() {
[[ "$name" == "$1" ]] || return 0
_wdev_handler "$1" "$cmd"
}
;;
esac
}
Setup will call: _wdev_handler mac80211 setup
_wdev_handler() {
json_load "$data"
json_select config
_wdev_prepare_channel
json_select ..
eval "drv_$1_$2 \"$interface\""
}
that is drv_mac80211_setup, this function is in /lib/netifd/wireless/mac80211.sh, from my test, it will only run once at startup wifi.
Now everything is clear, time to move on merge the driver!