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!