chenhaiq / mt7610u_wifi_sta_v3002_dpo_20130916

Modified usb wifi driver for TP_link TL-WDN5200
MIT License
132 stars 116 forks source link

Stopped working with NetworkManager since kernel 4.4.74 #30

Open PVince81 opened 7 years ago

PVince81 commented 7 years ago

See more detailed report here: https://bugzilla.suse.com/show_bug.cgi?id=1050959

Basically the driver stopped working with NetworkManager since kernel 4.4.75. It still works if I boot kernel 4.4.62.

And also wpa_supplicant itself works, and if wpa_supplicant runs before NetworkManager is started, then NetworkManager works too. It seems that the driver/device needs to be set into a specific state for NetworkManager to work.

Not sure if it's an issue with the driver which needs adjusting for newer kernel or just a but in NetworkManager.

This is a diff of the NetworkManager log. Left is when it fails when run alone. Right is when wpa_supplicant was running.

< <warn>  (ra0): error 100 getting card mode
---
> <info>  (ra0): new 802.11 WiFi device (carrier: UNKNOWN, driver: 'usb', ifindex: 4)
> <info>  (ra0): device state change: unmanaged -> unavailable (reason 'managed') [10 20 2]

So maybe something wrong with the driver function that retrieves the card mode with newer kernels ?

@chenhaiq

PVince81 commented 7 years ago

I discovered some more clues: it seems to be related to the driver's "Mode" which is always on "Auto" at the beginning. This seems to disturb NetworkManager.

When running wpa_supplicant, it will somehow switch the mode to "Managed" internally.

I tried setting the mode to "Managed" manually but "iwconfig ra0 mode Managed" doesn't switch it.

Then I patched the code to lie about the mode in giwmode:

diff --git a/os/linux/sta_ioctl.c b/os/linux/sta_ioctl.c
index 50afb88..7d4165f 100644
--- a/os/linux/sta_ioctl.c
+++ b/os/linux/sta_ioctl.c
@@ -284,7 +284,8 @@ int rt_ioctl_giwmode(struct net_device *dev,
                *mode = IW_MODE_MONITOR;
 #endif /* LINUX_VERSION_CODE */
        else
-               *mode = IW_MODE_AUTO;
+               //*mode = IW_MODE_AUTO;
+               *mode = IW_MODE_INFRA;

        DBGPRINT(RT_DEBUG_TRACE, ("==>rt_ioctl_giwmode(mode=%d)\n", *mode));
        return 0;

At this point, the following makes networkmanager work:

# modprobe mt7650u_sta
# ifconfig ra0 up   # for some reason it needs to be up to be picked up
# systemctl restart network   # force restart of network manager

At this state the connection works.

PVince81 commented 7 years ago

I suspect that there are three potential bugs involved:

1) Setting mode doesn't work 2) Returned mode is "Auto" instead of "Managed" by default ? Either driver bug or NetworkManager bug: https://bugzilla.opensuse.org/show_bug.cgi?id=1050959 3) Driver reports "network is down" by default. It looks like it's not supposed to and should be up but unconnected by default. Could be a requirement/behavior from the newer kernels.

PVince81 commented 7 years ago

Okay, first I tried to write a udev rule to automatically run ifconfig ra0 up and restart network managed, but it didn't work at boot time.

Then I noticed in the network manager log that when it starts it does find ra0 but fails with "error 100 getting card mode".

I have the feeling that since Kernel 4.4.75+ something has changed in the order how network device APIs are called.

Here is the fix that works for me: return "Managed" even if the "network is down":

diff --git a/os/linux/sta_ioctl.c b/os/linux/sta_ioctl.c
index 50afb88..57bc046 100644
--- a/os/linux/sta_ioctl.c
+++ b/os/linux/sta_ioctl.c
@@ -268,7 +268,10 @@ int rt_ioctl_giwmode(struct net_device *dev,
        if (RTMP_DRIVER_IOCTL_SANITY_CHECK(pAd, NULL) != NDIS_STATUS_SUCCESS)
     {
         DBGPRINT(RT_DEBUG_TRACE, ("INFO::Network is down!\n"));
-        return -ENETDOWN;   
+               // lie
+               *mode = IW_MODE_INFRA;
+               return 0;
+        //return -ENETDOWN;   
     }

@@ -284,7 +287,8 @@ int rt_ioctl_giwmode(struct net_device *dev,
                *mode = IW_MODE_MONITOR;
 #endif /* LINUX_VERSION_CODE */
        else
-               *mode = IW_MODE_AUTO;
+               //*mode = IW_MODE_AUTO;
+               *mode = IW_MODE_INFRA;

        DBGPRINT(RT_DEBUG_TRACE, ("==>rt_ioctl_giwmode(mode=%d)\n", *mode));
        return 0;

This patch applied onto master makes the wifi stick work correctly for me.

PVince81 commented 7 years ago

and I narrowed it down and removed the second hack.

So this is the minimal patch required to make the wifi stick work again for me:

diff --git a/os/linux/sta_ioctl.c b/os/linux/sta_ioctl.c
index 50afb88..18f7eca 100644
--- a/os/linux/sta_ioctl.c
+++ b/os/linux/sta_ioctl.c
@@ -268,7 +268,10 @@ int rt_ioctl_giwmode(struct net_device *dev,
        if (RTMP_DRIVER_IOCTL_SANITY_CHECK(pAd, NULL) != NDIS_STATUS_SUCCESS)
     {
         DBGPRINT(RT_DEBUG_TRACE, ("INFO::Network is down!\n"));
-        return -ENETDOWN;   
+               // callers like NetworkManager expect to be able to read a valid mode
+               // even when down
+        *mode = IW_MODE_INFRA;
+        return 0;
     }

@chenhaiq if interested I can submit this as PR, if the approach is valid.

chenhaiq commented 7 years ago

Please submit it as PR