Add linux-kpi wireless compat layer

Red Bear OS Team
This commit is contained in:
2026-04-16 12:43:33 +01:00
parent 6c418bb03b
commit f44940a562
10 changed files with 1245 additions and 0 deletions
@@ -0,0 +1,20 @@
#ifndef _LINUX_IEEE80211_H
#define _LINUX_IEEE80211_H
#include "types.h"
#define IEEE80211_MAX_SSID_LEN 32
#define IEEE80211_NUM_ACS 4
struct ieee80211_channel {
u16 center_freq;
u16 hw_value;
u32 flags;
};
struct ieee80211_rate {
u16 bitrate;
u16 hw_value;
};
#endif
@@ -0,0 +1,36 @@
#ifndef _LINUX_NETDEVICE_H
#define _LINUX_NETDEVICE_H
#include "device.h"
#include "types.h"
#include <stddef.h>
struct net_device {
char name[16];
unsigned char dev_addr[6];
unsigned char addr_len;
unsigned int mtu;
unsigned int flags;
int carrier;
void *ml_priv;
void *ieee80211_ptr;
void *priv_data;
int registered;
size_t __priv_alloc_size;
size_t __priv_alloc_align;
};
extern struct net_device *alloc_netdev_mqs(size_t sizeof_priv,
const char *name,
unsigned char name_assign_type,
void (*setup)(struct net_device *dev),
unsigned int txqs,
unsigned int rxqs);
extern void free_netdev(struct net_device *dev);
extern int register_netdev(struct net_device *dev);
extern void unregister_netdev(struct net_device *dev);
extern void netif_carrier_on(struct net_device *dev);
extern void netif_carrier_off(struct net_device *dev);
extern int netif_carrier_ok(const struct net_device *dev);
#endif
@@ -0,0 +1,25 @@
#ifndef _LINUX_NL80211_H
#define _LINUX_NL80211_H
enum nl80211_iftype {
NL80211_IFTYPE_UNSPECIFIED = 0,
NL80211_IFTYPE_ADHOC = 1,
NL80211_IFTYPE_STATION = 2,
NL80211_IFTYPE_AP = 3,
NL80211_IFTYPE_MONITOR = 6,
};
enum nl80211_commands {
NL80211_CMD_UNSPEC = 0,
NL80211_CMD_TRIGGER_SCAN = 33,
NL80211_CMD_NEW_SCAN_RESULTS = 34,
NL80211_CMD_AUTHENTICATE = 37,
NL80211_CMD_ASSOCIATE = 38,
NL80211_CMD_CONNECT = 46,
NL80211_CMD_DISCONNECT = 48,
NL80211_CMD_SET_KEY = 80,
};
#define NL80211_MAX_NR_CIPHER_SUITES 5
#endif
@@ -0,0 +1,24 @@
#ifndef _LINUX_SKBUFF_H
#define _LINUX_SKBUFF_H
#include "types.h"
struct sk_buff {
void *head;
void *data;
unsigned int len;
unsigned int tail;
unsigned int end;
};
extern struct sk_buff *alloc_skb(unsigned int size, gfp_t gfp_mask);
extern void kfree_skb(struct sk_buff *skb);
extern void skb_reserve(struct sk_buff *skb, unsigned int len);
extern void *skb_put(struct sk_buff *skb, unsigned int len);
extern void *skb_push(struct sk_buff *skb, unsigned int len);
extern void *skb_pull(struct sk_buff *skb, unsigned int len);
extern unsigned int skb_headroom(const struct sk_buff *skb);
extern unsigned int skb_tailroom(const struct sk_buff *skb);
extern void skb_trim(struct sk_buff *skb, unsigned int len);
#endif
@@ -0,0 +1,108 @@
#ifndef _NET_CFG80211_H
#define _NET_CFG80211_H
#include "../linux/device.h"
#include "../linux/ieee80211.h"
#include "../linux/netdevice.h"
#include "../linux/types.h"
#include <stddef.h>
#include <stdbool.h>
struct wiphy {
void *privid;
int registered;
u32 interface_modes;
int max_scan_ssids;
int max_scan_ie_len;
};
struct wireless_dev {
struct wiphy *wiphy;
struct net_device *netdev;
u32 iftype;
bool scan_in_flight;
bool scan_aborted;
bool connecting;
bool connected;
bool locally_generated;
u16 last_status;
u16 last_reason;
bool has_bssid;
u8 last_bssid[6];
};
struct cfg80211_scan_info {
bool aborted;
};
struct cfg80211_scan_request {
struct wiphy *wiphy;
struct wireless_dev *wdev;
u32 n_ssids;
u32 n_channels;
};
struct cfg80211_ssid {
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len;
};
struct key_params {
const u8 *key;
u8 key_len;
u32 cipher;
};
struct cfg80211_connect_params {
const u8 *ssid;
size_t ssid_len;
const u8 *bssid;
const u8 *ie;
size_t ie_len;
struct key_params key;
};
struct station_parameters {
const u8 *supported_rates;
size_t supported_rates_len;
u32 sta_flags_mask;
u32 sta_flags_set;
};
extern struct wiphy *wiphy_new_nm(const void *ops, size_t sizeof_priv, const char *requested_name);
extern void wiphy_free(struct wiphy *wiphy);
extern int wiphy_register(struct wiphy *wiphy);
extern void wiphy_unregister(struct wiphy *wiphy);
extern void cfg80211_scan_done(struct cfg80211_scan_request *request,
const struct cfg80211_scan_info *info);
extern void cfg80211_connect_result(struct net_device *dev,
const u8 *bssid,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp);
extern void cfg80211_disconnected(struct net_device *dev,
u16 reason,
const u8 *ie,
size_t ie_len,
bool locally_generated,
gfp_t gfp);
extern void cfg80211_connect_bss(struct net_device *dev,
const u8 *bssid,
const u8 *req_ie,
size_t req_ie_len,
const u8 *resp_ie,
size_t resp_ie_len,
u16 status,
gfp_t gfp);
extern void cfg80211_ready_on_channel(struct wireless_dev *wdev,
u64 cookie,
struct ieee80211_channel *chan,
u32 chan_type,
u32 duration,
gfp_t gfp);
#endif
@@ -0,0 +1,47 @@
#ifndef _NET_MAC80211_H
#define _NET_MAC80211_H
#include "../linux/device.h"
#include "../linux/netdevice.h"
#include "../linux/skbuff.h"
#include "../linux/types.h"
#include "cfg80211.h"
struct ieee80211_hw {
struct wiphy *wiphy;
void *priv;
int registered;
u32 extra_tx_headroom;
u16 queues;
};
struct ieee80211_vif {
u8 addr[6];
void *drv_priv;
u32 type;
bool cfg_assoc;
};
struct ieee80211_sta {
u8 addr[6];
void *drv_priv;
u16 aid;
};
struct ieee80211_bss_conf {
bool assoc;
u16 aid;
u16 beacon_int;
};
extern struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
const void *ops,
const char *requested_name);
extern void ieee80211_free_hw(struct ieee80211_hw *hw);
extern int ieee80211_register_hw(struct ieee80211_hw *hw);
extern void ieee80211_unregister_hw(struct ieee80211_hw *hw);
extern void ieee80211_queue_work(struct ieee80211_hw *hw, void *work);
extern void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted);
extern void ieee80211_connection_loss(struct ieee80211_vif *vif);
#endif
@@ -0,0 +1,200 @@
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::ffi::c_void;
use std::ptr;
use std::sync::atomic::{AtomicI32, Ordering};
use super::wireless::{wiphy_free, wiphy_new_nm, wiphy_register, wiphy_unregister, Wiphy};
use super::workqueue::{schedule_work, WorkStruct};
#[repr(C)]
pub struct Ieee80211Hw {
pub wiphy: *mut Wiphy,
pub priv_data: *mut c_void,
pub registered: AtomicI32,
pub extra_tx_headroom: u32,
pub queues: u16,
priv_alloc_size: usize,
priv_alloc_align: usize,
}
#[repr(C)]
pub struct Ieee80211Vif {
pub addr: [u8; 6],
pub drv_priv: *mut c_void,
pub type_: u32,
pub cfg_assoc: bool,
}
#[repr(C)]
pub struct Ieee80211Sta {
pub addr: [u8; 6],
pub drv_priv: *mut c_void,
pub aid: u16,
}
#[repr(C)]
pub struct Ieee80211BssConf {
pub assoc: bool,
pub aid: u16,
pub beacon_int: u16,
}
#[no_mangle]
pub extern "C" fn ieee80211_alloc_hw_nm(
priv_data_len: usize,
ops: *const c_void,
requested_name: *const u8,
) -> *mut Ieee80211Hw {
let wiphy = wiphy_new_nm(ops, 0, requested_name);
if wiphy.is_null() {
return ptr::null_mut();
}
let mut hw = Box::new(Ieee80211Hw {
wiphy,
priv_data: ptr::null_mut(),
registered: AtomicI32::new(0),
extra_tx_headroom: 0,
queues: 1,
priv_alloc_size: 0,
priv_alloc_align: 0,
});
if priv_data_len != 0 {
let layout = match Layout::from_size_align(priv_data_len, 16) {
Ok(layout) => layout,
Err(_) => {
wiphy_free(wiphy);
return ptr::null_mut();
}
};
let ptr = unsafe { alloc_zeroed(layout) } as *mut c_void;
if ptr.is_null() {
wiphy_free(wiphy);
return ptr::null_mut();
}
hw.priv_data = ptr;
hw.priv_alloc_size = priv_data_len;
hw.priv_alloc_align = 16;
}
Box::into_raw(hw)
}
#[no_mangle]
pub extern "C" fn ieee80211_free_hw(hw: *mut Ieee80211Hw) {
if hw.is_null() {
return;
}
unsafe {
let hw_box = Box::from_raw(hw);
if !hw_box.priv_data.is_null() {
if let Ok(layout) = Layout::from_size_align(
hw_box.priv_alloc_size.max(1),
hw_box.priv_alloc_align.max(1),
) {
dealloc(hw_box.priv_data.cast::<u8>(), layout);
}
}
wiphy_free(hw_box.wiphy);
}
}
#[no_mangle]
pub extern "C" fn ieee80211_register_hw(hw: *mut Ieee80211Hw) -> i32 {
if hw.is_null() {
return -22;
}
if unsafe { &*hw }.registered.load(Ordering::Acquire) != 0 {
return -16;
}
let rc = wiphy_register(unsafe { (*hw).wiphy });
if rc != 0 {
return rc;
}
unsafe { &*hw }.registered.store(1, Ordering::Release);
0
}
#[no_mangle]
pub extern "C" fn ieee80211_unregister_hw(hw: *mut Ieee80211Hw) {
if hw.is_null() {
return;
}
if unsafe { &*hw }.registered.load(Ordering::Acquire) == 0 {
return;
}
wiphy_unregister(unsafe { (*hw).wiphy });
unsafe { &*hw }.registered.store(0, Ordering::Release);
}
#[no_mangle]
pub extern "C" fn ieee80211_queue_work(_hw: *mut Ieee80211Hw, work: *mut c_void) {
if work.is_null() {
return;
}
let _ = schedule_work(work.cast::<WorkStruct>());
}
#[no_mangle]
pub extern "C" fn ieee80211_scan_completed(_hw: *mut Ieee80211Hw, _aborted: bool) {}
#[no_mangle]
pub extern "C" fn ieee80211_connection_loss(vif: *mut Ieee80211Vif) {
if vif.is_null() {
return;
}
unsafe { (*vif).cfg_assoc = false };
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rust_impl::workqueue::{flush_scheduled_work, WorkStruct};
use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
static WORK_RAN: AtomicBool = AtomicBool::new(false);
extern "C" fn test_work(_work: *mut WorkStruct) {
WORK_RAN.store(true, AtomicOrdering::Release);
}
#[test]
fn ieee80211_hw_registration_round_trip_works() {
let hw = ieee80211_alloc_hw_nm(0, ptr::null(), ptr::null());
assert!(!hw.is_null());
assert_eq!(ieee80211_register_hw(hw), 0);
assert_eq!(ieee80211_register_hw(hw), -16);
assert_eq!(unsafe { (*hw).registered.load(Ordering::Acquire) }, 1);
ieee80211_unregister_hw(hw);
assert_eq!(unsafe { (*hw).registered.load(Ordering::Acquire) }, 0);
ieee80211_free_hw(hw);
}
#[test]
fn ieee80211_queue_work_dispatches_work() {
let hw = ieee80211_alloc_hw_nm(0, ptr::null(), ptr::null());
assert!(!hw.is_null());
let mut work = WorkStruct {
func: Some(test_work),
__opaque: [0; 64],
};
WORK_RAN.store(false, AtomicOrdering::Release);
ieee80211_queue_work(hw, (&mut work as *mut WorkStruct).cast::<c_void>());
flush_scheduled_work();
assert!(WORK_RAN.load(AtomicOrdering::Acquire));
ieee80211_free_hw(hw);
}
#[test]
fn connection_loss_clears_assoc_state() {
let mut vif = Ieee80211Vif {
addr: [0; 6],
drv_priv: ptr::null_mut(),
type_: 0,
cfg_assoc: true,
};
ieee80211_connection_loss(&mut vif);
assert!(!vif.cfg_assoc);
}
}
@@ -0,0 +1,387 @@
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::ffi::c_void;
use std::ptr;
use std::sync::atomic::{AtomicI32, Ordering};
#[repr(C)]
pub struct SkBuff {
pub head: *mut u8,
pub data: *mut u8,
pub len: u32,
pub tail: u32,
pub end: u32,
}
#[repr(C)]
pub struct NetDevice {
pub name: [u8; 16],
pub dev_addr: [u8; 6],
pub addr_len: u8,
pub mtu: u32,
pub flags: u32,
pub carrier: AtomicI32,
pub ml_priv: *mut c_void,
pub ieee80211_ptr: *mut c_void,
pub priv_data: *mut c_void,
pub registered: AtomicI32,
priv_alloc_size: usize,
priv_alloc_align: usize,
}
unsafe fn free_skb_buffer(skb: *mut SkBuff) {
if skb.is_null() {
return;
}
let head = (*skb).head;
let end = (*skb).end as usize;
if !head.is_null() && end != 0 {
if let Ok(layout) = Layout::from_size_align(end, 16) {
dealloc(head, layout);
}
}
}
#[no_mangle]
pub extern "C" fn alloc_skb(size: u32, _gfp_mask: u32) -> *mut SkBuff {
let capacity = size as usize;
let layout = match Layout::from_size_align(capacity.max(1), 16) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let head = unsafe { alloc_zeroed(layout) };
if head.is_null() {
return ptr::null_mut();
}
Box::into_raw(Box::new(SkBuff {
head,
data: head,
len: 0,
tail: 0,
end: capacity as u32,
}))
}
#[no_mangle]
pub extern "C" fn kfree_skb(skb: *mut SkBuff) {
if skb.is_null() {
return;
}
unsafe {
free_skb_buffer(skb);
drop(Box::from_raw(skb));
}
}
#[no_mangle]
pub extern "C" fn skb_reserve(skb: *mut SkBuff, len: u32) {
if skb.is_null() {
return;
}
let skb_ref = unsafe { &mut *skb };
let headroom = unsafe { skb_ref.data.offset_from(skb_ref.head) };
let Ok(headroom) = u32::try_from(headroom) else {
return;
};
let new_headroom = headroom.saturating_add(len);
if new_headroom > skb_ref.end || skb_ref.tail != 0 || skb_ref.len != 0 {
return;
}
skb_ref.data = unsafe { skb_ref.head.add(new_headroom as usize) };
}
#[no_mangle]
pub extern "C" fn skb_put(skb: *mut SkBuff, len: u32) -> *mut u8 {
if skb.is_null() {
return ptr::null_mut();
}
let skb_ref = unsafe { &mut *skb };
let new_tail = skb_ref.tail.saturating_add(len);
if new_tail > skb_ref.end {
return ptr::null_mut();
}
let ptr = unsafe { skb_ref.data.add(skb_ref.tail as usize) };
skb_ref.tail = new_tail;
skb_ref.len = skb_ref.len.saturating_add(len);
ptr
}
#[no_mangle]
pub extern "C" fn skb_push(skb: *mut SkBuff, len: u32) -> *mut u8 {
if skb.is_null() {
return ptr::null_mut();
}
let skb_ref = unsafe { &mut *skb };
let headroom = unsafe { skb_ref.data.offset_from(skb_ref.head) };
if headroom < len as isize {
return ptr::null_mut();
}
skb_ref.data = unsafe { skb_ref.data.sub(len as usize) };
skb_ref.tail = skb_ref.tail.saturating_add(len);
skb_ref.len = skb_ref.len.saturating_add(len);
skb_ref.data
}
#[no_mangle]
pub extern "C" fn skb_pull(skb: *mut SkBuff, len: u32) -> *mut u8 {
if skb.is_null() {
return ptr::null_mut();
}
let skb_ref = unsafe { &mut *skb };
if len > skb_ref.len {
return ptr::null_mut();
}
skb_ref.data = unsafe { skb_ref.data.add(len as usize) };
skb_ref.tail -= len;
skb_ref.len -= len;
skb_ref.data
}
#[no_mangle]
pub extern "C" fn skb_headroom(skb: *const SkBuff) -> u32 {
if skb.is_null() {
return 0;
}
let skb_ref = unsafe { &*skb };
let headroom = unsafe { skb_ref.data.offset_from(skb_ref.head) };
u32::try_from(headroom).unwrap_or_default()
}
#[no_mangle]
pub extern "C" fn skb_tailroom(skb: *const SkBuff) -> u32 {
if skb.is_null() {
return 0;
}
let skb_ref = unsafe { &*skb };
let headroom = skb_headroom(skb);
skb_ref
.end
.saturating_sub(headroom.saturating_add(skb_ref.tail))
}
#[no_mangle]
pub extern "C" fn skb_trim(skb: *mut SkBuff, len: u32) {
if skb.is_null() {
return;
}
let skb_ref = unsafe { &mut *skb };
let new_len = len.min(skb_ref.len);
skb_ref.len = new_len;
skb_ref.tail = new_len;
}
#[no_mangle]
pub extern "C" fn alloc_netdev_mqs(
sizeof_priv: usize,
name: *const u8,
_name_assign_type: u8,
_setup: Option<extern "C" fn(*mut NetDevice)>,
_txqs: u32,
_rxqs: u32,
) -> *mut NetDevice {
let mut dev = Box::new(NetDevice {
name: [0; 16],
dev_addr: [0; 6],
addr_len: 6,
mtu: 1500,
flags: 0,
carrier: AtomicI32::new(0),
ml_priv: ptr::null_mut(),
ieee80211_ptr: ptr::null_mut(),
priv_data: ptr::null_mut(),
registered: AtomicI32::new(0),
priv_alloc_size: 0,
priv_alloc_align: 0,
});
if !name.is_null() {
for (idx, byte) in dev.name.iter_mut().enumerate() {
let value = unsafe { *name.add(idx) };
*byte = value;
if value == 0 {
break;
}
}
}
if sizeof_priv != 0 {
let layout = match Layout::from_size_align(sizeof_priv, 16) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let priv_ptr = unsafe { alloc_zeroed(layout) } as *mut c_void;
if priv_ptr.is_null() {
return ptr::null_mut();
}
dev.priv_data = priv_ptr;
dev.priv_alloc_size = sizeof_priv;
dev.priv_alloc_align = 16;
}
if let Some(setup) = _setup {
setup(dev.as_mut());
}
Box::into_raw(dev)
}
#[no_mangle]
pub extern "C" fn free_netdev(dev: *mut NetDevice) {
if dev.is_null() {
return;
}
unsafe {
let dev_box = Box::from_raw(dev);
if !dev_box.priv_data.is_null() {
let layout = Layout::from_size_align(
dev_box.priv_alloc_size.max(1),
dev_box.priv_alloc_align.max(1),
)
.ok();
if let Some(layout) = layout {
dealloc(dev_box.priv_data.cast::<u8>(), layout);
}
}
}
}
#[no_mangle]
pub extern "C" fn register_netdev(dev: *mut NetDevice) -> i32 {
if dev.is_null() {
return -22;
}
if unsafe { &*dev }.registered.load(Ordering::Acquire) != 0 {
return -16;
}
unsafe { &*dev }.registered.store(1, Ordering::Release);
0
}
#[no_mangle]
pub extern "C" fn unregister_netdev(dev: *mut NetDevice) {
if dev.is_null() {
return;
}
unsafe { &*dev }.registered.store(0, Ordering::Release);
netif_carrier_off(dev);
}
#[no_mangle]
pub extern "C" fn netif_carrier_on(dev: *mut NetDevice) {
if dev.is_null() {
return;
}
unsafe { &*dev }.carrier.store(1, Ordering::Release);
}
#[no_mangle]
pub extern "C" fn netif_carrier_off(dev: *mut NetDevice) {
if dev.is_null() {
return;
}
unsafe { &*dev }.carrier.store(0, Ordering::Release);
}
#[no_mangle]
pub extern "C" fn netif_carrier_ok(dev: *const NetDevice) -> i32 {
if dev.is_null() {
return 0;
}
if unsafe { &*dev }.carrier.load(Ordering::Acquire) != 0 {
1
} else {
0
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CString;
use std::sync::atomic::AtomicUsize;
static SETUP_CALLS: AtomicUsize = AtomicUsize::new(0);
extern "C" fn test_setup(_dev: *mut NetDevice) {
SETUP_CALLS.fetch_add(1, Ordering::AcqRel);
}
#[test]
fn skb_allocation_and_growth_work() {
let skb = alloc_skb(64, 0);
assert!(!skb.is_null());
let tail_ptr = skb_put(skb, 8);
assert!(!tail_ptr.is_null());
assert_eq!(unsafe { (*skb).len }, 8);
skb_trim(skb, 4);
assert_eq!(unsafe { (*skb).len }, 4);
kfree_skb(skb);
}
#[test]
fn skb_headroom_and_push_pull_work() {
let skb = alloc_skb(64, 0);
assert!(!skb.is_null());
skb_reserve(skb, 8);
assert_eq!(skb_headroom(skb), 8);
assert_eq!(skb_tailroom(skb), 56);
assert!(!skb_put(skb, 8).is_null());
assert!(!skb_push(skb, 4).is_null());
assert_eq!(unsafe { (*skb).len }, 12);
assert!(!skb_pull(skb, 6).is_null());
assert_eq!(unsafe { (*skb).len }, 6);
kfree_skb(skb);
}
#[test]
fn net_device_carrier_tracking_works() {
let name = CString::new("wlan%d").unwrap();
let dev = alloc_netdev_mqs(
0usize,
name.as_ptr().cast::<u8>(),
0u8,
None::<extern "C" fn(*mut NetDevice)>,
1u32,
1u32,
);
assert!(!dev.is_null());
assert_eq!(netif_carrier_ok(dev), 0);
netif_carrier_on(dev);
assert_eq!(netif_carrier_ok(dev), 1);
netif_carrier_off(dev);
assert_eq!(netif_carrier_ok(dev), 0);
free_netdev(dev);
}
#[test]
fn net_device_setup_and_registration_work() {
SETUP_CALLS.store(0, Ordering::Release);
let name = CString::new("wlan%d").unwrap();
let dev = alloc_netdev_mqs(
32usize,
name.as_ptr().cast::<u8>(),
0u8,
Some(test_setup),
1u32,
1u32,
);
assert!(!dev.is_null());
assert_eq!(SETUP_CALLS.load(Ordering::Acquire), 1);
assert_eq!(register_netdev(dev), 0);
assert_eq!(unsafe { (*dev).registered.load(Ordering::Acquire) }, 1);
assert_eq!(register_netdev(dev), -16);
unregister_netdev(dev);
assert_eq!(unsafe { (*dev).registered.load(Ordering::Acquire) }, 0);
free_netdev(dev);
}
}
@@ -0,0 +1,369 @@
use std::alloc::{alloc_zeroed, dealloc, Layout};
use std::ffi::c_void;
use std::ptr;
use std::sync::atomic::{AtomicI32, Ordering};
use super::net::{netif_carrier_off, netif_carrier_on, NetDevice};
#[repr(C)]
pub struct Wiphy {
pub priv_data: *mut c_void,
pub registered: AtomicI32,
pub interface_modes: u32,
pub max_scan_ssids: i32,
pub max_scan_ie_len: i32,
priv_alloc_size: usize,
priv_alloc_align: usize,
}
#[repr(C)]
pub struct WirelessDev {
pub wiphy: *mut Wiphy,
pub netdev: *mut c_void,
pub iftype: u32,
pub scan_in_flight: bool,
pub scan_aborted: bool,
pub connecting: bool,
pub connected: bool,
pub locally_generated: bool,
pub last_status: u16,
pub last_reason: u16,
pub has_bssid: bool,
pub last_bssid: [u8; 6],
}
#[repr(C)]
pub struct Cfg80211ScanInfo {
pub aborted: bool,
}
#[repr(C)]
pub struct Cfg80211ScanRequest {
pub wiphy: *mut Wiphy,
pub wdev: *mut WirelessDev,
pub n_ssids: u32,
pub n_channels: u32,
}
#[repr(C)]
pub struct Cfg80211Ssid {
pub ssid: [u8; 32],
pub ssid_len: u8,
}
#[repr(C)]
pub struct KeyParams {
pub key: *const u8,
pub key_len: u8,
pub cipher: u32,
}
#[repr(C)]
pub struct Cfg80211ConnectParams {
pub ssid: *const u8,
pub ssid_len: usize,
pub bssid: *const u8,
pub ie: *const u8,
pub ie_len: usize,
pub key: KeyParams,
}
#[repr(C)]
pub struct StationParameters {
pub supported_rates: *const u8,
pub supported_rates_len: usize,
pub sta_flags_mask: u32,
pub sta_flags_set: u32,
}
#[no_mangle]
pub extern "C" fn wiphy_new_nm(
_ops: *const c_void,
sizeof_priv: usize,
_requested_name: *const u8,
) -> *mut Wiphy {
let mut wiphy = Box::new(Wiphy {
priv_data: ptr::null_mut(),
registered: AtomicI32::new(0),
interface_modes: 0,
max_scan_ssids: 4,
max_scan_ie_len: 512,
priv_alloc_size: 0,
priv_alloc_align: 0,
});
if sizeof_priv != 0 {
let layout = match Layout::from_size_align(sizeof_priv, 16) {
Ok(layout) => layout,
Err(_) => return ptr::null_mut(),
};
let ptr = unsafe { alloc_zeroed(layout) } as *mut c_void;
if ptr.is_null() {
return ptr::null_mut();
}
wiphy.priv_data = ptr;
wiphy.priv_alloc_size = sizeof_priv;
wiphy.priv_alloc_align = 16;
}
Box::into_raw(wiphy)
}
#[no_mangle]
pub extern "C" fn wiphy_free(wiphy: *mut Wiphy) {
if wiphy.is_null() {
return;
}
unsafe {
let wiphy_box = Box::from_raw(wiphy);
if !wiphy_box.priv_data.is_null() {
if let Ok(layout) = Layout::from_size_align(
wiphy_box.priv_alloc_size.max(1),
wiphy_box.priv_alloc_align.max(1),
) {
dealloc(wiphy_box.priv_data.cast::<u8>(), layout);
}
}
}
}
#[no_mangle]
pub extern "C" fn wiphy_register(wiphy: *mut Wiphy) -> i32 {
if wiphy.is_null() {
return -22;
}
if unsafe { &*wiphy }.registered.load(Ordering::Acquire) != 0 {
return -16;
}
unsafe { &*wiphy }.registered.store(1, Ordering::Release);
0
}
#[no_mangle]
pub extern "C" fn wiphy_unregister(wiphy: *mut Wiphy) {
if wiphy.is_null() {
return;
}
unsafe { &*wiphy }.registered.store(0, Ordering::Release);
}
#[no_mangle]
pub extern "C" fn cfg80211_scan_done(
request: *mut Cfg80211ScanRequest,
info: *const Cfg80211ScanInfo,
) {
if request.is_null() {
return;
}
let wdev = unsafe { (*request).wdev };
if wdev.is_null() {
return;
}
unsafe {
(*wdev).scan_in_flight = false;
(*wdev).scan_aborted = if info.is_null() {
false
} else {
(*info).aborted
};
}
}
fn netdev_to_wireless_dev(dev: *mut c_void) -> *mut WirelessDev {
if dev.is_null() {
return ptr::null_mut();
}
let dev = dev.cast::<NetDevice>();
unsafe { (*dev).ieee80211_ptr.cast::<WirelessDev>() }
}
fn copy_bssid(dst: &mut WirelessDev, bssid: *const u8) {
if bssid.is_null() {
dst.has_bssid = false;
dst.last_bssid = [0; 6];
return;
}
unsafe {
ptr::copy_nonoverlapping(bssid, dst.last_bssid.as_mut_ptr(), dst.last_bssid.len());
}
dst.has_bssid = true;
}
#[no_mangle]
pub extern "C" fn cfg80211_connect_result(
dev: *mut c_void,
bssid: *const u8,
_req_ie: *const u8,
_req_ie_len: usize,
_resp_ie: *const u8,
_resp_ie_len: usize,
status: u16,
_gfp: u32,
) {
let wdev = netdev_to_wireless_dev(dev);
if wdev.is_null() {
return;
}
unsafe {
let wdev_ref = &mut *wdev;
wdev_ref.connecting = false;
wdev_ref.connected = status == 0;
wdev_ref.last_status = status;
wdev_ref.locally_generated = false;
copy_bssid(wdev_ref, bssid);
}
if status == 0 {
netif_carrier_on(dev.cast::<NetDevice>());
} else {
netif_carrier_off(dev.cast::<NetDevice>());
}
}
#[no_mangle]
pub extern "C" fn cfg80211_disconnected(
dev: *mut c_void,
reason: u16,
_ie: *const u8,
_ie_len: usize,
locally_generated: bool,
_gfp: u32,
) {
let wdev = netdev_to_wireless_dev(dev);
if !wdev.is_null() {
unsafe {
let wdev_ref = &mut *wdev;
wdev_ref.connecting = false;
wdev_ref.connected = false;
wdev_ref.last_reason = reason;
wdev_ref.locally_generated = locally_generated;
wdev_ref.has_bssid = false;
wdev_ref.last_bssid = [0; 6];
}
}
netif_carrier_off(dev.cast::<NetDevice>());
}
#[no_mangle]
pub extern "C" fn cfg80211_connect_bss(
dev: *mut c_void,
bssid: *const u8,
req_ie: *const u8,
req_ie_len: usize,
resp_ie: *const u8,
resp_ie_len: usize,
status: u16,
gfp: u32,
) {
cfg80211_connect_result(
dev,
bssid,
req_ie,
req_ie_len,
resp_ie,
resp_ie_len,
status,
gfp,
)
}
#[no_mangle]
pub extern "C" fn cfg80211_ready_on_channel(
_wdev: *mut WirelessDev,
_cookie: u64,
_chan: *mut c_void,
_chan_type: u32,
_duration: u32,
_gfp: u32,
) {
}
#[cfg(test)]
mod tests {
use super::*;
use crate::rust_impl::net::{alloc_netdev_mqs, free_netdev, netif_carrier_ok};
use std::ffi::CString;
#[test]
fn wiphy_registration_round_trip_works() {
let wiphy = wiphy_new_nm(ptr::null(), 0, ptr::null());
assert!(!wiphy.is_null());
assert_eq!(wiphy_register(wiphy), 0);
assert_eq!(wiphy_register(wiphy), -16);
assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 1);
wiphy_unregister(wiphy);
assert_eq!(unsafe { (*wiphy).registered.load(Ordering::Acquire) }, 0);
wiphy_free(wiphy);
}
#[test]
fn scan_and_connect_lifecycle_updates_wireless_state() {
let name = CString::new("wlan%d").unwrap();
let dev = alloc_netdev_mqs(0, name.as_ptr().cast::<u8>(), 0, None, 1, 1);
assert!(!dev.is_null());
let wiphy = wiphy_new_nm(ptr::null(), 32, ptr::null());
assert!(!wiphy.is_null());
let mut wdev = WirelessDev {
wiphy,
netdev: dev.cast::<c_void>(),
iftype: 0,
scan_in_flight: true,
scan_aborted: false,
connecting: true,
connected: false,
locally_generated: false,
last_status: u16::MAX,
last_reason: 0,
has_bssid: false,
last_bssid: [0; 6],
};
unsafe {
(*dev).ieee80211_ptr = (&mut wdev as *mut WirelessDev).cast::<c_void>();
}
let mut request = Cfg80211ScanRequest {
wiphy,
wdev: &mut wdev,
n_ssids: 1,
n_channels: 1,
};
let info = Cfg80211ScanInfo { aborted: true };
cfg80211_scan_done(&mut request, &info);
assert!(!wdev.scan_in_flight);
assert!(wdev.scan_aborted);
let bssid = [1, 2, 3, 4, 5, 6];
cfg80211_connect_result(
dev.cast::<c_void>(),
bssid.as_ptr(),
ptr::null(),
0,
ptr::null(),
0,
0,
0,
);
assert!(wdev.connected);
assert_eq!(wdev.last_status, 0);
assert!(wdev.has_bssid);
assert_eq!(wdev.last_bssid, bssid);
assert_eq!(netif_carrier_ok(dev), 1);
cfg80211_disconnected(dev.cast::<c_void>(), 7, ptr::null(), 0, true, 0);
assert!(!wdev.connected);
assert_eq!(wdev.last_reason, 7);
assert!(wdev.locally_generated);
assert_eq!(netif_carrier_ok(dev), 0);
wiphy_free(wiphy);
free_netdev(dev);
}
}
@@ -0,0 +1,29 @@
#[cfg(all(test, not(target_os = "redox")))]
#[no_mangle]
pub extern "C" fn redox_open_v1(
_path_base: *const u8,
_path_len: usize,
_flags: u32,
_mode: u16,
) -> usize {
usize::MAX
}
#[cfg(all(test, not(target_os = "redox")))]
#[no_mangle]
pub extern "C" fn redox_close_v1(_fd: usize) -> usize {
0
}
#[cfg(all(test, not(target_os = "redox")))]
#[no_mangle]
pub extern "C" fn redox_sys_call_v0(
_fd: usize,
_payload: *mut u8,
_payload_len: usize,
_flags: usize,
_metadata: *const u64,
_metadata_len: usize,
) -> usize {
usize::MAX
}