Select Git revision
network.rs 6.33 KiB
use core::fmt::Write;
use smoltcp;
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{Neighbor, NeighborCache, EthernetInterface, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet, SocketSetItem, SocketHandle, TcpSocket, TcpSocketBuffer};
use byteorder::{ByteOrder, LittleEndian};
use ::flash;
use ::build_info;
use ::Error;
use ethernet::EthernetDevice;
const CMD_INFO: u32 = 0;
const CMD_READ: u32 = 1;
const CMD_ERASE: u32 = 2;
const CMD_WRITE: u32 = 3;
const CMD_BOOT: u32 = 4;
use ::config::TCP_PORT;
/// Read an address and length from the socket
fn read_adr_len(socket: &mut TcpSocket) -> (u32, usize) {
let mut adr = [0u8; 4];
let mut len = [0u8; 4];
socket.recv_slice(&mut adr[..]).ok();
socket.recv_slice(&mut len[..]).ok();
let adr = LittleEndian::read_u32(&adr);
let len = LittleEndian::read_u32(&len);
(adr, len as usize)
}
/// Send a status word back at the start of a response
fn send_status(socket: &mut TcpSocket, status: ::Error) {
let mut resp = [0u8; 4];
LittleEndian::write_u32(&mut resp, status as u32);
socket.send_slice(&resp).unwrap();
}
/// Respond to the information request command with our build information.
fn cmd_info(socket: &mut TcpSocket) {
// Read the device unique ID
let id1: u32 = unsafe { *(0x1FFF_7A10 as *const u32) };
let id2: u32 = unsafe { *(0x1FFF_7A14 as *const u32) };
let id3: u32 = unsafe { *(0x1FFF_7A18 as *const u32) };
send_status(socket, Error::Success);
write!(socket, "blethrs {} {}\r\nBuilt: {}\r\nCompiler: {}\r\nMCU ID: {:08X}{:08X}{:08X}\r\n",
build_info::PKG_VERSION, build_info::GIT_VERSION.unwrap(), build_info::BUILT_TIME_UTC,
build_info::RUSTC_VERSION, id1, id2, id3).ok();
}
fn cmd_read(socket: &mut TcpSocket) {
let (adr, len) = read_adr_len(socket);
match flash::read(adr, len) {
Ok(data) => {
send_status(socket, Error::Success);
socket.send_slice(data).unwrap();
},
Err(err) => send_status(socket, err),
};
}
fn cmd_erase(socket: &mut TcpSocket) {
let (adr, len) = read_adr_len(socket);
match flash::erase(adr, len) {
Ok(()) => send_status(socket, Error::Success),
Err(err) => send_status(socket, err),
}
}
fn cmd_write(socket: &mut TcpSocket) {
let (adr, len) = read_adr_len(socket);
match socket.recv(|buf| (buf.len(), flash::write(adr, len, buf))) {
Ok(Ok(())) => send_status(socket, Error::Success),
Ok(Err(err)) => send_status(socket, err),
Err(_) => send_status(socket, Error::NetworkError),
}
}
fn cmd_boot(socket: &mut TcpSocket) {
send_status(socket, Error::Success);
::schedule_reset(50);
}
// Stores the underlying data buffers. If these were included in Network,
// they couldn't live in BSS and therefore take up a load of flash space.
struct NetworkBuffers {
tcp_tx_buf: [u8; 1536],
tcp_rx_buf: [u8; 1536],
}
static mut NETWORK_BUFFERS: NetworkBuffers = NetworkBuffers {
tcp_tx_buf: [0u8; 1536],
tcp_rx_buf: [0u8; 1536],
};
// Stores all the smoltcp required structs.
pub struct Network<'a> {
neighbor_cache_storage: [Option<(IpAddress, Neighbor)>; 16],
ip_addr: Option<[IpCidr; 1]>,
eth_iface: Option<EthernetInterface<'a, 'a, EthernetDevice>>,
sockets_storage: [Option<SocketSetItem<'a, 'a>>; 1],
sockets: Option<SocketSet<'a, 'a, 'a>>,
tcp_handle: Option<SocketHandle>,
initialised: bool,
}
pub static mut NETWORK: Network = Network {
neighbor_cache_storage: [None; 16],
ip_addr: None,
eth_iface: None,
sockets_storage: [None],
sockets: None,
tcp_handle: None,
initialised: false,
};
/// Initialise the static NETWORK.
///
/// Sets up the required EthernetInterface and sockets.
pub unsafe fn init<'a>(eth_dev: EthernetDevice, mac_addr: EthernetAddress, ip_addr: IpCidr) {
let neighbor_cache = NeighborCache::new(&mut NETWORK.neighbor_cache_storage.as_mut()[..]);
NETWORK.ip_addr = Some([ip_addr]);
NETWORK.eth_iface = Some(EthernetInterfaceBuilder::new(eth_dev)
.ethernet_addr(mac_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(&mut NETWORK.ip_addr.as_mut().unwrap()[..])
.finalize());
NETWORK.sockets = Some(SocketSet::new(&mut NETWORK.sockets_storage.as_mut()[..]));
let tcp_rx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_rx_buf.as_mut()[..]);
let tcp_tx_buf = TcpSocketBuffer::new(&mut NETWORK_BUFFERS.tcp_tx_buf.as_mut()[..]);
let tcp_socket = TcpSocket::new(tcp_rx_buf, tcp_tx_buf);
NETWORK.tcp_handle = Some(NETWORK.sockets.as_mut().unwrap().add(tcp_socket));
NETWORK.initialised = true;
}
/// Poll network stack.
///
/// Arrange for this function to be called frequently.
pub fn poll(time_ms: i64) {
unsafe {
// Bail out early if NETWORK is not initialised.
if !NETWORK.initialised {
return;
}
let sockets = NETWORK.sockets.as_mut().unwrap();
// Handle TCP
{
let mut socket = sockets.get::<TcpSocket>(NETWORK.tcp_handle.unwrap());
if !socket.is_open() {
socket.listen(TCP_PORT).unwrap();
}
if !socket.may_recv() && socket.may_send() {
socket.close();
}
if socket.can_recv() {
let mut cmd = [0u8; 4];
socket.recv_slice(&mut cmd[..]).ok();
let cmd = LittleEndian::read_u32(&cmd[..]);
match cmd {
CMD_INFO => cmd_info(&mut socket),
CMD_READ => cmd_read(&mut socket),
CMD_ERASE => cmd_erase(&mut socket),
CMD_WRITE => cmd_write(&mut socket),
CMD_BOOT => cmd_boot(&mut socket),
_ => (),
};
socket.close();
}
}
// Poll smoltcp
let timestamp = Instant::from_millis(time_ms);
match NETWORK.eth_iface.as_mut().unwrap().poll(sockets, timestamp) {
Ok(_) | Err(smoltcp::Error::Exhausted) => (),
Err(_) => (),
}
}
}