From bf863053b3d8efa55cec6c8abe0517d5f70098c5 Mon Sep 17 00:00:00 2001 From: whitequark Date: Tue, 6 Sep 2016 16:42:13 +0000 Subject: [PATCH] Rust: implement bindings for lwip TCP/UDP stacks. --- artiq/runtime.rs/Cargo.lock | 45 +- artiq/runtime.rs/Cargo.toml | 7 +- artiq/runtime.rs/liblwip-sys/Cargo.toml | 8 + artiq/runtime.rs/liblwip-sys/lib.rs | 160 ++++++++ artiq/runtime.rs/liblwip/Cargo.toml | 12 + artiq/runtime.rs/liblwip/lib.rs | 410 +++++++++++++++++++ artiq/runtime.rs/libstd_artiq/lib.rs | 10 +- artiq/runtime.rs/src/{scheduler.rs => io.rs} | 20 +- artiq/runtime.rs/src/lib.rs | 61 ++- artiq/runtime/main.c | 14 +- 10 files changed, 677 insertions(+), 70 deletions(-) create mode 100644 artiq/runtime.rs/liblwip-sys/Cargo.toml create mode 100644 artiq/runtime.rs/liblwip-sys/lib.rs create mode 100644 artiq/runtime.rs/liblwip/Cargo.toml create mode 100644 artiq/runtime.rs/liblwip/lib.rs rename artiq/runtime.rs/src/{scheduler.rs => io.rs} (88%) diff --git a/artiq/runtime.rs/Cargo.lock b/artiq/runtime.rs/Cargo.lock index b313700a9..6324f7558 100644 --- a/artiq/runtime.rs/Cargo.lock +++ b/artiq/runtime.rs/Cargo.lock @@ -2,7 +2,8 @@ name = "runtime" version = "0.0.0" 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", ] @@ -12,21 +13,10 @@ version = "0.0.0" [[package]] name = "fringe" -version = "0.0.1" -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" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -34,6 +24,18 @@ name = "libc" version = "0.2.15" 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]] name = "std_artiq" version = "0.0.0" @@ -41,19 +43,6 @@ dependencies = [ "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] -"checksum fringe 0.0.1 (git+https://github.com/whitequark/libfringe)" = "" -"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum fringe 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c5257685076edffc8f1a85a382135cdf27ba915ac74e47ae55aba19945c17955" "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" diff --git a/artiq/runtime.rs/Cargo.toml b/artiq/runtime.rs/Cargo.toml index cae587199..c9a0de84f 100644 --- a/artiq/runtime.rs/Cargo.toml +++ b/artiq/runtime.rs/Cargo.toml @@ -10,11 +10,8 @@ path = "src/lib.rs" [dependencies] std_artiq = { path = "libstd_artiq" } - -[dependencies.fringe] -git = "https://github.com/whitequark/libfringe" -default-features = false -features = ["alloc"] +fringe = { version = "1.0.5", default-features = false, features = ["alloc"] } +lwip = { path = "liblwip" } [profile.dev] panic = 'abort' diff --git a/artiq/runtime.rs/liblwip-sys/Cargo.toml b/artiq/runtime.rs/liblwip-sys/Cargo.toml new file mode 100644 index 000000000..eaaffdd99 --- /dev/null +++ b/artiq/runtime.rs/liblwip-sys/Cargo.toml @@ -0,0 +1,8 @@ +[package] +authors = ["The ARTIQ Project Developers"] +name = "lwip-sys" +version = "0.0.0" + +[lib] +name = "lwip_sys" +path = "lib.rs" diff --git a/artiq/runtime.rs/liblwip-sys/lib.rs b/artiq/runtime.rs/liblwip-sys/lib.rs new file mode 100644 index 000000000..aaac1d424 --- /dev/null +++ b/artiq/runtime.rs/liblwip-sys/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); +} diff --git a/artiq/runtime.rs/liblwip/Cargo.toml b/artiq/runtime.rs/liblwip/Cargo.toml new file mode 100644 index 000000000..cd76fe969 --- /dev/null +++ b/artiq/runtime.rs/liblwip/Cargo.toml @@ -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" } diff --git a/artiq/runtime.rs/liblwip/lib.rs b/artiq/runtime.rs/liblwip/lib.rs new file mode 100644 index 000000000..27d9a716e --- /dev/null +++ b/artiq/runtime.rs/liblwip/lib.rs @@ -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 = core::result::Result; + +fn result_from(err: lwip_sys::err, f: F) -> Result + 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)>> +} + +impl UdpSocket { + pub fn new() -> Result { + 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> +} + +impl TcpListener { + pub fn bind(addr: SocketAddr) -> Result { + 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; + (*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 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 { + 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>>> +} + +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>>; + 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>>; + (*buffer).push_back(result_from(err, || unreachable!())) + } + } + + unsafe { + let mut buffer = Box::new(LinkedList::new()); + let arg = &mut *buffer as *mut LinkedList>> 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 { + 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>> { + 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); + } + } +} diff --git a/artiq/runtime.rs/libstd_artiq/lib.rs b/artiq/runtime.rs/libstd_artiq/lib.rs index acf313bfa..b9ddea792 100644 --- a/artiq/runtime.rs/libstd_artiq/lib.rs +++ b/artiq/runtime.rs/libstd_artiq/lib.rs @@ -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] #![needs_panic_runtime] extern crate alloc_artiq; +extern crate alloc; extern crate collections; 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 v1 { pub use core::prelude::v1::*; diff --git a/artiq/runtime.rs/src/scheduler.rs b/artiq/runtime.rs/src/io.rs similarity index 88% rename from artiq/runtime.rs/src/scheduler.rs rename to artiq/runtime.rs/src/io.rs index 8edca4b94..43394c928 100644 --- a/artiq/runtime.rs/src/scheduler.rs +++ b/artiq/runtime.rs/src/io.rs @@ -1,18 +1,18 @@ extern crate fringe; -use std::prelude::v1::*; +use std::vec::Vec; use std::time::{Instant, Duration}; use self::fringe::OwnedStack; use self::fringe::generator::{Generator, Yielder}; #[derive(Debug)] -pub struct WaitRequest { +struct WaitRequest { timeout: Option, event: Option } #[derive(Debug)] -pub enum WaitResult { +enum WaitResult { Completed, TimedOut, Interrupted @@ -36,11 +36,11 @@ impl Scheduler { Scheduler { threads: Vec::new(), index: 0 } } - pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { + pub unsafe fn spawn(&mut self, stack_size: usize, f: F) { let stack = OwnedStack::new(stack_size); let thread = Thread { generator: Generator::unsafe_new(stack, move |yielder, _| { - f(Io(yielder)) + f(Waiter(yielder)) }), waiting_for: WaitRequest { timeout: None, @@ -102,7 +102,7 @@ impl Scheduler { } #[derive(Debug)] -pub enum WaitEvent {} +enum WaitEvent {} impl WaitEvent { fn completed(&self) -> bool { @@ -110,13 +110,13 @@ impl WaitEvent { } } -pub type IoResult = Result; +pub type Result = ::std::result::Result; #[derive(Debug)] -pub struct Io<'a>(&'a mut Yielder); +pub struct Waiter<'a>(&'a mut Yielder); -impl<'a> Io<'a> { - pub fn sleep(&mut self, duration: Duration) -> IoResult<()> { +impl<'a> Waiter<'a> { + pub fn sleep(&mut self, duration: Duration) -> Result<()> { let request = WaitRequest { timeout: Some(Instant::now() + duration), event: None diff --git a/artiq/runtime.rs/src/lib.rs b/artiq/runtime.rs/src/lib.rs index b5d089c82..39ecf44e1 100644 --- a/artiq/runtime.rs/src/lib.rs +++ b/artiq/runtime.rs/src/lib.rs @@ -2,29 +2,50 @@ #[macro_use] extern crate std_artiq as std; +extern crate fringe; +extern crate lwip; 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] -pub extern "C" fn rust_main() { - // let mut scheduler = Scheduler::new(); - // unsafe { - // scheduler.spawn(4096, move |mut io| { - // loop { - // println!("thread A"); - // io.sleep(Duration::from_secs(1)).unwrap() - // } - // }); - // scheduler.spawn(4096, move |mut io| { - // loop { - // println!("thread B"); - // io.sleep(Duration::from_millis(333)).unwrap() - // } - // }); - // } - // loop { scheduler.run() } +pub unsafe extern fn rust_main() { + println!("Accepting network sessions in Rust."); + network_init(); + + let addr = lwip::SocketAddr::new(lwip::IP4_ANY, 1234); + let mut listener = lwip::TcpListener::bind(addr).unwrap(); + let mut stream = None; + loop { + lwip_service(); + if let Some(new_stream) = listener.try_accept() { + stream = Some(new_stream) + } + 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"); + } + } + } } diff --git a/artiq/runtime/main.c b/artiq/runtime/main.c index 9bf000754..25fdc3b31 100644 --- a/artiq/runtime/main.c +++ b/artiq/runtime/main.c @@ -51,7 +51,7 @@ static struct netif netif; static ppp_pcb *ppp; #endif -static void lwip_service(void) +void lwip_service(void) { sys_check_timeouts(); #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 } -static void network_init(void) +void network_init(void) { struct ip4_addr local_ip; 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(); @@ -264,6 +264,10 @@ extern void _fheap, _eheap; extern void rust_main(); +u16_t tcp_sndbuf_(struct tcp_pcb *pcb) { + return tcp_sndbuf(pcb); +} + int main(void) { irq_setmask(0); @@ -278,14 +282,12 @@ int main(void) puts("Press 't' to enter test mode..."); blink_led(); - puts("Calling Rust..."); - rust_main(); - if(check_test_mode()) { puts("Entering test mode."); test_main(); } else { puts("Entering regular mode."); + // rust_main(); session_startup_kernel(); regular_main(); }