forked from M-Labs/artiq
Rust: implement bindings for lwip TCP/UDP stacks.
This commit is contained in:
parent
5d293d14c6
commit
bf863053b3
|
@ -2,7 +2,8 @@
|
||||||
name = "runtime"
|
name = "runtime"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fringe 0.0.1 (git+https://github.com/whitequark/libfringe)",
|
"fringe 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lwip 0.0.0",
|
||||||
"std_artiq 0.0.0",
|
"std_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -12,21 +13,10 @@ version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fringe"
|
name = "fringe"
|
||||||
version = "0.0.1"
|
version = "1.0.5"
|
||||||
source = "git+https://github.com/whitequark/libfringe#48512d0e4ecc913be7a56f94825ba856c5a1e8aa"
|
|
||||||
dependencies = [
|
|
||||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kernel32-sys"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -34,6 +24,18 @@ name = "libc"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lwip"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"lwip-sys 0.0.0",
|
||||||
|
"std_artiq 0.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lwip-sys"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "std_artiq"
|
name = "std_artiq"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -41,19 +43,6 @@ dependencies = [
|
||||||
"alloc_artiq 0.0.0",
|
"alloc_artiq 0.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-build"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum fringe 0.0.1 (git+https://github.com/whitequark/libfringe)" = "<none>"
|
"checksum fringe 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c5257685076edffc8f1a85a382135cdf27ba915ac74e47ae55aba19945c17955"
|
||||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
|
||||||
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
|
||||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
|
||||||
|
|
|
@ -10,11 +10,8 @@ path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
std_artiq = { path = "libstd_artiq" }
|
std_artiq = { path = "libstd_artiq" }
|
||||||
|
fringe = { version = "1.0.5", default-features = false, features = ["alloc"] }
|
||||||
[dependencies.fringe]
|
lwip = { path = "liblwip" }
|
||||||
git = "https://github.com/whitequark/libfringe"
|
|
||||||
default-features = false
|
|
||||||
features = ["alloc"]
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
authors = ["The ARTIQ Project Developers"]
|
||||||
|
name = "lwip-sys"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "lwip_sys"
|
||||||
|
path = "lib.rs"
|
|
@ -0,0 +1,160 @@
|
||||||
|
#![no_std]
|
||||||
|
#![feature(libc)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
pub use err::*;
|
||||||
|
pub use pbuf_layer::*;
|
||||||
|
pub use pbuf_type::*;
|
||||||
|
pub use ip_addr_type::*;
|
||||||
|
|
||||||
|
use libc::{c_void, c_int};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(i8)]
|
||||||
|
pub enum err {
|
||||||
|
ERR_OK = 0,
|
||||||
|
ERR_MEM = -1,
|
||||||
|
ERR_BUF = -2,
|
||||||
|
ERR_TIMEOUT = -3,
|
||||||
|
ERR_RTE = -4,
|
||||||
|
ERR_INPROGRESS = -5,
|
||||||
|
ERR_VAL = -6,
|
||||||
|
ERR_WOULDBLOCK = -7,
|
||||||
|
ERR_USE = -8,
|
||||||
|
ERR_ALREADY = -9,
|
||||||
|
ERR_ISCONN = -10,
|
||||||
|
ERR_CONN = -11,
|
||||||
|
ERR_IF = -12,
|
||||||
|
ERR_ABRT = -13,
|
||||||
|
ERR_RST = -14,
|
||||||
|
ERR_CLSD = -15,
|
||||||
|
ERR_ARG = -16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum pbuf_layer {
|
||||||
|
PBUF_TRANSPORT,
|
||||||
|
PBUF_IP,
|
||||||
|
PBUF_LINK,
|
||||||
|
PBUF_RAW_TX,
|
||||||
|
PBUF_RAW
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum pbuf_type {
|
||||||
|
PBUF_RAM,
|
||||||
|
PBUF_ROM,
|
||||||
|
PBUF_REF,
|
||||||
|
PBUF_POOL,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ip_addr_type {
|
||||||
|
IPADDR_TYPE_V4 = 0,
|
||||||
|
IPADDR_TYPE_V6 = 6,
|
||||||
|
IPADDR_TYPE_ANY = 46,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct pbuf {
|
||||||
|
pub next: *mut pbuf,
|
||||||
|
pub payload: *mut c_void,
|
||||||
|
pub tot_len: u16,
|
||||||
|
pub len: u16,
|
||||||
|
pub type_: pbuf_type,
|
||||||
|
pub flags: u8,
|
||||||
|
pub ref_: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ip4_addr {
|
||||||
|
pub addr: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ip6_addr {
|
||||||
|
pub addr: [u32; 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ip_addr {
|
||||||
|
pub data: [u32; 4],
|
||||||
|
pub type_: ip_addr_type
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct tcp_pcb {
|
||||||
|
__opaque: c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct udp_pcb {
|
||||||
|
__opaque: c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const TCP_WRITE_FLAG_COPY: u8 = 0x01;
|
||||||
|
pub const TCP_WRITE_FLAG_MORE: u8 = 0x02;
|
||||||
|
|
||||||
|
extern {
|
||||||
|
pub fn pbuf_alloc(l: pbuf_layer, length: u16, type_: pbuf_type) -> *mut pbuf;
|
||||||
|
pub fn pbuf_realloc(p: *mut pbuf, length: u16);
|
||||||
|
pub fn pbuf_ref(p: *mut pbuf);
|
||||||
|
pub fn pbuf_free(p: *mut pbuf);
|
||||||
|
pub fn pbuf_cat(head: *mut pbuf, tail: *mut pbuf);
|
||||||
|
pub fn pbuf_chain(head: *mut pbuf, tail: *mut pbuf);
|
||||||
|
pub fn pbuf_dechain(p: *mut pbuf) -> *mut pbuf;
|
||||||
|
pub fn pbuf_copy(p_to: *mut pbuf, p_from: *mut pbuf) -> err;
|
||||||
|
pub fn pbuf_copy_partial(p: *mut pbuf, dataptr: *mut c_void, len: u16, offset: u16) -> u16;
|
||||||
|
pub fn pbuf_take(p: *mut pbuf, dataptr: *const c_void, len: u16) -> err;
|
||||||
|
pub fn pbuf_take_at(p: *mut pbuf, dataptr: *const c_void, len: u16, offset: u16) -> err;
|
||||||
|
pub fn pbuf_skip(in_: *mut pbuf, in_offset: u16, out_offset: *mut u16) -> *mut pbuf;
|
||||||
|
|
||||||
|
pub fn tcp_new() -> *mut tcp_pcb;
|
||||||
|
pub fn tcp_arg(pcb: *mut tcp_pcb, arg: *mut c_void);
|
||||||
|
pub fn tcp_bind(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||||
|
pub fn tcp_listen_with_backlog(pcb: *mut tcp_pcb, backlog: u8) -> *mut tcp_pcb;
|
||||||
|
pub fn tcp_accept(pcb: *mut tcp_pcb,
|
||||||
|
accept: extern fn(arg: *mut c_void, newpcb: *mut tcp_pcb,
|
||||||
|
err: err) -> err);
|
||||||
|
pub fn tcp_connect(pcb: *mut tcp_pcb, ipaddr: *mut ip_addr, port: u16,
|
||||||
|
connected: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, err: err)) -> err;
|
||||||
|
pub fn tcp_write(pcb: *mut tcp_pcb, dataptr: *const c_void, len: u16, apiflags: u8) -> err;
|
||||||
|
pub fn tcp_sent(pcb: *mut tcp_pcb,
|
||||||
|
sent: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, len: u16) -> err);
|
||||||
|
pub fn tcp_recv(pcb: *mut tcp_pcb,
|
||||||
|
recv: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb, p: *mut pbuf,
|
||||||
|
err: err) -> err);
|
||||||
|
pub fn tcp_recved(pcb: *mut tcp_pcb, len: u16);
|
||||||
|
pub fn tcp_poll(pcb: *mut tcp_pcb,
|
||||||
|
poll: extern fn(arg: *mut c_void, tcb: *mut tcp_pcb),
|
||||||
|
interval: u8);
|
||||||
|
pub fn tcp_shutdown(pcb: *mut tcp_pcb, shut_rx: c_int, shut_tx: c_int) -> err;
|
||||||
|
pub fn tcp_close(pcb: *mut tcp_pcb) -> err;
|
||||||
|
pub fn tcp_abort(pcb: *mut tcp_pcb);
|
||||||
|
pub fn tcp_err(pcb: *mut tcp_pcb,
|
||||||
|
err: extern fn(arg: *mut c_void, err: err));
|
||||||
|
|
||||||
|
// nonstandard
|
||||||
|
pub fn tcp_sndbuf_(pcb: *mut tcp_pcb) -> u16;
|
||||||
|
|
||||||
|
pub fn udp_new() -> *mut udp_pcb;
|
||||||
|
pub fn udp_new_ip_type(type_: ip_addr_type) -> *mut udp_pcb;
|
||||||
|
pub fn udp_remove(pcb: *mut udp_pcb);
|
||||||
|
pub fn udp_bind(pcb: *mut udp_pcb, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||||
|
pub fn udp_connect(pcb: *mut udp_pcb, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||||
|
pub fn udp_disconnect(pcb: *mut udp_pcb) -> err;
|
||||||
|
pub fn udp_send(pcb: *mut udp_pcb, p: *mut pbuf) -> err;
|
||||||
|
pub fn udp_sendto(pcb: *mut udp_pcb, p: *mut pbuf, ipaddr: *mut ip_addr, port: u16) -> err;
|
||||||
|
pub fn udp_recv(pcb: *mut udp_pcb,
|
||||||
|
recv: extern fn(arg: *mut c_void, upcb: *mut udp_pcb, p: *mut pbuf,
|
||||||
|
addr: *mut ip_addr, port: u16),
|
||||||
|
recv_arg: *mut c_void);
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
authors = ["The ARTIQ Project Developers"]
|
||||||
|
name = "lwip"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "lwip"
|
||||||
|
path = "lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lwip-sys = { path = "../liblwip-sys" }
|
||||||
|
std_artiq = { path = "../libstd_artiq" }
|
|
@ -0,0 +1,410 @@
|
||||||
|
#![feature(alloc, collections, libc)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate collections;
|
||||||
|
extern crate libc;
|
||||||
|
extern crate lwip_sys;
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use collections::LinkedList;
|
||||||
|
use libc::c_void;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Error {
|
||||||
|
OutOfMemory,
|
||||||
|
Buffer,
|
||||||
|
Timeout,
|
||||||
|
Routing,
|
||||||
|
InProgress,
|
||||||
|
IllegalValue,
|
||||||
|
WouldBlock,
|
||||||
|
AddressInUse,
|
||||||
|
AlreadyConnecting,
|
||||||
|
AlreadyConnected,
|
||||||
|
NotConnected,
|
||||||
|
Interface,
|
||||||
|
ConnectionAborted,
|
||||||
|
ConnectionReset,
|
||||||
|
ConnectionClosed,
|
||||||
|
IllegalArgument,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
|
fn result_from<T, F>(err: lwip_sys::err, f: F) -> Result<T>
|
||||||
|
where F: FnOnce() -> T {
|
||||||
|
match err {
|
||||||
|
lwip_sys::ERR_OK => Ok(f()),
|
||||||
|
lwip_sys::ERR_MEM => Err(Error::OutOfMemory),
|
||||||
|
lwip_sys::ERR_BUF => Err(Error::Buffer),
|
||||||
|
lwip_sys::ERR_TIMEOUT => Err(Error::Timeout),
|
||||||
|
lwip_sys::ERR_RTE => Err(Error::Routing),
|
||||||
|
lwip_sys::ERR_INPROGRESS => Err(Error::InProgress),
|
||||||
|
lwip_sys::ERR_VAL => Err(Error::IllegalValue),
|
||||||
|
lwip_sys::ERR_WOULDBLOCK => Err(Error::WouldBlock),
|
||||||
|
lwip_sys::ERR_USE => Err(Error::AddressInUse),
|
||||||
|
lwip_sys::ERR_ALREADY => Err(Error::AlreadyConnecting),
|
||||||
|
lwip_sys::ERR_ISCONN => Err(Error::AlreadyConnected),
|
||||||
|
lwip_sys::ERR_CONN => Err(Error::NotConnected),
|
||||||
|
lwip_sys::ERR_IF => Err(Error::Interface),
|
||||||
|
lwip_sys::ERR_ABRT => Err(Error::ConnectionAborted),
|
||||||
|
lwip_sys::ERR_RST => Err(Error::ConnectionReset),
|
||||||
|
lwip_sys::ERR_CLSD => Err(Error::ConnectionClosed),
|
||||||
|
lwip_sys::ERR_ARG => Err(Error::IllegalArgument),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum IpAddr {
|
||||||
|
Ip4([u8; 4]),
|
||||||
|
Ip6([u16; 8])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const IP4_ANY: IpAddr = IpAddr::Ip4([0, 0, 0, 0]);
|
||||||
|
pub const IP6_ANY: IpAddr = IpAddr::Ip6([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
|
||||||
|
impl IpAddr {
|
||||||
|
fn into_raw(self) -> lwip_sys::ip_addr {
|
||||||
|
match self {
|
||||||
|
IpAddr::Ip4(ref octets) =>
|
||||||
|
lwip_sys::ip_addr {
|
||||||
|
data: [(octets[0] as u32) << 24 |
|
||||||
|
(octets[1] as u32) << 16 |
|
||||||
|
(octets[2] as u32) << 8 |
|
||||||
|
(octets[3] as u32) << 0,
|
||||||
|
0, 0, 0],
|
||||||
|
type_: lwip_sys::IPADDR_TYPE_V4
|
||||||
|
},
|
||||||
|
IpAddr::Ip6(ref segments) =>
|
||||||
|
lwip_sys::ip_addr {
|
||||||
|
data: [(segments[0] as u32) << 16 | (segments[1] as u32),
|
||||||
|
(segments[2] as u32) << 16 | (segments[3] as u32),
|
||||||
|
(segments[4] as u32) << 16 | (segments[5] as u32),
|
||||||
|
(segments[6] as u32) << 16 | (segments[7] as u32)],
|
||||||
|
type_: lwip_sys::IPADDR_TYPE_V6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_raw(raw: *mut lwip_sys::ip_addr) -> IpAddr {
|
||||||
|
match *raw {
|
||||||
|
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V4, data } =>
|
||||||
|
IpAddr::Ip4([(data[0] >> 24) as u8,
|
||||||
|
(data[0] >> 16) as u8,
|
||||||
|
(data[0] >> 8) as u8,
|
||||||
|
(data[0] >> 0) as u8]),
|
||||||
|
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_V6, data } =>
|
||||||
|
IpAddr::Ip6([(data[0] >> 16) as u16, data[0] as u16,
|
||||||
|
(data[1] >> 16) as u16, data[1] as u16,
|
||||||
|
(data[2] >> 16) as u16, data[2] as u16,
|
||||||
|
(data[3] >> 16) as u16, data[3] as u16]),
|
||||||
|
_ => panic!("unknown IP address type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct SocketAddr {
|
||||||
|
pub ip: IpAddr,
|
||||||
|
pub port: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocketAddr {
|
||||||
|
pub fn new(ip: IpAddr, port: u16) -> SocketAddr {
|
||||||
|
SocketAddr { ip: ip, port: port }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pbuf<'payload> {
|
||||||
|
raw: *mut lwip_sys::pbuf,
|
||||||
|
phantom: PhantomData<&'payload [u8]>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'payload> Pbuf<'payload> {
|
||||||
|
unsafe fn from_raw(raw: *mut lwip_sys::pbuf) -> Pbuf<'payload> {
|
||||||
|
Pbuf { raw: raw, phantom: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_raw(&self) -> *mut lwip_sys::pbuf {
|
||||||
|
self.raw
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn into_raw(self) -> *mut lwip_sys::pbuf {
|
||||||
|
let raw = self.raw;
|
||||||
|
core::mem::forget(self);
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_slice_with_type<'a>(slice: &'a [u8], type_: lwip_sys::pbuf_type) -> Pbuf<'a> {
|
||||||
|
assert!(slice.len() <= core::u16::MAX as usize);
|
||||||
|
unsafe {
|
||||||
|
let raw = lwip_sys::pbuf_alloc(lwip_sys::PBUF_RAW, slice.len() as u16, type_);
|
||||||
|
(*raw).payload = slice.as_ptr() as *mut u8 as *mut c_void;
|
||||||
|
Pbuf { raw: raw, phantom: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice(slice: &'payload [u8]) -> Pbuf<'payload> {
|
||||||
|
Self::from_slice_with_type(slice, lwip_sys::PBUF_REF)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_static_slice(slice: &'static [u8]) -> Pbuf<'static> {
|
||||||
|
// Avoids a copy.
|
||||||
|
Self::from_slice_with_type(slice, lwip_sys::PBUF_ROM)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_slice(&self) -> &'payload [u8] {
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts((*self.raw).payload as *const u8,
|
||||||
|
(*self.raw).len as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_slice(&mut self) -> &'payload mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
core::slice::from_raw_parts_mut((*self.raw).payload as *mut u8,
|
||||||
|
(*self.raw).len as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn concat(&mut self, tail: Pbuf<'payload>) {
|
||||||
|
unsafe { lwip_sys::pbuf_cat(self.raw, tail.raw) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chain(&mut self, tail: &mut Pbuf<'payload>) {
|
||||||
|
unsafe { lwip_sys::pbuf_chain(self.raw, tail.raw) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for Pbuf<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { lwip_sys::pbuf_free(self.raw) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UdpSocket {
|
||||||
|
raw: *mut lwip_sys::udp_pcb,
|
||||||
|
buffer: Box<LinkedList<(SocketAddr, Pbuf<'static>)>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UdpSocket {
|
||||||
|
pub fn new() -> Result<UdpSocket> {
|
||||||
|
extern fn recv(arg: *mut c_void, _pcb: *mut lwip_sys::udp_pcb,
|
||||||
|
pbuf: *mut lwip_sys::pbuf,
|
||||||
|
addr: *mut lwip_sys::ip_addr, port: u16) {
|
||||||
|
unsafe {
|
||||||
|
let buffer = arg as *mut LinkedList<(SocketAddr, Pbuf)>;
|
||||||
|
let socket_addr = SocketAddr { ip: IpAddr::from_raw(addr), port: port };
|
||||||
|
(*buffer).push_back((socket_addr, Pbuf::from_raw(pbuf)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let raw = lwip_sys::udp_new();
|
||||||
|
if raw.is_null() { return Err(Error::OutOfMemory) }
|
||||||
|
|
||||||
|
let mut buffer = Box::new(LinkedList::new());
|
||||||
|
let arg = &mut *buffer as *mut LinkedList<(SocketAddr, Pbuf)> as *mut _;
|
||||||
|
lwip_sys::udp_recv(raw, recv, arg);
|
||||||
|
Ok(UdpSocket { raw: raw, buffer: buffer })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind(&mut self, addr: SocketAddr) -> Result<()> {
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::udp_bind(self.raw, &mut addr.ip.into_raw(), addr.port)
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect(&mut self, addr: SocketAddr) -> Result<()> {
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::udp_connect(self.raw, &mut addr.ip.into_raw(), addr.port)
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disconnect(&mut self) -> Result<()> {
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::udp_disconnect(self.raw)
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send<'a>(&'a mut self, pbuf: Pbuf<'a>) -> Result<()> {
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::udp_send(self.raw, pbuf.as_raw())
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_to<'a>(&'a mut self, addr: SocketAddr, pbuf: Pbuf<'a>) -> Result<()> {
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::udp_sendto(self.raw, pbuf.as_raw(),
|
||||||
|
&mut addr.ip.into_raw(), addr.port)
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_recv(&mut self) -> Option<(SocketAddr, Pbuf<'static>)> {
|
||||||
|
self.buffer.pop_front()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for UdpSocket {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { lwip_sys::udp_remove(self.raw) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TcpListener {
|
||||||
|
raw: *mut lwip_sys::tcp_pcb,
|
||||||
|
backlog: Box<LinkedList<TcpStream>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpListener {
|
||||||
|
pub fn bind(addr: SocketAddr) -> Result<TcpListener> {
|
||||||
|
extern fn accept(arg: *mut c_void, newpcb: *mut lwip_sys::tcp_pcb,
|
||||||
|
err: lwip_sys::err) -> lwip_sys::err {
|
||||||
|
if err != lwip_sys::ERR_OK { return err }
|
||||||
|
unsafe {
|
||||||
|
let backlog = arg as *mut LinkedList<TcpStream>;
|
||||||
|
(*backlog).push_back(TcpStream::from_raw(newpcb));
|
||||||
|
}
|
||||||
|
lwip_sys::ERR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let raw = lwip_sys::tcp_new();
|
||||||
|
if raw.is_null() { return Err(Error::OutOfMemory) }
|
||||||
|
|
||||||
|
let mut backlog = Box::new(LinkedList::new());
|
||||||
|
let arg = &mut *backlog as *mut LinkedList<TcpStream> as *mut _;
|
||||||
|
lwip_sys::tcp_arg(raw, arg);
|
||||||
|
try!(result_from(lwip_sys::tcp_bind(raw, &mut addr.ip.into_raw(), addr.port),
|
||||||
|
|| ()));
|
||||||
|
|
||||||
|
let raw2 = lwip_sys::tcp_listen_with_backlog(raw, 0xff);
|
||||||
|
if raw2.is_null() {
|
||||||
|
lwip_sys::tcp_abort(raw);
|
||||||
|
return Err(Error::OutOfMemory)
|
||||||
|
}
|
||||||
|
lwip_sys::tcp_accept(raw2, accept);
|
||||||
|
Ok(TcpListener { raw: raw2, backlog: backlog })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_accept(&mut self) -> Option<TcpStream> {
|
||||||
|
self.backlog.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) {
|
||||||
|
// just drop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TcpListener {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// tcp_close never fails on listening sockets
|
||||||
|
let _ = lwip_sys::tcp_close(self.raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum Shutdown {
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TcpStream {
|
||||||
|
raw: *mut lwip_sys::tcp_pcb,
|
||||||
|
buffer: Box<LinkedList<Result<Pbuf<'static>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TcpStream {
|
||||||
|
fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream {
|
||||||
|
extern fn recv(arg: *mut c_void, _tcb: *mut lwip_sys::tcp_pcb,
|
||||||
|
pbuf: *mut lwip_sys::pbuf, err: lwip_sys::err) -> lwip_sys::err {
|
||||||
|
if err != lwip_sys::ERR_OK { return err }
|
||||||
|
unsafe {
|
||||||
|
let buffer = arg as *mut LinkedList<Result<Pbuf<'static>>>;
|
||||||
|
if pbuf.is_null() {
|
||||||
|
(*buffer).push_back(Err(Error::ConnectionClosed))
|
||||||
|
} else {
|
||||||
|
(*buffer).push_back(Ok(Pbuf::from_raw(pbuf)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lwip_sys::ERR_OK
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn err(arg: *mut c_void, err: lwip_sys::err) {
|
||||||
|
unsafe {
|
||||||
|
let buffer = arg as *mut LinkedList<Result<Pbuf<'static>>>;
|
||||||
|
(*buffer).push_back(result_from(err, || unreachable!()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut buffer = Box::new(LinkedList::new());
|
||||||
|
let arg = &mut *buffer as *mut LinkedList<Result<Pbuf<'static>>> as *mut _;
|
||||||
|
lwip_sys::tcp_arg(raw, arg);
|
||||||
|
lwip_sys::tcp_recv(raw, recv);
|
||||||
|
lwip_sys::tcp_err(raw, err);
|
||||||
|
TcpStream { raw: raw, buffer: buffer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, data: &[u8]) -> Result<usize> {
|
||||||
|
let sndbuf = unsafe { lwip_sys::tcp_sndbuf_(self.raw) } as usize;
|
||||||
|
let len = if data.len() < sndbuf { data.len() } else { sndbuf };
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16,
|
||||||
|
lwip_sys::TCP_WRITE_FLAG_COPY)
|
||||||
|
}, || len)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_read(&mut self) -> Result<Option<Pbuf<'static>>> {
|
||||||
|
match self.buffer.front() {
|
||||||
|
None => return Ok(None),
|
||||||
|
Some(&Err(err)) => return Err(err),
|
||||||
|
Some(_) => ()
|
||||||
|
}
|
||||||
|
match self.buffer.pop_front() {
|
||||||
|
Some(Ok(pbuf)) => return Ok(Some(pbuf)),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&mut self, how: Shutdown) -> Result<()> {
|
||||||
|
let (shut_rx, shut_tx) = match how {
|
||||||
|
Shutdown::Read => (1, 0),
|
||||||
|
Shutdown::Write => (0, 1),
|
||||||
|
Shutdown::Both => (1, 1)
|
||||||
|
};
|
||||||
|
result_from(unsafe {
|
||||||
|
lwip_sys::tcp_shutdown(self.raw, shut_rx, shut_tx)
|
||||||
|
}, || ())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self) -> Result<()> {
|
||||||
|
let result = result_from(unsafe {
|
||||||
|
lwip_sys::tcp_close(self.raw)
|
||||||
|
}, || ());
|
||||||
|
core::mem::forget(self); // closing twice is illegal
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TcpStream {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
// tcp_close can fail here, but in drop() we don't care
|
||||||
|
let _ = lwip_sys::tcp_close(self.raw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,19 @@
|
||||||
#![feature(lang_items, asm, collections, libc, needs_panic_runtime)]
|
#![feature(lang_items, asm, alloc, collections, libc, needs_panic_runtime)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![needs_panic_runtime]
|
#![needs_panic_runtime]
|
||||||
|
|
||||||
extern crate alloc_artiq;
|
extern crate alloc_artiq;
|
||||||
|
extern crate alloc;
|
||||||
extern crate collections;
|
extern crate collections;
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
|
||||||
|
pub use core::{any, cell, clone, cmp, convert, default, hash, iter, marker, mem, num,
|
||||||
|
ops, option, ptr, result, sync,
|
||||||
|
char, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize, f32, f64};
|
||||||
|
pub use alloc::{arc, rc, oom, raw_vec};
|
||||||
|
pub use collections::{binary_heap, borrow, boxed, btree_map, btree_set, fmt, linked_list, slice,
|
||||||
|
str, string, vec, vec_deque};
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
pub use core::prelude::v1::*;
|
pub use core::prelude::v1::*;
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
extern crate fringe;
|
extern crate fringe;
|
||||||
|
|
||||||
use std::prelude::v1::*;
|
use std::vec::Vec;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use self::fringe::OwnedStack;
|
use self::fringe::OwnedStack;
|
||||||
use self::fringe::generator::{Generator, Yielder};
|
use self::fringe::generator::{Generator, Yielder};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WaitRequest {
|
struct WaitRequest {
|
||||||
timeout: Option<Instant>,
|
timeout: Option<Instant>,
|
||||||
event: Option<WaitEvent>
|
event: Option<WaitEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WaitResult {
|
enum WaitResult {
|
||||||
Completed,
|
Completed,
|
||||||
TimedOut,
|
TimedOut,
|
||||||
Interrupted
|
Interrupted
|
||||||
|
@ -36,11 +36,11 @@ impl Scheduler {
|
||||||
Scheduler { threads: Vec::new(), index: 0 }
|
Scheduler { threads: Vec::new(), index: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn spawn<F: FnOnce(Io) + Send>(&mut self, stack_size: usize, f: F) {
|
pub unsafe fn spawn<F: FnOnce(Waiter) + Send>(&mut self, stack_size: usize, f: F) {
|
||||||
let stack = OwnedStack::new(stack_size);
|
let stack = OwnedStack::new(stack_size);
|
||||||
let thread = Thread {
|
let thread = Thread {
|
||||||
generator: Generator::unsafe_new(stack, move |yielder, _| {
|
generator: Generator::unsafe_new(stack, move |yielder, _| {
|
||||||
f(Io(yielder))
|
f(Waiter(yielder))
|
||||||
}),
|
}),
|
||||||
waiting_for: WaitRequest {
|
waiting_for: WaitRequest {
|
||||||
timeout: None,
|
timeout: None,
|
||||||
|
@ -102,7 +102,7 @@ impl Scheduler {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WaitEvent {}
|
enum WaitEvent {}
|
||||||
|
|
||||||
impl WaitEvent {
|
impl WaitEvent {
|
||||||
fn completed(&self) -> bool {
|
fn completed(&self) -> bool {
|
||||||
|
@ -110,13 +110,13 @@ impl WaitEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type IoResult<T> = Result<T, ()>;
|
pub type Result<T> = ::std::result::Result<T, ()>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Io<'a>(&'a mut Yielder<WaitResult, WaitRequest, OwnedStack>);
|
pub struct Waiter<'a>(&'a mut Yielder<WaitResult, WaitRequest, OwnedStack>);
|
||||||
|
|
||||||
impl<'a> Io<'a> {
|
impl<'a> Waiter<'a> {
|
||||||
pub fn sleep(&mut self, duration: Duration) -> IoResult<()> {
|
pub fn sleep(&mut self, duration: Duration) -> Result<()> {
|
||||||
let request = WaitRequest {
|
let request = WaitRequest {
|
||||||
timeout: Some(Instant::now() + duration),
|
timeout: Some(Instant::now() + duration),
|
||||||
event: None
|
event: None
|
|
@ -2,29 +2,50 @@
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate std_artiq as std;
|
extern crate std_artiq as std;
|
||||||
|
extern crate fringe;
|
||||||
|
extern crate lwip;
|
||||||
|
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::time::Duration;
|
|
||||||
use scheduler::Scheduler;
|
|
||||||
|
|
||||||
pub mod scheduler;
|
pub mod io;
|
||||||
|
|
||||||
|
extern {
|
||||||
|
fn network_init();
|
||||||
|
fn lwip_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test1(mut waiter: io::Waiter) {
|
||||||
|
loop {
|
||||||
|
println!("A");
|
||||||
|
waiter.sleep(std::time::Duration::from_millis(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test2(mut waiter: io::Waiter) {
|
||||||
|
loop {
|
||||||
|
println!("B");
|
||||||
|
waiter.sleep(std::time::Duration::from_millis(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn rust_main() {
|
pub unsafe extern fn rust_main() {
|
||||||
// let mut scheduler = Scheduler::new();
|
println!("Accepting network sessions in Rust.");
|
||||||
// unsafe {
|
network_init();
|
||||||
// scheduler.spawn(4096, move |mut io| {
|
|
||||||
// loop {
|
let addr = lwip::SocketAddr::new(lwip::IP4_ANY, 1234);
|
||||||
// println!("thread A");
|
let mut listener = lwip::TcpListener::bind(addr).unwrap();
|
||||||
// io.sleep(Duration::from_secs(1)).unwrap()
|
let mut stream = None;
|
||||||
// }
|
loop {
|
||||||
// });
|
lwip_service();
|
||||||
// scheduler.spawn(4096, move |mut io| {
|
if let Some(new_stream) = listener.try_accept() {
|
||||||
// loop {
|
stream = Some(new_stream)
|
||||||
// println!("thread B");
|
}
|
||||||
// io.sleep(Duration::from_millis(333)).unwrap()
|
if let Some(ref mut stream) = stream {
|
||||||
// }
|
if let Some(pbuf) = stream.try_read().expect("read") {
|
||||||
// });
|
println!("{:?}", pbuf.as_slice());
|
||||||
// }
|
stream.write(pbuf.as_slice()).expect("write");
|
||||||
// loop { scheduler.run() }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ static struct netif netif;
|
||||||
static ppp_pcb *ppp;
|
static ppp_pcb *ppp;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void lwip_service(void)
|
void lwip_service(void)
|
||||||
{
|
{
|
||||||
sys_check_timeouts();
|
sys_check_timeouts();
|
||||||
#ifdef CSR_ETHMAC_BASE
|
#ifdef CSR_ETHMAC_BASE
|
||||||
|
@ -126,7 +126,7 @@ static void fsip_or_default(struct ip4_addr *d, char *key, int i1, int i2, int i
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void network_init(void)
|
void network_init(void)
|
||||||
{
|
{
|
||||||
struct ip4_addr local_ip;
|
struct ip4_addr local_ip;
|
||||||
struct ip4_addr netmask;
|
struct ip4_addr netmask;
|
||||||
|
@ -167,7 +167,7 @@ static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void network_init(void)
|
void network_init(void)
|
||||||
{
|
{
|
||||||
lwip_init();
|
lwip_init();
|
||||||
|
|
||||||
|
@ -264,6 +264,10 @@ extern void _fheap, _eheap;
|
||||||
|
|
||||||
extern void rust_main();
|
extern void rust_main();
|
||||||
|
|
||||||
|
u16_t tcp_sndbuf_(struct tcp_pcb *pcb) {
|
||||||
|
return tcp_sndbuf(pcb);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
irq_setmask(0);
|
irq_setmask(0);
|
||||||
|
@ -278,14 +282,12 @@ int main(void)
|
||||||
puts("Press 't' to enter test mode...");
|
puts("Press 't' to enter test mode...");
|
||||||
blink_led();
|
blink_led();
|
||||||
|
|
||||||
puts("Calling Rust...");
|
|
||||||
rust_main();
|
|
||||||
|
|
||||||
if(check_test_mode()) {
|
if(check_test_mode()) {
|
||||||
puts("Entering test mode.");
|
puts("Entering test mode.");
|
||||||
test_main();
|
test_main();
|
||||||
} else {
|
} else {
|
||||||
puts("Entering regular mode.");
|
puts("Entering regular mode.");
|
||||||
|
// rust_main();
|
||||||
session_startup_kernel();
|
session_startup_kernel();
|
||||||
regular_main();
|
regular_main();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue