mirror of https://github.com/m-labs/artiq.git
646 lines
20 KiB
Rust
646 lines
20 KiB
Rust
#![feature(alloc, collections, libc)]
|
|
#![no_std]
|
|
|
|
extern crate alloc;
|
|
extern crate collections;
|
|
extern crate libc;
|
|
extern crate lwip_sys;
|
|
extern crate std_artiq as std;
|
|
|
|
use core::marker::PhantomData;
|
|
use core::ptr;
|
|
use core::cell::RefCell;
|
|
use core::fmt;
|
|
use alloc::boxed::Box;
|
|
use collections::LinkedList;
|
|
use libc::c_void;
|
|
use std::error;
|
|
|
|
#[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,
|
|
// Not used by lwip; added for building blocking interfaces.
|
|
Interrupted
|
|
}
|
|
|
|
impl Error {
|
|
fn as_str(&self) -> &str {
|
|
match *self {
|
|
Error::OutOfMemory => "out of memory error",
|
|
Error::Buffer => "buffer error",
|
|
Error::Timeout => "timeout",
|
|
Error::Routing => "routing error",
|
|
Error::InProgress => "operation in progress",
|
|
Error::IllegalValue => "illegal value",
|
|
Error::WouldBlock => "operation would block",
|
|
Error::AddressInUse => "address in use",
|
|
Error::AlreadyConnecting => "already connecting",
|
|
Error::AlreadyConnected => "already connected",
|
|
Error::NotConnected => "not connected",
|
|
Error::Interface => "low-level netif error",
|
|
Error::ConnectionAborted => "connection aborted",
|
|
Error::ConnectionReset => "connection reset",
|
|
Error::ConnectionClosed => "connection closed",
|
|
Error::IllegalArgument => "illegal argument",
|
|
Error::Interrupted => "interrupted"
|
|
}
|
|
}
|
|
}
|
|
|
|
impl core::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
write!(f, "{}", self.as_str())
|
|
}
|
|
}
|
|
|
|
impl error::Error for Error {
|
|
fn description(&self) -> &str {
|
|
self.as_str()
|
|
}
|
|
}
|
|
|
|
impl From<Error> for std::io::Error {
|
|
fn from(lower: Error) -> std::io::Error {
|
|
use std::io;
|
|
|
|
match lower {
|
|
Error::Interrupted => io::Error::new(io::ErrorKind::Interrupted, "interrupted"),
|
|
err => io::Error::new(io::ErrorKind::Other, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
V4([u8; 4]),
|
|
V6([u16; 8]),
|
|
Any
|
|
}
|
|
|
|
pub const IP4_ANY: IpAddr = IpAddr::V4([0, 0, 0, 0]);
|
|
pub const IP6_ANY: IpAddr = IpAddr::V6([0, 0, 0, 0, 0, 0, 0, 0]);
|
|
pub const IP_ANY: IpAddr = IpAddr::Any;
|
|
|
|
impl fmt::Display for IpAddr {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match *self {
|
|
IpAddr::V4(ref octets) =>
|
|
write!(f, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]),
|
|
|
|
IpAddr::V6(ref segments) => {
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
enum State { Head, Skip, Tail };
|
|
|
|
let mut state = State::Head;
|
|
for (idx, &segment) in segments.iter().enumerate() {
|
|
match state {
|
|
State::Head | State::Skip if segment == 0 =>
|
|
state = State::Skip,
|
|
State::Skip if segment != 0 => {
|
|
state = State::Tail;
|
|
try!(write!(f, ":{:x}", segment))
|
|
}
|
|
_ => try!(write!(f, "{:x}", segment))
|
|
}
|
|
|
|
if state != State::Skip && idx != 15 {
|
|
try!(write!(f, ":"))
|
|
}
|
|
}
|
|
Ok(())
|
|
},
|
|
|
|
IpAddr::Any =>
|
|
write!(f, "*")
|
|
}
|
|
}
|
|
}
|
|
|
|
impl IpAddr {
|
|
fn into_raw(self) -> lwip_sys::ip_addr {
|
|
match self {
|
|
IpAddr::V4(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::V6(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
|
|
},
|
|
IpAddr::Any =>
|
|
lwip_sys::ip_addr {
|
|
data: [0; 4],
|
|
type_: lwip_sys::IPADDR_TYPE_ANY
|
|
}
|
|
}
|
|
}
|
|
|
|
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::V4([(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::V6([(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]),
|
|
lwip_sys::ip_addr { type_: lwip_sys::IPADDR_TYPE_ANY, .. } =>
|
|
IpAddr::Any
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct SocketAddr {
|
|
pub ip: IpAddr,
|
|
pub port: u16
|
|
}
|
|
|
|
impl fmt::Display for SocketAddr {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
write!(f, "{}:{}", self.ip, self.port)
|
|
}
|
|
}
|
|
|
|
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]>
|
|
}
|
|
|
|
#[cfg(not(feature = "preemption"))]
|
|
unsafe impl<'payload> Send for Pbuf<'payload> {}
|
|
|
|
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 len(&self) -> usize {
|
|
unsafe { (*self.raw).len as usize }
|
|
}
|
|
|
|
pub fn as_slice(&self) -> &[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) -> &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 UdpSocketState {
|
|
recv_buffer: LinkedList<(Pbuf<'static>, SocketAddr)>
|
|
}
|
|
|
|
impl UdpSocketState {
|
|
pub fn readable(&self) -> bool {
|
|
!self.recv_buffer.is_empty()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct UdpSocket {
|
|
raw: *mut lwip_sys::udp_pcb,
|
|
state: Box<RefCell<UdpSocketState>>
|
|
}
|
|
|
|
#[cfg(not(feature = "preemption"))]
|
|
unsafe impl Send for UdpSocket {}
|
|
|
|
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 state = arg as *mut RefCell<UdpSocketState>;
|
|
let socket_addr = SocketAddr { ip: IpAddr::from_raw(addr), port: port };
|
|
(*state).borrow_mut().recv_buffer.push_back((Pbuf::from_raw(pbuf), socket_addr));
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
let raw = lwip_sys::udp_new();
|
|
if raw.is_null() { return Err(Error::OutOfMemory) }
|
|
|
|
let mut state = Box::new(RefCell::new(UdpSocketState {
|
|
recv_buffer: LinkedList::new()
|
|
}));
|
|
let arg = &mut *state as *mut RefCell<UdpSocketState> as *mut _;
|
|
lwip_sys::udp_recv(raw, Some(recv), arg);
|
|
Ok(UdpSocket { raw: raw, state: state })
|
|
}
|
|
}
|
|
|
|
pub fn state(&self) -> &RefCell<UdpSocketState> {
|
|
&*self.state
|
|
}
|
|
|
|
pub fn bind(&self, addr: SocketAddr) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::udp_bind(self.raw, &mut addr.ip.into_raw(), addr.port)
|
|
}, || ())
|
|
}
|
|
|
|
pub fn connect(&self, addr: SocketAddr) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::udp_connect(self.raw, &mut addr.ip.into_raw(), addr.port)
|
|
}, || ())
|
|
}
|
|
|
|
pub fn disconnect(&self) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::udp_disconnect(self.raw)
|
|
}, || ())
|
|
}
|
|
|
|
pub fn send<'a>(&'a self, pbuf: Pbuf<'a>) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::udp_send(self.raw, pbuf.as_raw())
|
|
}, || ())
|
|
}
|
|
|
|
pub fn send_to<'a>(&'a self, pbuf: Pbuf<'a>, addr: SocketAddr) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::udp_sendto(self.raw, pbuf.as_raw(),
|
|
&mut addr.ip.into_raw(), addr.port)
|
|
}, || ())
|
|
}
|
|
|
|
pub fn try_recv(&self) -> Option<(Pbuf<'static>, SocketAddr)> {
|
|
self.state.borrow_mut().recv_buffer.pop_front()
|
|
}
|
|
}
|
|
|
|
impl Drop for UdpSocket {
|
|
fn drop(&mut self) {
|
|
unsafe { lwip_sys::udp_remove(self.raw) }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TcpListenerState {
|
|
backlog: LinkedList<TcpStream>
|
|
}
|
|
|
|
impl TcpListenerState {
|
|
pub fn acceptable(&self) -> bool {
|
|
!self.backlog.is_empty()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TcpListener {
|
|
raw: *mut lwip_sys::tcp_pcb,
|
|
state: Box<RefCell<TcpListenerState>>
|
|
}
|
|
|
|
#[cfg(not(feature = "preemption"))]
|
|
unsafe impl Send for TcpListener {}
|
|
|
|
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 state = arg as *mut RefCell<TcpListenerState>;
|
|
(*state).borrow_mut().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) }
|
|
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)
|
|
}
|
|
|
|
let mut state = Box::new(RefCell::new(TcpListenerState {
|
|
backlog: LinkedList::new()
|
|
}));
|
|
let arg = &mut *state as *mut RefCell<TcpListenerState> as *mut _;
|
|
lwip_sys::tcp_arg(raw2, arg);
|
|
lwip_sys::tcp_accept(raw2, Some(accept));
|
|
Ok(TcpListener { raw: raw2, state: state })
|
|
}
|
|
}
|
|
|
|
pub fn state(&self) -> &RefCell<TcpListenerState> {
|
|
&*self.state
|
|
}
|
|
|
|
pub fn try_accept(&self) -> Option<TcpStream> {
|
|
self.state.borrow_mut().backlog.pop_front()
|
|
}
|
|
|
|
pub fn keepalive(&self) -> bool {
|
|
unsafe { *lwip_sys::tcp_so_options_(self.raw) & lwip_sys::SOF_KEEPALIVE != 0 }
|
|
}
|
|
|
|
pub fn set_keepalive(&self, keepalive: bool) {
|
|
if keepalive {
|
|
unsafe { *lwip_sys::tcp_so_options_(self.raw) |= lwip_sys::SOF_KEEPALIVE }
|
|
} else {
|
|
unsafe { *lwip_sys::tcp_so_options_(self.raw) &= !lwip_sys::SOF_KEEPALIVE }
|
|
}
|
|
}
|
|
}
|
|
|
|
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 TcpStreamState {
|
|
recv_buffer: LinkedList<Result<Pbuf<'static>>>,
|
|
send_avail: usize,
|
|
total_sent: usize
|
|
}
|
|
|
|
impl TcpStreamState {
|
|
pub fn readable(&self) -> bool {
|
|
!self.recv_buffer.is_empty()
|
|
}
|
|
|
|
pub fn writeable(&self) -> bool {
|
|
!(self.send_avail == 0)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TcpStream {
|
|
raw: *mut lwip_sys::tcp_pcb,
|
|
state: Box<RefCell<TcpStreamState>>
|
|
}
|
|
|
|
#[cfg(not(feature = "preemption"))]
|
|
unsafe impl Send for TcpStream {}
|
|
|
|
impl TcpStream {
|
|
fn from_raw(raw: *mut lwip_sys::tcp_pcb) -> TcpStream {
|
|
extern fn recv(arg: *mut c_void, _raw: *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 state = arg as *mut RefCell<TcpStreamState>;
|
|
if pbuf.is_null() {
|
|
(*state).borrow_mut().recv_buffer.push_back(Err(Error::ConnectionClosed))
|
|
} else {
|
|
(*state).borrow_mut().recv_buffer.push_back(Ok(Pbuf::from_raw(pbuf)))
|
|
}
|
|
}
|
|
lwip_sys::ERR_OK
|
|
}
|
|
|
|
extern fn sent(arg: *mut c_void, raw: *mut lwip_sys::tcp_pcb,
|
|
len: u16) -> lwip_sys::err {
|
|
unsafe {
|
|
let state = arg as *mut RefCell<TcpStreamState>;
|
|
let mut state = (*state).borrow_mut();
|
|
state.send_avail = lwip_sys::tcp_sndbuf_(raw) as usize;
|
|
state.total_sent = state.total_sent.wrapping_add(len as usize);
|
|
}
|
|
lwip_sys::ERR_OK
|
|
}
|
|
|
|
extern fn err(arg: *mut c_void, err: lwip_sys::err) {
|
|
unsafe {
|
|
let state = arg as *mut RefCell<TcpStreamState>;
|
|
(*state).borrow_mut().recv_buffer.push_back(result_from(err, || unreachable!()))
|
|
}
|
|
}
|
|
|
|
unsafe {
|
|
let mut state = Box::new(RefCell::new(TcpStreamState {
|
|
recv_buffer: LinkedList::new(),
|
|
send_avail: lwip_sys::tcp_sndbuf_(raw) as usize,
|
|
total_sent: 0
|
|
}));
|
|
let arg = &mut *state as *mut RefCell<TcpStreamState> as *mut _;
|
|
lwip_sys::tcp_arg(raw, arg);
|
|
lwip_sys::tcp_recv(raw, Some(recv));
|
|
lwip_sys::tcp_sent(raw, Some(sent));
|
|
lwip_sys::tcp_err(raw, Some(err));
|
|
lwip_sys::tcp_nagle_disable_(raw);
|
|
TcpStream { raw: raw, state: state }
|
|
}
|
|
}
|
|
|
|
pub fn state(&self) -> &RefCell<TcpStreamState> {
|
|
&*self.state
|
|
}
|
|
|
|
unsafe fn write_common(&self, data: &[u8], copy: bool) -> Result<usize> {
|
|
let sndbuf = lwip_sys::tcp_sndbuf_(self.raw) as usize;
|
|
let len = if data.len() < sndbuf { data.len() } else { sndbuf };
|
|
let result = result_from({
|
|
lwip_sys::tcp_write(self.raw, data as *const [u8] as *const _, len as u16,
|
|
lwip_sys::TCP_WRITE_FLAG_MORE |
|
|
if copy { lwip_sys::TCP_WRITE_FLAG_COPY } else { 0 })
|
|
}, || len);
|
|
self.state.borrow_mut().send_avail = lwip_sys::tcp_sndbuf_(self.raw) as usize;
|
|
result
|
|
}
|
|
|
|
pub fn write(&self, data: &[u8]) -> Result<usize> {
|
|
unsafe { self.write_common(data, true) }
|
|
}
|
|
|
|
pub fn write_in_place<F>(&self, data: &[u8], mut relinquish: F) -> Result<usize>
|
|
where F: FnMut() -> Result<()> {
|
|
let cursor = self.state.borrow().total_sent;
|
|
let written = try!(unsafe { self.write_common(data, false) });
|
|
loop {
|
|
let cursor_now = self.state.borrow().total_sent;
|
|
if cursor_now >= cursor.wrapping_add(written) {
|
|
return Ok(written)
|
|
} else {
|
|
try!(relinquish())
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn flush(&self) -> Result<()> {
|
|
result_from(unsafe {
|
|
lwip_sys::tcp_write(self.raw, ptr::null(), 0, 0)
|
|
}, || ())
|
|
}
|
|
|
|
pub fn try_read(&self) -> Result<Option<Pbuf<'static>>> {
|
|
let mut state = self.state.borrow_mut();
|
|
match state.recv_buffer.front() {
|
|
None => return Ok(None),
|
|
Some(&Err(err)) => return Err(err),
|
|
Some(_) => ()
|
|
}
|
|
match state.recv_buffer.pop_front() {
|
|
Some(Ok(pbuf)) => {
|
|
unsafe { lwip_sys::tcp_recved(self.raw, pbuf.len() as u16) }
|
|
return Ok(Some(pbuf))
|
|
},
|
|
_ => unreachable!()
|
|
}
|
|
}
|
|
|
|
pub fn shutdown(&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 {
|
|
// lwip *will* try to call back after tcp_close
|
|
lwip_sys::tcp_recv(self.raw, None);
|
|
lwip_sys::tcp_sent(self.raw, None);
|
|
lwip_sys::tcp_err(self.raw, None);
|
|
|
|
// tcp_close can fail here, but in drop() we don't care
|
|
let _ = lwip_sys::tcp_close(self.raw);
|
|
}
|
|
}
|
|
}
|