v1.1.0: Complete kernel modules, fix WebUI bugs
Kernel modules fully implemented for kernel 6.6/Tensor G5: - rc_wifi_mon: kprobes kallsyms, bcmdhd iovar monitor/promisc/allmulti, sysfs status at /sys/kernel/rc_wifi_mon/, clean unpatch on unload - rc_shannon_cmd: ioctl interface (AT_CMD, GET_URC, SET_TIMEOUT, GET_STATUS, FLUSH), URC ring buffer (64 entries), modem probe on init - rc_diag_bridge: HDLC decode with CRC-16 validation, FTM ioctl, EFS read/write/stat/unlink, version query, subsystem dispatch - rc_ioctl.h: shared userspace header for all ioctl definitions - All modules handle class_create() API change in kernel 6.4+ WebUI fixes: - Fix malformed WiFi firmware JSON output - Add vonr/vt/apn/nradv to carrier config read endpoint - Fix carrier toggle state loading in frontend - Fix redundant replace in kmod toggle logic Makefile: single-module build (MOD=), make package target uninstall.sh: unload kernel modules before cleanup
This commit is contained in:
@@ -9,19 +9,19 @@
|
||||
*
|
||||
* Supported drivers:
|
||||
* - Samsung SCSC/SLSI (scsc_wlan)
|
||||
* - Broadcom bcmdhd / DHD
|
||||
* - Qualcomm cnss2 / ath11k / ath12k (usually already has
|
||||
* monitor, but vendor builds may disable it)
|
||||
* - Broadcom bcmdhd / DHD (BCM4390 etc)
|
||||
* - Qualcomm cnss2 / ath11k / ath12k
|
||||
*
|
||||
* Approach:
|
||||
* 1. Locate the WiFi driver's registered wiphy via cfg80211
|
||||
* 2. Find the cfg80211_ops function table
|
||||
* 3. Patch change_virtual_intf to accept monitor mode
|
||||
* 4. Update wiphy->interface_modes bitmask
|
||||
* 5. For SCSC: also hook the firmware command path to send
|
||||
* the MIB key that enables RF monitor in Maxwell firmware
|
||||
* 5. Driver-specific: send iovars/MIBs to firmware
|
||||
*
|
||||
* This is a live kernel patch — no reboot required after insmod.
|
||||
*
|
||||
* Target: Pixel 10 Pro Fold (rango), Tensor G5, BCM4390, kernel 6.6
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@@ -30,8 +30,11 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -39,7 +42,32 @@ MODULE_AUTHOR("RadioControl");
|
||||
MODULE_DESCRIPTION("Runtime WiFi monitor/injection mode enabler");
|
||||
MODULE_VERSION("1.0");
|
||||
|
||||
/* Which driver we detected */
|
||||
/* ---- kallsyms_lookup_name workaround for kernel >= 5.7 ---- */
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
|
||||
static unsigned long rc_kallsyms_lookup(const char *name)
|
||||
{
|
||||
struct kprobe kp = { .symbol_name = name };
|
||||
unsigned long addr;
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&kp);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
addr = (unsigned long)kp.addr;
|
||||
unregister_kprobe(&kp);
|
||||
return addr;
|
||||
}
|
||||
#else
|
||||
#include <linux/kallsyms.h>
|
||||
static unsigned long rc_kallsyms_lookup(const char *name)
|
||||
{
|
||||
return kallsyms_lookup_name(name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ---- Driver type detection ---- */
|
||||
|
||||
enum wifi_driver_type {
|
||||
DRIVER_UNKNOWN = 0,
|
||||
DRIVER_SCSC,
|
||||
@@ -49,9 +77,15 @@ enum wifi_driver_type {
|
||||
DRIVER_CNSS,
|
||||
};
|
||||
|
||||
static const char *driver_names[] = {
|
||||
"unknown", "scsc", "bcmdhd", "ath11k", "ath12k", "cnss"
|
||||
};
|
||||
|
||||
static enum wifi_driver_type detected_driver = DRIVER_UNKNOWN;
|
||||
static struct wiphy *target_wiphy;
|
||||
static struct cfg80211_ops *target_ops;
|
||||
static const struct cfg80211_ops *target_ops;
|
||||
static struct net_device *target_netdev;
|
||||
static u32 orig_interface_modes;
|
||||
|
||||
/* Original function pointer we're replacing */
|
||||
static int (*orig_change_virtual_intf)(struct wiphy *wiphy,
|
||||
@@ -59,6 +93,156 @@ static int (*orig_change_virtual_intf)(struct wiphy *wiphy,
|
||||
enum nl80211_iftype type,
|
||||
struct vif_params *params);
|
||||
|
||||
/* ---- sysfs status interface ---- */
|
||||
|
||||
static struct kobject *rc_kobj;
|
||||
|
||||
static ssize_t status_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sysfs_emit(buf,
|
||||
"driver=%s\n"
|
||||
"wiphy=%s\n"
|
||||
"netdev=%s\n"
|
||||
"monitor_supported=%d\n"
|
||||
"interface_modes=0x%x\n",
|
||||
driver_names[detected_driver],
|
||||
target_wiphy ? wiphy_name(target_wiphy) : "none",
|
||||
target_netdev ? target_netdev->name : "none",
|
||||
target_wiphy ? !!(target_wiphy->interface_modes &
|
||||
BIT(NL80211_IFTYPE_MONITOR)) : 0,
|
||||
target_wiphy ? target_wiphy->interface_modes : 0);
|
||||
}
|
||||
static struct kobj_attribute status_attr = __ATTR_RO(status);
|
||||
|
||||
static ssize_t driver_show(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%s\n", driver_names[detected_driver]);
|
||||
}
|
||||
static struct kobj_attribute driver_attr = __ATTR_RO(driver);
|
||||
|
||||
static struct attribute *rc_attrs[] = {
|
||||
&status_attr.attr,
|
||||
&driver_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
static struct attribute_group rc_attr_group = {
|
||||
.attrs = rc_attrs,
|
||||
};
|
||||
|
||||
/* ---- bcmdhd driver-level monitor mode ---- */
|
||||
|
||||
/*
|
||||
* BCM4390 uses the bcmdhd4390 driver. Monitor mode requires:
|
||||
* 1. cfg80211 patching (we do above) so nl80211 accepts the type
|
||||
* 2. DHD driver internal flag set via dhd_set_monitor()
|
||||
* 3. Firmware iovar "monitor" set to 1
|
||||
* 4. Firmware iovar "promisc" set to 1
|
||||
*
|
||||
* We locate dhd_net_if_lock/unlock and the iovar path via kallsyms.
|
||||
* For full injection, Nexmon firmware patches are also required
|
||||
* (separate from this module).
|
||||
*/
|
||||
|
||||
/* DHD iovar interface — we call dhd_iovar through the netdev private data */
|
||||
typedef int (*dhd_ioctl_fn)(struct net_device *dev, struct ifreq *ifr, int cmd);
|
||||
|
||||
/*
|
||||
* Send a bcmdhd private iovar via SIOCDEVPRIVATE.
|
||||
* The DHD driver exposes iovars through wl_android_priv_cmd or
|
||||
* through the standard SIOCDEVPRIVATE ioctl path.
|
||||
*
|
||||
* For monitor mode we need:
|
||||
* wl monitor 1 (enable monitor)
|
||||
* wl promisc 1 (promiscuous)
|
||||
* wl allmulti 1 (all multicast)
|
||||
*/
|
||||
static int bcmdhd_set_iovar_int(const char *iovar, int val)
|
||||
{
|
||||
typedef int (*wldev_iovar_setint_fn)(struct net_device *dev,
|
||||
const char *iovar, int val);
|
||||
wldev_iovar_setint_fn set_fn;
|
||||
|
||||
set_fn = (wldev_iovar_setint_fn)rc_kallsyms_lookup("wldev_iovar_setint");
|
||||
if (!set_fn) {
|
||||
pr_debug("rc_wifi_mon: wldev_iovar_setint not found\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
if (!target_netdev) {
|
||||
pr_err("rc_wifi_mon: no target netdev for iovar\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return set_fn(target_netdev, iovar, val);
|
||||
}
|
||||
|
||||
static int bcmdhd_enable_monitor(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bcmdhd_set_iovar_int("monitor", 1);
|
||||
if (ret) {
|
||||
pr_warn("rc_wifi_mon: bcmdhd 'monitor' iovar failed: %d "
|
||||
"(may need Nexmon firmware)\n", ret);
|
||||
/* Non-fatal — cfg80211 patching still works for some captures */
|
||||
} else {
|
||||
pr_info("rc_wifi_mon: bcmdhd firmware monitor mode enabled\n");
|
||||
}
|
||||
|
||||
bcmdhd_set_iovar_int("promisc", 1);
|
||||
bcmdhd_set_iovar_int("allmulti", 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bcmdhd_disable_monitor(void)
|
||||
{
|
||||
bcmdhd_set_iovar_int("monitor", 0);
|
||||
bcmdhd_set_iovar_int("promisc", 0);
|
||||
bcmdhd_set_iovar_int("allmulti", 0);
|
||||
}
|
||||
|
||||
/* ---- SCSC/SLSI firmware monitor mode ---- */
|
||||
|
||||
/*
|
||||
* The Samsung SLSI firmware uses MIB OIDs to control behavior.
|
||||
* We locate slsi_mlme_set() and write the monitor-enable MIB.
|
||||
*
|
||||
* Key MIBs for SCSC monitor mode:
|
||||
* unifiMonitorModeEnabled = 0x09001E (OID)
|
||||
* unifiRxDataRate = 0x090020
|
||||
* unifiFrameRxCounters = 0x090021
|
||||
*
|
||||
* slsi_mlme_set signature:
|
||||
* int slsi_mlme_set(struct slsi_dev *sdev, struct net_device *dev,
|
||||
* u8 *mib, int mib_len);
|
||||
*
|
||||
* The MIB is TLV encoded: [OID 2B] [Length 2B] [Value...]
|
||||
*/
|
||||
|
||||
static void scsc_enable_fw_monitor(void)
|
||||
{
|
||||
typedef int (*slsi_mlme_set_fn)(void *sdev, struct net_device *dev,
|
||||
u8 *mib, int mib_len);
|
||||
slsi_mlme_set_fn mlme_set;
|
||||
|
||||
mlme_set = (slsi_mlme_set_fn)rc_kallsyms_lookup("slsi_mlme_set");
|
||||
if (!mlme_set) {
|
||||
pr_info("rc_wifi_mon: slsi_mlme_set not found — "
|
||||
"SCSC FW monitor mode MIB not set\n");
|
||||
pr_info("rc_wifi_mon: monitor mode will work at driver level "
|
||||
"but FW may filter some frames\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: found slsi_mlme_set, SCSC FW patching "
|
||||
"available (MIB write deferred to mode switch)\n");
|
||||
}
|
||||
|
||||
/* ---- cfg80211 ops patching ---- */
|
||||
|
||||
/*
|
||||
* Our replacement change_virtual_intf that accepts monitor mode.
|
||||
* Falls through to the original handler for non-monitor types.
|
||||
@@ -68,28 +252,34 @@ static int rc_change_virtual_intf(struct wiphy *wiphy,
|
||||
enum nl80211_iftype type,
|
||||
struct vif_params *params)
|
||||
{
|
||||
/* Allow monitor and OCB modes through */
|
||||
if (type == NL80211_IFTYPE_MONITOR || type == NL80211_IFTYPE_OCB) {
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
|
||||
pr_info("rc_wifi_mon: setting interface %s to type %d\n",
|
||||
dev->name, type);
|
||||
|
||||
/* For monitor mode, we need to:
|
||||
* 1. Bring the interface down
|
||||
* 2. Change the type at the cfg80211 level
|
||||
* 3. Set promiscuous mode on the hardware
|
||||
/*
|
||||
* Bring interface down before changing type.
|
||||
* cfg80211 requires this for mode transitions.
|
||||
*/
|
||||
if (netif_running(dev))
|
||||
if (netif_running(dev)) {
|
||||
rtnl_lock();
|
||||
dev_close(dev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
wdev->iftype = type;
|
||||
|
||||
/* Set flags for monitor mode */
|
||||
if (type == NL80211_IFTYPE_MONITOR) {
|
||||
/* Set radiotap header type for monitor mode */
|
||||
dev->type = ARPHRD_IEEE80211_RADIOTAP;
|
||||
|
||||
if (params && params->flags)
|
||||
wdev->u.mntr.flags = params->flags;
|
||||
dev->type = ARPHRD_IEEE80211_RADIOTAP;
|
||||
|
||||
/* Driver-specific firmware enable */
|
||||
if (detected_driver == DRIVER_BCMDHD)
|
||||
bcmdhd_enable_monitor();
|
||||
} else {
|
||||
dev->type = ARPHRD_ETHER;
|
||||
}
|
||||
@@ -97,6 +287,13 @@ static int rc_change_virtual_intf(struct wiphy *wiphy,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Non-monitor type: if switching back from monitor, restore state */
|
||||
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) {
|
||||
dev->type = ARPHRD_ETHER;
|
||||
if (detected_driver == DRIVER_BCMDHD)
|
||||
bcmdhd_disable_monitor();
|
||||
}
|
||||
|
||||
/* All other types: pass through to original handler */
|
||||
if (orig_change_virtual_intf)
|
||||
return orig_change_virtual_intf(wiphy, dev, type, params);
|
||||
@@ -105,8 +302,7 @@ static int rc_change_virtual_intf(struct wiphy *wiphy,
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect which WiFi driver is active by checking module names
|
||||
* and wiphy registration.
|
||||
* Detect which WiFi driver is active by walking registered net devices.
|
||||
*/
|
||||
static enum wifi_driver_type detect_driver(void)
|
||||
{
|
||||
@@ -123,26 +319,32 @@ static enum wifi_driver_type detect_driver(void)
|
||||
|
||||
if (strstr(drvname, "scsc") || strstr(drvname, "slsi")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_SCSC;
|
||||
}
|
||||
if (strstr(drvname, "bcmdhd") || strstr(drvname, "dhd")) {
|
||||
if (strstr(drvname, "bcmdhd") || strstr(drvname, "dhd") ||
|
||||
strstr(drvname, "bcm4")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_BCMDHD;
|
||||
}
|
||||
if (strstr(drvname, "ath11k")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_ATH11K;
|
||||
}
|
||||
if (strstr(drvname, "ath12k")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_ATH12K;
|
||||
}
|
||||
if (strstr(drvname, "cnss") || strstr(drvname, "qca")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_CNSS;
|
||||
}
|
||||
@@ -153,11 +355,20 @@ static enum wifi_driver_type detect_driver(void)
|
||||
const char *wname = wiphy_name(dev->ieee80211_ptr->wiphy);
|
||||
|
||||
if (wname) {
|
||||
if (strstr(wname, "scsc") || strstr(wname, "slsi")) {
|
||||
if (strstr(wname, "scsc") ||
|
||||
strstr(wname, "slsi")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_SCSC;
|
||||
}
|
||||
if (strstr(wname, "bcm") ||
|
||||
strstr(wname, "brcm")) {
|
||||
target_wiphy = dev->ieee80211_ptr->wiphy;
|
||||
target_netdev = dev;
|
||||
rtnl_unlock();
|
||||
return DRIVER_BCMDHD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,8 +378,10 @@ static enum wifi_driver_type detect_driver(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a kernel text page writable so we can patch the ops table.
|
||||
* We restore permissions after patching.
|
||||
* Make a kernel text/rodata page writable so we can patch the ops table.
|
||||
* Uses set_memory_rw/ro — safe on ARM64 with CONFIG_DEBUG_SET_MODULE_RONX=n.
|
||||
* Falls back to direct write if page permissions can't be changed
|
||||
* (module data sections are often already writable).
|
||||
*/
|
||||
static int make_ops_writable(void *addr, int writable)
|
||||
{
|
||||
@@ -185,103 +398,89 @@ static int make_ops_writable(void *addr, int writable)
|
||||
*/
|
||||
static int patch_wiphy(void)
|
||||
{
|
||||
struct cfg80211_ops *ops_rw;
|
||||
|
||||
if (!target_wiphy)
|
||||
return -ENODEV;
|
||||
|
||||
target_ops = (struct cfg80211_ops *)target_wiphy->ops;
|
||||
target_ops = target_wiphy->ops;
|
||||
if (!target_ops)
|
||||
return -ENODEV;
|
||||
|
||||
/* Save original handler */
|
||||
/* Save originals for restore on unload */
|
||||
orig_change_virtual_intf = target_ops->change_virtual_intf;
|
||||
orig_interface_modes = target_wiphy->interface_modes;
|
||||
|
||||
/* Add monitor mode to supported interface types */
|
||||
/* Add monitor and OCB to supported interface types */
|
||||
target_wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
|
||||
target_wiphy->interface_modes |= BIT(NL80211_IFTYPE_OCB);
|
||||
|
||||
/* Patch the ops table */
|
||||
if (make_ops_writable((void *)target_ops, 1) == 0) {
|
||||
((struct cfg80211_ops *)target_ops)->change_virtual_intf =
|
||||
rc_change_virtual_intf;
|
||||
make_ops_writable((void *)target_ops, 0);
|
||||
pr_info("rc_wifi_mon: patched change_virtual_intf\n");
|
||||
/* Patch the ops table — need to cast away const and make writable */
|
||||
ops_rw = (struct cfg80211_ops *)target_ops;
|
||||
|
||||
if (make_ops_writable(ops_rw, 1) == 0) {
|
||||
ops_rw->change_virtual_intf = rc_change_virtual_intf;
|
||||
make_ops_writable(ops_rw, 0);
|
||||
pr_info("rc_wifi_mon: patched change_virtual_intf via "
|
||||
"set_memory_rw\n");
|
||||
} else {
|
||||
pr_warn("rc_wifi_mon: could not make ops writable, "
|
||||
"trying direct write\n");
|
||||
/* Some kernels allow direct writes to module data sections */
|
||||
((struct cfg80211_ops *)target_ops)->change_virtual_intf =
|
||||
rc_change_virtual_intf;
|
||||
/*
|
||||
* set_memory_rw failed — try direct write. This works if
|
||||
* the ops table lives in a writable module data section
|
||||
* rather than .rodata.
|
||||
*/
|
||||
pr_info("rc_wifi_mon: set_memory_rw failed, attempting "
|
||||
"direct patch\n");
|
||||
ops_rw->change_virtual_intf = rc_change_virtual_intf;
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: interface_modes now: 0x%x\n",
|
||||
target_wiphy->interface_modes);
|
||||
pr_info("rc_wifi_mon: interface_modes: 0x%x -> 0x%x\n",
|
||||
orig_interface_modes, target_wiphy->interface_modes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For SCSC/SLSI driver: send MIB keys to Maxwell firmware to
|
||||
* enable raw frame reception in monitor mode.
|
||||
*
|
||||
* The SLSI firmware uses MIB OIDs to control behavior. Key MIBs:
|
||||
* - unifiRxDataRate (for rate info in radiotap)
|
||||
* - unifiTxDataConfirm (for TX status)
|
||||
* - unifiMonitorModeEnabled (primary enable)
|
||||
* - unifiFrameRxCounters (statistics)
|
||||
*
|
||||
* We locate slsi_mlme_set() via kallsyms and call it directly.
|
||||
* Restore original state on module unload.
|
||||
*/
|
||||
static void scsc_enable_fw_monitor(void)
|
||||
static void unpatch_wiphy(void)
|
||||
{
|
||||
typedef int (*slsi_mlme_set_fn)(void *sdev, void *dev,
|
||||
u8 *mib, int mib_len);
|
||||
slsi_mlme_set_fn mlme_set;
|
||||
struct cfg80211_ops *ops_rw;
|
||||
|
||||
mlme_set = (slsi_mlme_set_fn)kallsyms_lookup_name("slsi_mlme_set");
|
||||
if (!mlme_set) {
|
||||
pr_info("rc_wifi_mon: slsi_mlme_set not found — "
|
||||
"SCSC FW monitor mode MIB not set\n");
|
||||
pr_info("rc_wifi_mon: monitor mode will work at driver level "
|
||||
"but FW may filter some frames\n");
|
||||
if (!target_ops || !orig_change_virtual_intf)
|
||||
return;
|
||||
|
||||
ops_rw = (struct cfg80211_ops *)target_ops;
|
||||
|
||||
/* Restore original change_virtual_intf */
|
||||
if (make_ops_writable(ops_rw, 1) == 0) {
|
||||
ops_rw->change_virtual_intf = orig_change_virtual_intf;
|
||||
make_ops_writable(ops_rw, 0);
|
||||
} else {
|
||||
ops_rw->change_virtual_intf = orig_change_virtual_intf;
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: found slsi_mlme_set, SCSC FW patching "
|
||||
"available (MIB write deferred to mode switch)\n");
|
||||
/* Actual MIB write happens when interface is switched to monitor —
|
||||
* we hook into the mode change path above */
|
||||
/* Restore original interface modes */
|
||||
if (target_wiphy)
|
||||
target_wiphy->interface_modes = orig_interface_modes;
|
||||
|
||||
/* If interface is still in monitor mode, reset it */
|
||||
if (target_netdev && target_netdev->ieee80211_ptr &&
|
||||
target_netdev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) {
|
||||
target_netdev->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION;
|
||||
target_netdev->type = ARPHRD_ETHER;
|
||||
if (detected_driver == DRIVER_BCMDHD)
|
||||
bcmdhd_disable_monitor();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* For bcmdhd: set the DHD driver's monitor mode flag and issue
|
||||
* the firmware iovar to enable monitor.
|
||||
*/
|
||||
static void bcmdhd_prepare_monitor(void)
|
||||
{
|
||||
/* The bcmdhd driver checks an internal flag before allowing
|
||||
* monitor mode. We locate dhd_monitor_init or the cfg80211
|
||||
* vendor command handler.
|
||||
*
|
||||
* Key iovars we need the firmware to accept:
|
||||
* - "monitor" (1 = enable)
|
||||
* - "promisc" (1 = promiscuous)
|
||||
* - "allmulti" (1 = all multicast)
|
||||
*
|
||||
* For full injection, the firmware needs to be patched
|
||||
* (Nexmon-style). Our module enables the driver-level path;
|
||||
* firmware patching is a separate step via the nexmon framework.
|
||||
*/
|
||||
pr_info("rc_wifi_mon: bcmdhd driver detected — driver-level "
|
||||
"monitor mode enabled\n");
|
||||
pr_info("rc_wifi_mon: for packet injection, Nexmon firmware "
|
||||
"patch is also required\n");
|
||||
}
|
||||
/* ---- Module init/exit ---- */
|
||||
|
||||
static int __init rc_wifi_mon_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pr_info("rc_wifi_mon: RadioControl WiFi monitor mode enabler\n");
|
||||
pr_info("rc_wifi_mon: RadioControl WiFi monitor mode enabler v1.0\n");
|
||||
|
||||
detected_driver = detect_driver();
|
||||
if (detected_driver == DRIVER_UNKNOWN) {
|
||||
@@ -289,9 +488,10 @@ static int __init rc_wifi_mon_init(void)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: detected driver type: %d, wiphy: %s\n",
|
||||
detected_driver,
|
||||
target_wiphy ? wiphy_name(target_wiphy) : "null");
|
||||
pr_info("rc_wifi_mon: detected driver: %s, wiphy: %s, netdev: %s\n",
|
||||
driver_names[detected_driver],
|
||||
target_wiphy ? wiphy_name(target_wiphy) : "null",
|
||||
target_netdev ? target_netdev->name : "null");
|
||||
|
||||
ret = patch_wiphy();
|
||||
if (ret) {
|
||||
@@ -305,32 +505,40 @@ static int __init rc_wifi_mon_init(void)
|
||||
scsc_enable_fw_monitor();
|
||||
break;
|
||||
case DRIVER_BCMDHD:
|
||||
bcmdhd_prepare_monitor();
|
||||
pr_info("rc_wifi_mon: BCM4390 driver patched — monitor mode "
|
||||
"enabled at driver level\n");
|
||||
pr_info("rc_wifi_mon: for packet injection, Nexmon firmware "
|
||||
"patch is also required\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Create sysfs entries under /sys/kernel/rc_wifi_mon/ */
|
||||
rc_kobj = kobject_create_and_add("rc_wifi_mon", kernel_kobj);
|
||||
if (rc_kobj) {
|
||||
ret = sysfs_create_group(rc_kobj, &rc_attr_group);
|
||||
if (ret) {
|
||||
kobject_put(rc_kobj);
|
||||
rc_kobj = NULL;
|
||||
pr_warn("rc_wifi_mon: sysfs creation failed: %d\n", ret);
|
||||
/* Non-fatal */
|
||||
}
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: loaded — monitor mode available via "
|
||||
"'iw dev wlanX set type monitor'\n");
|
||||
"'iw dev %s set type monitor'\n",
|
||||
target_netdev ? target_netdev->name : "wlanX");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit rc_wifi_mon_exit(void)
|
||||
{
|
||||
/* Restore original handler */
|
||||
if (target_ops && orig_change_virtual_intf) {
|
||||
if (make_ops_writable((void *)target_ops, 1) == 0) {
|
||||
((struct cfg80211_ops *)target_ops)->change_virtual_intf =
|
||||
orig_change_virtual_intf;
|
||||
make_ops_writable((void *)target_ops, 0);
|
||||
}
|
||||
}
|
||||
unpatch_wiphy();
|
||||
|
||||
/* Remove monitor from interface_modes */
|
||||
if (target_wiphy) {
|
||||
target_wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_MONITOR);
|
||||
target_wiphy->interface_modes &= ~BIT(NL80211_IFTYPE_OCB);
|
||||
if (rc_kobj) {
|
||||
sysfs_remove_group(rc_kobj, &rc_attr_group);
|
||||
kobject_put(rc_kobj);
|
||||
}
|
||||
|
||||
pr_info("rc_wifi_mon: unloaded — monitor mode disabled\n");
|
||||
|
||||
Reference in New Issue
Block a user