Skip to main content

strat9_kernel/hardware/nic/
scheme.rs

1//! Network VFS scheme – mounts at `/dev/net/`.
2//!
3//! read = receive packet, write = send packet.
4
5use super::{get_device, list_interfaces};
6use crate::{
7    syscall::error::SyscallError,
8    vfs::{
9        scheme::{
10            finalize_pseudo_stat, DirEntry, FileFlags, FileStat, OpenFlags, OpenResult, Scheme,
11            DEV_NETFS, DT_REG,
12        },
13        scheme_router,
14    },
15};
16use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
17use core::sync::atomic::{AtomicU64, Ordering};
18use net_core::NetError;
19use spin::RwLock;
20
21#[derive(Clone)]
22enum Handle {
23    Root,
24    Iface(String),
25}
26
27pub struct NetScheme {
28    handles: RwLock<BTreeMap<u64, Handle>>,
29    next: AtomicU64,
30}
31
32impl NetScheme {
33    /// Creates a new instance.
34    fn new() -> Self {
35        Self {
36            handles: RwLock::new(BTreeMap::new()),
37            next: AtomicU64::new(1),
38        }
39    }
40    /// Allocates id.
41    fn alloc_id(&self) -> u64 {
42        self.next.fetch_add(1, Ordering::Relaxed)
43    }
44}
45
46impl Scheme for NetScheme {
47    /// Performs the open operation.
48    fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
49        let path = path.trim_start_matches('/');
50        let id = self.alloc_id();
51        if path.is_empty() {
52            self.handles.write().insert(id, Handle::Root);
53            return Ok(OpenResult {
54                file_id: id,
55                size: None,
56                flags: FileFlags::DIRECTORY,
57            });
58        }
59        if !list_interfaces().iter().any(|n| n == path) {
60            return Err(SyscallError::BadHandle);
61        }
62        self.handles
63            .write()
64            .insert(id, Handle::Iface(String::from(path)));
65        Ok(OpenResult {
66            file_id: id,
67            size: None,
68            flags: FileFlags::DEVICE,
69        })
70    }
71
72    /// Performs the read operation.
73    fn read(&self, fid: u64, _off: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
74        let h = self.handles.read();
75        let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
76        match handle {
77            Handle::Root => {
78                let list: String = list_interfaces().into_iter().map(|n| n + "\n").collect();
79                let b = list.as_bytes();
80                let n = core::cmp::min(b.len(), buf.len());
81                buf[..n].copy_from_slice(&b[..n]);
82                Ok(n)
83            }
84            Handle::Iface(name) => {
85                let dev = get_device(name).ok_or(SyscallError::BadHandle)?;
86                drop(h);
87                dev.receive(buf).map_err(|e| match e {
88                    NetError::NoPacket => SyscallError::Again,
89                    _ => SyscallError::IoError,
90                })
91            }
92        }
93    }
94
95    /// Performs the write operation.
96    fn write(&self, fid: u64, _off: u64, buf: &[u8]) -> Result<usize, SyscallError> {
97        let h = self.handles.read();
98        let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
99        match handle {
100            Handle::Root => Err(SyscallError::PermissionDenied),
101            Handle::Iface(name) => {
102                let dev = get_device(name).ok_or(SyscallError::BadHandle)?;
103                drop(h);
104                dev.transmit(buf).map_err(|e| match e {
105                    NetError::TxQueueFull => SyscallError::Again,
106                    _ => SyscallError::IoError,
107                })?;
108                Ok(buf.len())
109            }
110        }
111    }
112
113    /// Performs the close operation.
114    fn close(&self, fid: u64) -> Result<(), SyscallError> {
115        self.handles.write().remove(&fid);
116        Ok(())
117    }
118
119    /// Performs the stat operation.
120    fn stat(&self, fid: u64) -> Result<FileStat, SyscallError> {
121        let h = self.handles.read();
122        let handle = h.get(&fid).ok_or(SyscallError::BadHandle)?;
123        Ok(match handle {
124            Handle::Root => finalize_pseudo_stat(
125                FileStat {
126                    st_ino: 0,
127                    st_mode: 0o040555,
128                    st_nlink: 2,
129                    st_size: 0,
130                    st_blksize: 1514,
131                    st_blocks: 0,
132                    ..FileStat::zeroed()
133                },
134                DEV_NETFS,
135                0,
136            ),
137            Handle::Iface(_) => finalize_pseudo_stat(
138                FileStat {
139                    st_ino: fid,
140                    st_mode: 0o020666,
141                    st_nlink: 1,
142                    st_size: 0,
143                    st_blksize: 1514,
144                    st_blocks: 0,
145                    ..FileStat::zeroed()
146                },
147                DEV_NETFS,
148                fid,
149            ),
150        })
151    }
152
153    /// Performs the readdir operation.
154    fn readdir(&self, fid: u64) -> Result<Vec<DirEntry>, SyscallError> {
155        let h = self.handles.read();
156        if !matches!(h.get(&fid), Some(Handle::Root)) {
157            return Err(SyscallError::InvalidArgument);
158        }
159        Ok(list_interfaces()
160            .into_iter()
161            .enumerate()
162            .map(|(i, name)| DirEntry {
163                ino: (i + 1) as u64,
164                file_type: DT_REG,
165                name,
166            })
167            .collect())
168    }
169}
170
171/// Performs the register net scheme operation.
172pub fn register_net_scheme() -> Result<(), SyscallError> {
173    let scheme = Arc::new(NetScheme::new());
174    scheme_router::register_scheme("net", scheme)?;
175    scheme_router::mount_scheme("net", "/dev/net")?;
176    log::info!("[net] Scheme at /dev/net/");
177    Ok(())
178}