Add linux-kpi wireless compat layer
Red Bear OS Team
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user