Daily Archives: 2018-05-05

VoCore2: Port mt7628.ko to LuCI 4

now create simple test script in /lib/netifd/wireless/mt7628.sh

#!/bin/sh
. /lib/netifd/netifd-wireless.sh
. /lib/netifd/hostapd.sh

init_wireless_driver "$@"

drv_mt7628_setup() {
	echo drv_mt7628_setup >> /tmp/wifi.log
}

drv_mt7628_teardown() {
	echo drv_mt7628_teardown >> /tmp/wifi.log
}

drv_mt7628_cleanup() {
        hostapd_common_cleanup
}

drv_mt7628_init_device_config() {
	echo drv_mt7628_init_device_config >> /tmp/wifi.log
}

drv_mt7628_init_iface_config() {
	echo drv_mt7628_init_iface_config >> /tmp/wifi.log
}

add_driver mt7628

other setting file in /etc/config/wireless

config wifi-device 'ra0'
	option type 'mt7628'
	option channel '11'
	option hwmode '11g'
	option path 'platform/10300000.wmac'
	option htmode 'HT20'
	option disabled '0'

config wifi-iface 'default-ap'
	option device 'ra0'
	option network 'lan'
	option mode 'ap'
	option ssid 'VoCore2'
	option encryption 'none'

mt7628 can be replace to others, such as mac80211 etc…this must be same in the file name, the function name and in setting file option type.

reboot and check /tmp/wifi.log

drv_mt7628_init_device_config
drv_mt7628_init_iface_config
drv_mt7628_setup
drv_mt7628_teardown
drv_mt7628_setup
drv_mt7628_teardown
drv_mt7628_setup
drv_mt7628_teardown
drv_mt7628_setup
drv_mt7628_teardown

It is almost done!

VoCore2: Port mt7628.ko to LuCI 3

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!