From f4555b51b778ae5e677ce63eccdfd9376d07dd5d Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Sun, 16 Feb 2020 12:40:02 +0300 Subject: Wireless: support NL80211 userspace <-> kernelspace API NL80211 was introduced in Linux 2.6.24 in 2007 as a new extensible universal API, replacing "wireless extensions" ioctls. It works on top of netlink, and allows direct communication to cfg80211 kernel subsystem. Since then it became a hard requirement for all upstream wireless drivers to hook into cfg80211 (SoftMAC drivers do it via the common mac80211 layer). There's still additional compatibility code that allows limited Wext functionality for cfg80211 drivers but it's buggy and can be disabled altogether when CONFIG_CFG80211_WEXT is not set. This patch makes use of "netlink" Haskell library which doesn't have any additional runtime dependencies (so neither iwlib nor libnl are required). The operation is the same as performed by "iw dev link" command. The signal level is transformed to "quality" by first clamping it to [-110; -40], then adding 110 and dividing by 70 (same meaningless formula as used by the cfg80211 Wext compatibility layer). "essid" template argument is replaced by more appropriate "ssid" (with the old variant still available for backwards compatibility) --- src/Xmobar/Plugins/Monitors.hs | 8 ++-- src/Xmobar/Plugins/Monitors/Wireless.hs | 73 +++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/Xmobar/Plugins/Monitors.hs b/src/Xmobar/Plugins/Monitors.hs index d6fc0a0..322d401 100644 --- a/src/Xmobar/Plugins/Monitors.hs +++ b/src/Xmobar/Plugins/Monitors.hs @@ -42,7 +42,7 @@ import Xmobar.Plugins.Monitors.CatInt #ifdef UVMETER import Xmobar.Plugins.Monitors.UVMeter #endif -#ifdef IWLIB +#if defined IWLIB || defined USE_NL80211 import Xmobar.Plugins.Monitors.Wireless #endif #ifdef LIBMPD @@ -85,7 +85,7 @@ data Monitors = Network Interface Args Rate #ifdef UVMETER | UVMeter Station Args Rate #endif -#ifdef IWLIB +#if defined IWLIB || defined USE_NL80211 | Wireless Interface Args Rate #endif #ifdef LIBMPD @@ -142,7 +142,7 @@ instance Exec Monitors where #ifdef UVMETER alias (UVMeter s _ _) = "uv " ++ s #endif -#ifdef IWLIB +#if defined IWLIB || defined USE_NL80211 alias (Wireless i _ _) = i ++ "wi" #endif #ifdef LIBMPD @@ -186,7 +186,7 @@ instance Exec Monitors where #ifdef UVMETER start (UVMeter s a r) = startUVMeter s a r #endif -#ifdef IWLIB +#if defined IWLIB || defined USE_NL80211 start (Wireless i a r) = runM a wirelessConfig (runWireless i) r #endif #ifdef LIBMPD diff --git a/src/Xmobar/Plugins/Monitors/Wireless.hs b/src/Xmobar/Plugins/Monitors/Wireless.hs index f2b1e6a..28aba50 100644 --- a/src/Xmobar/Plugins/Monitors/Wireless.hs +++ b/src/Xmobar/Plugins/Monitors/Wireless.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE TypeApplications, CPP #-} ----------------------------------------------------------------------------- -- | -- Module : Plugins.Monitors.Wireless @@ -8,16 +9,68 @@ -- Stability : unstable -- Portability : unportable -- --- A monitor reporting ESSID and link quality for wireless interfaces +-- A monitor reporting SSID and signal level for wireless interfaces -- ----------------------------------------------------------------------------- module Xmobar.Plugins.Monitors.Wireless (wirelessConfig, runWireless) where import System.Console.GetOpt +import Data.Maybe (fromMaybe) import Xmobar.Plugins.Monitors.Common + +#ifdef IWLIB import Network.IWlib +#elif defined USE_NL80211 +import Control.Exception (bracket) +import qualified Data.Map as M +import GHC.Int (Int8) +import Data.Maybe (listToMaybe) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) +import Data.ByteString.Char8 (unpack) +import Data.Serialize.Put (runPut, putWord32host, putByteString) +import Data.Serialize.Get (runGet) + +import System.Linux.Netlink hiding (query) +import System.Linux.Netlink.GeNetlink.NL80211 +import System.Linux.Netlink.GeNetlink.NL80211.StaInfo +import System.Linux.Netlink.GeNetlink.NL80211.Constants +import System.Posix.IO (closeFd) + +data IwData = IwData { wiEssid :: String, wiSignal :: Maybe Int, wiQuality :: Int } + +getWirelessInfo :: String -> IO IwData +getWirelessInfo ifname = do + bracket makeNL80211Socket (closeFd . getFd) (\s -> do + iflist <- getInterfaceList s + iwdata <- runMaybeT $ do + ifidx <- MaybeT . return $ foldr (\(n, i) z -> + if (ifname == "" || ifname == n) then Just i else z) + Nothing + iflist + scanp <- liftIO (getConnectedWifi s ifidx) >>= + MaybeT . return . listToMaybe + bssid <- MaybeT . return $ M.lookup eNL80211_ATTR_BSS (packetAttributes scanp) >>= + rightToMaybe . runGet getAttributes >>= + M.lookup eNL80211_BSS_BSSID + stap <- liftIO (query s eNL80211_CMD_GET_STATION True $ M.fromList + [(eNL80211_ATTR_IFINDEX, runPut $ putWord32host ifidx), + (eNL80211_ATTR_MAC, runPut $ putByteString bssid)]) >>= + MaybeT . return . listToMaybe + let ssid = fromMaybe "" $ getWifiAttributes scanp >>= M.lookup eWLAN_EID_SSID >>= + return . unpack + signal = staInfoFromPacket stap >>= staSignalMBM >>= + return . fromIntegral @Int8 . fromIntegral + qlty = fromMaybe (-1) (round @Float . (/ 0.7) . (+ 110) . + clamp (-110) (-40) . fromIntegral <$> signal) + MaybeT . return $ Just $ IwData ssid signal qlty + return $ fromMaybe (IwData "" Nothing (-1)) iwdata) + where + rightToMaybe = either (const Nothing) Just + clamp lb up v = if v < lb then lb else if v > up then up else v +#endif newtype WirelessOpts = WirelessOpts { qualityIconPattern :: Maybe IconPattern @@ -36,29 +89,41 @@ options = wirelessConfig :: IO MConfig wirelessConfig = - mkMConfig " " - ["essid", "quality", "qualitybar", "qualityvbar", "qualityipat"] + mkMConfig " " + ["ssid", "essid", "signal", "quality", "qualitybar", "qualityvbar", "qualityipat"] runWireless :: String -> [String] -> Monitor String runWireless iface args = do opts <- io $ parseOptsWith options defaultOpts args +#ifdef IWLIB iface' <- if "" == iface then io findInterface else return iface +#else + let iface' = iface +#endif wi <- io $ getWirelessInfo iface' na <- getConfigValue naString let essid = wiEssid wi qlty = fromIntegral $ wiQuality wi e = if essid == "" then na else essid ep <- showWithPadding e +#ifdef USE_NL80211 + let s = wiSignal wi +#else + let s = if qlty >= 0 then Just (qlty * 0.7 - 110) else Nothing +#endif + sp <- showWithPadding $ fromMaybe "" (show <$> s) q <- if qlty >= 0 then showPercentWithColors (qlty / 100) else showWithPadding "" qb <- showPercentBar qlty (qlty / 100) qvb <- showVerticalBar qlty (qlty / 100) qipat <- showIconPattern (qualityIconPattern opts) (qlty / 100) - parseTemplate [ep, q, qb, qvb, qipat] + parseTemplate [ep, ep, sp, q, qb, qvb, qipat] +#ifdef IWLIB findInterface :: IO String findInterface = do c <- readFile "/proc/net/wireless" let nds = lines c return $ if length nds > 2 then takeWhile (/= 'c') (nds!!2) else [] +#endif -- cgit v1.2.3