summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorPaul Fertser <fercerpav@gmail.com>2020-02-16 12:40:02 +0300
committerPaul Fertser <fercerpav@gmail.com>2020-02-25 13:07:44 +0300
commitf4555b51b778ae5e677ce63eccdfd9376d07dd5d (patch)
treef7a8f59022efc3396e602c78856958537ca6de4d
parent3f11da6eed40b06044c705db9e3e81fd25abb391 (diff)
downloadxmobar-f4555b51b778ae5e677ce63eccdfd9376d07dd5d.tar.gz
xmobar-f4555b51b778ae5e677ce63eccdfd9376d07dd5d.tar.bz2
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 <devname> 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)
-rw-r--r--changelog.md2
-rw-r--r--readme.md31
-rw-r--r--src/Xmobar/Plugins/Monitors.hs8
-rw-r--r--src/Xmobar/Plugins/Monitors/Wireless.hs73
-rw-r--r--xmobar.cabal14
5 files changed, 106 insertions, 22 deletions
diff --git a/changelog.md b/changelog.md
index a52fce3..93fa31e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -20,6 +20,8 @@ _New features_
- New more efficient time coalescing strategy for monitor updates,
available with the threaded runtime: use the `with_threaded` flag
to enable it (see #410; thanks to Tomáš Janoušek).
+ - `Wireless` supports current nl80211 API on Linux now, old Wext ioctls still
+ available as compile-time option (thanks to Paul Fertser).
## Version 0.32 (December, 2019)
diff --git a/readme.md b/readme.md
index eb14023..7afac44 100644
--- a/readme.md
+++ b/readme.md
@@ -117,11 +117,15 @@ Otherwise, you'll need to install them yourself.
option is needed for the MBox and Mail plugins to work. Requires the
[hinotify] package.
-- `with_iwlib` Support for wireless cards. Enables the Wireless
- plugin. No Haskell library is required, but you will need the
- [iwlib] C library and headers in your system (e.g., install
- `libiw-dev` in Debian-based systems or `wireless_tools` on Arch
- Linux).
+- `with_nl80211` Support for wireless cards on Linux via nl80211 (all
+ upstream drivers). Enables the Wireless plugin. Requires [netlink]
+ and [cereal] packages.
+
+- `with_iwlib` Support for wireless cards via Wext ioctls
+ (deprecated). Enables the Wireless plugin. No Haskell library is
+ required, but you will need the [iwlib] C library and headers in your
+ system (e.g., install `libiw-dev` in Debian-based systems or
+ `wireless_tools` on Arch Linux). Conflicts with `with_nl80211`.
- `with_alsa` Support for ALSA sound cards. Enables the Volume
plugin. Requires the [alsa-mixer] package. To install the latter,
@@ -820,18 +824,21 @@ specification, such as `("clear", "<icon=weather-clear.xbm/>")`.
### `Wireless Interface Args RefreshRate`
-- If set to "", the interface is looked up in /proc/net/wireless.
+- If set to "", first suitable wireless interface is used.
- Aliases to the interface name with the suffix "wi": thus, `Wireless
"wlan0" []` can be used as `%wlan0wi%`, and `Wireless "" []` as `%wi%`.
- Args: default monitor arguments, plus:
- `--quality-icon-pattern`: dynamic string for connection quality in `qualityipat`.
- Variables that can be used with the `-t`/`--template` argument:
- `essid`, `quality`, `qualitybar`, `qualityvbar`, `qualityipat`
-- Thresholds refer to link quality in a `[0, 100]` scale
-- Default template: `<essid> <quality>`
-- Requires the C library [iwlib] (part of the wireless tools suite)
- installed in your system. In addition, to activate this plugin you
- must pass `--flags="with_iwlib"` during compilation
+ `ssid`, `signal`, `quality`, `qualitybar`, `qualityvbar`, `qualityipat`
+- Thresholds refer to link quality on a `[0, 100]` scale. Note that
+ `quality` is calculated from `signal` (in dBm) by a possibly lossy
+ conversion. It is also not taking into account many factors such as
+ noise level, air busy time, transcievers' capabilities and the
+ others which can have drastic impact on the link performance.
+- Default template: `<ssid> <quality>`
+- To activate this plugin you must pass `--flags="with_nl80211"` or
+ `--flags="with_iwlib"` during compilation
### `Memory Args RefreshRate`
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>"
- ["essid", "quality", "qualitybar", "qualityvbar", "qualityipat"]
+ mkMConfig "<ssid> <quality>"
+ ["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
diff --git a/xmobar.cabal b/xmobar.cabal
index f23275f..b29b987 100644
--- a/xmobar.cabal
+++ b/xmobar.cabal
@@ -40,7 +40,11 @@ flag with_inotify
default: False
flag with_iwlib
- description: Wireless info support. Required for the Wireless plugin, needs iwlib installed.
+ description: Wireless info support via Wext ioctls (deprecated). Required for the Wireless plugin, needs iwlib installed.
+ default: False
+
+flag with_nl80211
+ description: Wireless info support via nl80211. Required for the Wireless plugin on systems running Linux, the kernel.
default: False
flag with_mpd
@@ -209,12 +213,18 @@ library
exposed-modules: Xmobar.Plugins.Mail, Xmobar.Plugins.MBox
cpp-options: -DINOTIFY
- if flag(with_iwlib) || flag(all_extensions)
+ if flag(with_iwlib)
extra-libraries: iw
build-depends: iwlib >= 0.1.0 && < 0.2
exposed-modules: Xmobar.Plugins.Monitors.Wireless
cpp-options: -DIWLIB
+ if flag(with_nl80211) || flag(all_extensions)
+ build-depends: netlink >= 1.1.1.0,
+ cereal >= 0.5.8.1
+ exposed-modules: Xmobar.Plugins.Monitors.Wireless
+ cpp-options: -DUSE_NL80211
+
if flag(with_mpd) || flag(all_extensions)
build-depends: libmpd >= 0.9.0.10
exposed-modules: Xmobar.Plugins.Monitors.MPD