forked from M-Labs/artiq
firmware: implement libio and use in in drtioaux.
This commit is contained in:
parent
d543c9aa63
commit
46c8afc56c
10
artiq/firmware/Cargo.lock
generated
10
artiq/firmware/Cargo.lock
generated
@ -107,10 +107,9 @@ version = "0.0.0"
|
||||
dependencies = [
|
||||
"board 0.0.0",
|
||||
"build_misoc 0.0.0",
|
||||
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crc 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"io 0.0.0",
|
||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"std_artiq 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -125,6 +124,13 @@ dependencies = [
|
||||
"libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
|
@ -14,6 +14,5 @@ build_misoc = { path = "../libbuild_misoc" }
|
||||
[dependencies]
|
||||
log = { version = "0.4", default-features = false }
|
||||
crc = { version = "1.7", default-features = false }
|
||||
std_artiq = { path = "../libstd_artiq", features = ["alloc"] }
|
||||
io = { path = "../libio", features = ["byteorder"] }
|
||||
board = { path = "../libboard" }
|
||||
byteorder = { version = "1.0", default-features = false }
|
||||
|
139
artiq/firmware/libdrtioaux/hw.rs
Normal file
139
artiq/firmware/libdrtioaux/hw.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use super::*;
|
||||
|
||||
use {Error, Result};
|
||||
use io::Cursor;
|
||||
use io::proto::ProtoRead;
|
||||
|
||||
pub fn reset(linkno: u8) {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
// clear buffer first to limit race window with buffer overflow
|
||||
// error. We assume the CPU is fast enough so that no two packets
|
||||
// will be received between the buffer and the error flag are cleared.
|
||||
(board::csr::DRTIO[linkno].aux_rx_present_write)(1);
|
||||
(board::csr::DRTIO[linkno].aux_rx_error_write)(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_rx_error(linkno: u8) -> bool {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
let error = (board::csr::DRTIO[linkno].aux_rx_error_read)() != 0;
|
||||
if error {
|
||||
(board::csr::DRTIO[linkno].aux_rx_error_write)(1)
|
||||
}
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
fn receive<F, T>(linkno: u8, f: F) -> Result<Option<T>, !>
|
||||
where F: FnOnce(&[u8]) -> Result<T, !>
|
||||
{
|
||||
let linkidx = linkno as usize;
|
||||
unsafe {
|
||||
if (board::csr::DRTIO[linkidx].aux_rx_present_read)() == 1 {
|
||||
let ptr = board::mem::DRTIO_AUX[linkidx].base +
|
||||
board::mem::DRTIO_AUX[linkidx].size / 2;
|
||||
let len = (board::csr::DRTIO[linkidx].aux_rx_length_read)();
|
||||
let result = f(slice::from_raw_parts(ptr as *mut u8, len as usize));
|
||||
(board::csr::DRTIO[linkidx].aux_rx_present_write)(1);
|
||||
Ok(Some(result?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_link(linkno: u8) -> Result<Option<Packet>, !> {
|
||||
if has_rx_error(linkno) {
|
||||
return Err(Error::GatewareError)
|
||||
}
|
||||
|
||||
receive(linkno, |buffer| {
|
||||
if buffer.len() < 8 {
|
||||
return Err(Error::Io(IoError::UnexpectedEof))
|
||||
}
|
||||
|
||||
let mut reader = Cursor::new(buffer);
|
||||
|
||||
let checksum_at = buffer.len() - 4;
|
||||
let checksum = crc::crc32::checksum_ieee(&reader.get_ref()[0..checksum_at]);
|
||||
reader.set_position(checksum_at);
|
||||
if reader.read_u32()? != checksum {
|
||||
return Err(Error::CorruptedPacket)
|
||||
}
|
||||
reader.set_position(0);
|
||||
|
||||
Packet::read_from(&mut reader)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recv_timeout_link(linkno: u8, timeout_ms: Option<u64>) -> Result<Packet, !> {
|
||||
let timeout_ms = timeout_ms.unwrap_or(10);
|
||||
let limit = board::clock::get_ms() + timeout_ms;
|
||||
while board::clock::get_ms() < limit {
|
||||
match recv_link(linkno)? {
|
||||
None => (),
|
||||
Some(packet) => return Ok(packet),
|
||||
}
|
||||
}
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
|
||||
fn transmit<F>(linkno: u8, f: F) -> Result<(), !>
|
||||
where F: FnOnce(&mut [u8]) -> Result<usize, !>
|
||||
{
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
while (board::csr::DRTIO[linkno].aux_tx_read)() != 0 {}
|
||||
let ptr = board::mem::DRTIO_AUX[linkno].base;
|
||||
let len = board::mem::DRTIO_AUX[linkno].size / 2;
|
||||
let len = f(slice::from_raw_parts_mut(ptr as *mut u8, len))?;
|
||||
(board::csr::DRTIO[linkno].aux_tx_length_write)(len as u16);
|
||||
(board::csr::DRTIO[linkno].aux_tx_write)(1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_link(linkno: u8, packet: &Packet) -> Result<(), !> {
|
||||
transmit(linkno, |buffer| {
|
||||
let mut writer = Cursor::new(buffer);
|
||||
|
||||
packet.write_to(&mut writer)?;
|
||||
|
||||
let padding = 4 - (writer.position() % 4);
|
||||
if padding != 4 {
|
||||
for _ in 0..padding {
|
||||
writer.write_u8(0)?;
|
||||
}
|
||||
}
|
||||
|
||||
let checksum = crc::crc32::checksum_ieee(&writer.get_ref()[0..writer.position()]);
|
||||
writer.write_u32(checksum)?;
|
||||
|
||||
Ok(writer.position())
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: routing
|
||||
fn get_linkno(nodeno: u8) -> Result<u8, !> {
|
||||
if nodeno == 0 || nodeno as usize > board::csr::DRTIO.len() {
|
||||
return Err(Error::NoRoute)
|
||||
}
|
||||
Ok(nodeno - 1)
|
||||
}
|
||||
|
||||
pub fn recv(nodeno: u8) -> Result<Option<Packet>, !> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
recv_link(linkno)
|
||||
}
|
||||
|
||||
pub fn recv_timeout(nodeno: u8, timeout_ms: Option<u64>) -> Result<Packet, !> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
recv_timeout_link(linkno, timeout_ms)
|
||||
}
|
||||
|
||||
pub fn send(nodeno: u8, packet: &Packet) -> Result<(), !> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
send_link(linkno, packet)
|
||||
}
|
@ -1,17 +1,63 @@
|
||||
#![no_std]
|
||||
#![feature(never_type)]
|
||||
|
||||
extern crate byteorder;
|
||||
extern crate crc;
|
||||
#[macro_use]
|
||||
extern crate std_artiq as std;
|
||||
|
||||
extern crate io;
|
||||
extern crate board;
|
||||
|
||||
mod proto;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
#[cfg(has_drtio)]
|
||||
use core::slice;
|
||||
use proto::*;
|
||||
use core::result;
|
||||
use core::fmt;
|
||||
|
||||
use io::{Read, Write, Error as IoError};
|
||||
use io::proto::{ProtoRead, ProtoWrite};
|
||||
|
||||
pub type Result<T, E> = result::Result<T, Error<E>>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error<T> {
|
||||
UnknownPacket(u8),
|
||||
CorruptedPacket,
|
||||
TimedOut,
|
||||
NoRoute,
|
||||
GatewareError,
|
||||
Io(IoError<T>),
|
||||
Other(T)
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Error<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::UnknownPacket(ty) =>
|
||||
write!(f, "unknown packet type {:#02x}", ty),
|
||||
&Error::CorruptedPacket =>
|
||||
write!(f, "packet CRC failed"),
|
||||
&Error::TimedOut =>
|
||||
write!(f, "timed out waiting for data"),
|
||||
&Error::NoRoute =>
|
||||
write!(f, "invalid node number"),
|
||||
&Error::GatewareError =>
|
||||
write!(f, "gateware reported error"),
|
||||
&Error::Io(ref io) =>
|
||||
write!(f, "I/O error ({})", io),
|
||||
&Error::Other(ref err) =>
|
||||
write!(f, "{}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error<T> {
|
||||
fn from(value: T) -> Error<T> {
|
||||
Error::Other(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<IoError<T>> for Error<T> {
|
||||
fn from(value: IoError<T>) -> Error<T> {
|
||||
Error::Io(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Packet {
|
||||
@ -49,215 +95,220 @@ pub enum Packet {
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
pub fn read_from(reader: &mut Read) -> io::Result<Packet> {
|
||||
Ok(match read_u8(reader)? {
|
||||
pub fn read_from<T: Read>(reader: &mut T) -> Result<Packet, T::ReadError> {
|
||||
Ok(match reader.read_u8()? {
|
||||
0x00 => Packet::EchoRequest,
|
||||
0x01 => Packet::EchoReply,
|
||||
0x02 => Packet::ResetRequest {
|
||||
phy: read_bool(reader)?
|
||||
phy: reader.read_bool()?
|
||||
},
|
||||
0x03 => Packet::ResetAck,
|
||||
|
||||
0x20 => Packet::RtioErrorRequest,
|
||||
0x21 => Packet::RtioNoErrorReply,
|
||||
0x22 => Packet::RtioErrorSequenceErrorReply {
|
||||
channel: read_u16(reader)?
|
||||
channel: reader.read_u16()?
|
||||
},
|
||||
0x23 => Packet::RtioErrorCollisionReply {
|
||||
channel: read_u16(reader)?
|
||||
channel: reader.read_u16()?
|
||||
},
|
||||
0x24 => Packet::RtioErrorBusyReply {
|
||||
channel: read_u16(reader)?
|
||||
channel: reader.read_u16()?
|
||||
},
|
||||
|
||||
0x40 => Packet::MonitorRequest {
|
||||
channel: read_u16(reader)?,
|
||||
probe: read_u8(reader)?
|
||||
channel: reader.read_u16()?,
|
||||
probe: reader.read_u8()?
|
||||
},
|
||||
0x41 => Packet::MonitorReply {
|
||||
value: read_u32(reader)?
|
||||
value: reader.read_u32()?
|
||||
},
|
||||
0x50 => Packet::InjectionRequest {
|
||||
channel: read_u16(reader)?,
|
||||
overrd: read_u8(reader)?,
|
||||
value: read_u8(reader)?
|
||||
channel: reader.read_u16()?,
|
||||
overrd: reader.read_u8()?,
|
||||
value: reader.read_u8()?
|
||||
},
|
||||
0x51 => Packet::InjectionStatusRequest {
|
||||
channel: read_u16(reader)?,
|
||||
overrd: read_u8(reader)?
|
||||
channel: reader.read_u16()?,
|
||||
overrd: reader.read_u8()?
|
||||
},
|
||||
0x52 => Packet::InjectionStatusReply {
|
||||
value: read_u8(reader)?
|
||||
value: reader.read_u8()?
|
||||
},
|
||||
|
||||
0x80 => Packet::I2cStartRequest {
|
||||
busno: read_u8(reader)?
|
||||
busno: reader.read_u8()?
|
||||
},
|
||||
0x81 => Packet::I2cRestartRequest {
|
||||
busno: read_u8(reader)?
|
||||
busno: reader.read_u8()?
|
||||
},
|
||||
0x82 => Packet::I2cStopRequest {
|
||||
busno: read_u8(reader)?
|
||||
busno: reader.read_u8()?
|
||||
},
|
||||
0x83 => Packet::I2cWriteRequest {
|
||||
busno: read_u8(reader)?,
|
||||
data: read_u8(reader)?
|
||||
busno: reader.read_u8()?,
|
||||
data: reader.read_u8()?
|
||||
},
|
||||
0x84 => Packet::I2cWriteReply {
|
||||
succeeded: read_bool(reader)?,
|
||||
ack: read_bool(reader)?
|
||||
succeeded: reader.read_bool()?,
|
||||
ack: reader.read_bool()?
|
||||
},
|
||||
0x85 => Packet::I2cReadRequest {
|
||||
busno: read_u8(reader)?,
|
||||
ack: read_bool(reader)?
|
||||
busno: reader.read_u8()?,
|
||||
ack: reader.read_bool()?
|
||||
},
|
||||
0x86 => Packet::I2cReadReply {
|
||||
succeeded: read_bool(reader)?,
|
||||
data: read_u8(reader)?
|
||||
succeeded: reader.read_bool()?,
|
||||
data: reader.read_u8()?
|
||||
},
|
||||
0x87 => Packet::I2cBasicReply {
|
||||
succeeded: read_bool(reader)?
|
||||
succeeded: reader.read_bool()?
|
||||
},
|
||||
|
||||
0x90 => Packet::SpiSetConfigRequest {
|
||||
busno: read_u8(reader)?,
|
||||
flags: read_u8(reader)?,
|
||||
length: read_u8(reader)?,
|
||||
div: read_u8(reader)?,
|
||||
cs: read_u8(reader)?
|
||||
busno: reader.read_u8()?,
|
||||
flags: reader.read_u8()?,
|
||||
length: reader.read_u8()?,
|
||||
div: reader.read_u8()?,
|
||||
cs: reader.read_u8()?
|
||||
},
|
||||
/* 0x91: was Packet::SpiSetXferRequest */
|
||||
0x92 => Packet::SpiWriteRequest {
|
||||
busno: read_u8(reader)?,
|
||||
data: read_u32(reader)?
|
||||
busno: reader.read_u8()?,
|
||||
data: reader.read_u32()?
|
||||
},
|
||||
0x93 => Packet::SpiReadRequest {
|
||||
busno: read_u8(reader)?
|
||||
busno: reader.read_u8()?
|
||||
},
|
||||
0x94 => Packet::SpiReadReply {
|
||||
succeeded: read_bool(reader)?,
|
||||
data: read_u32(reader)?
|
||||
succeeded: reader.read_bool()?,
|
||||
data: reader.read_u32()?
|
||||
},
|
||||
0x95 => Packet::SpiBasicReply {
|
||||
succeeded: read_bool(reader)?
|
||||
succeeded: reader.read_bool()?
|
||||
},
|
||||
|
||||
_ => return Err(io::Error::new(io::ErrorKind::InvalidData, "unknown packet type"))
|
||||
ty => return Err(Error::UnknownPacket(ty))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_to(&self, writer: &mut Write) -> io::Result<()> {
|
||||
pub fn write_to<T: Write>(&self, writer: &mut T) -> Result<(), T::WriteError> {
|
||||
match *self {
|
||||
Packet::EchoRequest => write_u8(writer, 0x00)?,
|
||||
Packet::EchoReply => write_u8(writer, 0x01)?,
|
||||
Packet::EchoRequest =>
|
||||
writer.write_u8(0x00)?,
|
||||
Packet::EchoReply =>
|
||||
writer.write_u8(0x01)?,
|
||||
Packet::ResetRequest { phy } => {
|
||||
write_u8(writer, 0x02)?;
|
||||
write_bool(writer, phy)?;
|
||||
writer.write_u8(0x02)?;
|
||||
writer.write_bool(phy)?;
|
||||
},
|
||||
Packet::ResetAck => write_u8(writer, 0x03)?,
|
||||
Packet::ResetAck =>
|
||||
writer.write_u8(0x03)?,
|
||||
|
||||
Packet::RtioErrorRequest => write_u8(writer, 0x20)?,
|
||||
Packet::RtioNoErrorReply => write_u8(writer, 0x21)?,
|
||||
Packet::RtioErrorRequest =>
|
||||
writer.write_u8(0x20)?,
|
||||
Packet::RtioNoErrorReply =>
|
||||
writer.write_u8(0x21)?,
|
||||
Packet::RtioErrorSequenceErrorReply { channel } => {
|
||||
write_u8(writer, 0x22)?;
|
||||
write_u16(writer, channel)?;
|
||||
writer.write_u8(0x22)?;
|
||||
writer.write_u16(channel)?;
|
||||
},
|
||||
Packet::RtioErrorCollisionReply { channel } => {
|
||||
write_u8(writer, 0x23)?;
|
||||
write_u16(writer, channel)?;
|
||||
writer.write_u8(0x23)?;
|
||||
writer.write_u16(channel)?;
|
||||
},
|
||||
Packet::RtioErrorBusyReply { channel } => {
|
||||
write_u8(writer, 0x24)?;
|
||||
write_u16(writer, channel)?;
|
||||
writer.write_u8(0x24)?;
|
||||
writer.write_u16(channel)?;
|
||||
},
|
||||
|
||||
Packet::MonitorRequest { channel, probe } => {
|
||||
write_u8(writer, 0x40)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, probe)?;
|
||||
writer.write_u8(0x40)?;
|
||||
writer.write_u16(channel)?;
|
||||
writer.write_u8(probe)?;
|
||||
},
|
||||
Packet::MonitorReply { value } => {
|
||||
write_u8(writer, 0x41)?;
|
||||
write_u32(writer, value)?;
|
||||
writer.write_u8(0x41)?;
|
||||
writer.write_u32(value)?;
|
||||
},
|
||||
Packet::InjectionRequest { channel, overrd, value } => {
|
||||
write_u8(writer, 0x50)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, overrd)?;
|
||||
write_u8(writer, value)?;
|
||||
writer.write_u8(0x50)?;
|
||||
writer.write_u16(channel)?;
|
||||
writer.write_u8(overrd)?;
|
||||
writer.write_u8(value)?;
|
||||
},
|
||||
Packet::InjectionStatusRequest { channel, overrd } => {
|
||||
write_u8(writer, 0x51)?;
|
||||
write_u16(writer, channel)?;
|
||||
write_u8(writer, overrd)?;
|
||||
writer.write_u8(0x51)?;
|
||||
writer.write_u16(channel)?;
|
||||
writer.write_u8(overrd)?;
|
||||
},
|
||||
Packet::InjectionStatusReply { value } => {
|
||||
write_u8(writer, 0x52)?;
|
||||
write_u8(writer, value)?;
|
||||
writer.write_u8(0x52)?;
|
||||
writer.write_u8(value)?;
|
||||
},
|
||||
|
||||
Packet::I2cStartRequest { busno } => {
|
||||
write_u8(writer, 0x80)?;
|
||||
write_u8(writer, busno)?;
|
||||
writer.write_u8(0x80)?;
|
||||
writer.write_u8(busno)?;
|
||||
},
|
||||
Packet::I2cRestartRequest { busno } => {
|
||||
write_u8(writer, 0x81)?;
|
||||
write_u8(writer, busno)?;
|
||||
writer.write_u8(0x81)?;
|
||||
writer.write_u8(busno)?;
|
||||
},
|
||||
Packet::I2cStopRequest { busno } => {
|
||||
write_u8(writer, 0x82)?;
|
||||
write_u8(writer, busno)?;
|
||||
writer.write_u8(0x82)?;
|
||||
writer.write_u8(busno)?;
|
||||
},
|
||||
Packet::I2cWriteRequest { busno, data } => {
|
||||
write_u8(writer, 0x83)?;
|
||||
write_u8(writer, busno)?;
|
||||
write_u8(writer, data)?;
|
||||
writer.write_u8(0x83)?;
|
||||
writer.write_u8(busno)?;
|
||||
writer.write_u8(data)?;
|
||||
},
|
||||
Packet::I2cWriteReply { succeeded, ack } => {
|
||||
write_u8(writer, 0x84)?;
|
||||
write_bool(writer, succeeded)?;
|
||||
write_bool(writer, ack)?;
|
||||
writer.write_u8(0x84)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
writer.write_bool(ack)?;
|
||||
},
|
||||
Packet::I2cReadRequest { busno, ack } => {
|
||||
write_u8(writer, 0x85)?;
|
||||
write_u8(writer, busno)?;
|
||||
write_bool(writer, ack)?;
|
||||
writer.write_u8(0x85)?;
|
||||
writer.write_u8(busno)?;
|
||||
writer.write_bool(ack)?;
|
||||
},
|
||||
Packet::I2cReadReply { succeeded, data } => {
|
||||
write_u8(writer, 0x86)?;
|
||||
write_bool(writer, succeeded)?;
|
||||
write_u8(writer, data)?;
|
||||
writer.write_u8(0x86)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
writer.write_u8(data)?;
|
||||
},
|
||||
Packet::I2cBasicReply { succeeded } => {
|
||||
write_u8(writer, 0x87)?;
|
||||
write_bool(writer, succeeded)?;
|
||||
writer.write_u8(0x87)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
},
|
||||
|
||||
Packet::SpiSetConfigRequest { busno, flags, length, div, cs } => {
|
||||
write_u8(writer, 0x90)?;
|
||||
write_u8(writer, busno)?;
|
||||
write_u8(writer, flags)?;
|
||||
write_u8(writer, length)?;
|
||||
write_u8(writer, div)?;
|
||||
write_u8(writer, cs)?;
|
||||
writer.write_u8(0x90)?;
|
||||
writer.write_u8(busno)?;
|
||||
writer.write_u8(flags)?;
|
||||
writer.write_u8(length)?;
|
||||
writer.write_u8(div)?;
|
||||
writer.write_u8(cs)?;
|
||||
},
|
||||
Packet::SpiWriteRequest { busno, data } => {
|
||||
write_u8(writer, 0x92)?;
|
||||
write_u8(writer, busno)?;
|
||||
write_u32(writer, data)?;
|
||||
writer.write_u8(0x92)?;
|
||||
writer.write_u8(busno)?;
|
||||
writer.write_u32(data)?;
|
||||
},
|
||||
Packet::SpiReadRequest { busno } => {
|
||||
write_u8(writer, 0x93)?;
|
||||
write_u8(writer, busno)?;
|
||||
writer.write_u8(0x93)?;
|
||||
writer.write_u8(busno)?;
|
||||
},
|
||||
Packet::SpiReadReply { succeeded, data } => {
|
||||
write_u8(writer, 0x94)?;
|
||||
write_bool(writer, succeeded)?;
|
||||
write_u32(writer, data)?;
|
||||
writer.write_u8(0x94)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
writer.write_u32(data)?;
|
||||
},
|
||||
Packet::SpiBasicReply { succeeded } => {
|
||||
write_u8(writer, 0x95)?;
|
||||
write_bool(writer, succeeded)?;
|
||||
writer.write_u8(0x95)?;
|
||||
writer.write_bool(succeeded)?;
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
@ -265,163 +316,4 @@ impl Packet {
|
||||
}
|
||||
|
||||
#[cfg(has_drtio)]
|
||||
pub mod hw {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
pub fn reset(linkno: u8) {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
// clear buffer first to limit race window with buffer overflow
|
||||
// error. We assume the CPU is fast enough so that no two packets
|
||||
// will be received between the buffer and the error flag are cleared.
|
||||
(board::csr::DRTIO[linkno].aux_rx_present_write)(1);
|
||||
(board::csr::DRTIO[linkno].aux_rx_error_write)(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn rx_has_error(linkno: u8) -> bool {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
let error = (board::csr::DRTIO[linkno].aux_rx_error_read)() != 0;
|
||||
if error {
|
||||
(board::csr::DRTIO[linkno].aux_rx_error_write)(1)
|
||||
}
|
||||
error
|
||||
}
|
||||
}
|
||||
|
||||
struct RxBuffer(u8, &'static [u8]);
|
||||
|
||||
impl Drop for RxBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(board::csr::DRTIO[self.0 as usize].aux_rx_present_write)(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rx_get_buffer(linkno: u8) -> Option<RxBuffer> {
|
||||
let linkidx = linkno as usize;
|
||||
unsafe {
|
||||
if (board::csr::DRTIO[linkidx].aux_rx_present_read)() == 1 {
|
||||
let length = (board::csr::DRTIO[linkidx].aux_rx_length_read)();
|
||||
let base = board::mem::DRTIO_AUX[linkidx].base + board::mem::DRTIO_AUX[linkidx].size/2;
|
||||
let sl = slice::from_raw_parts(base as *mut u8, length as usize);
|
||||
Some(RxBuffer(linkno, sl))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_link(linkno: u8) -> io::Result<Option<Packet>> {
|
||||
if rx_has_error(linkno) {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "gateware reported error"))
|
||||
}
|
||||
let buffer = rx_get_buffer(linkno);
|
||||
match buffer {
|
||||
Some(rxb) => {
|
||||
let slice = rxb.1;
|
||||
let mut reader = Cursor::new(slice);
|
||||
|
||||
let len = slice.len();
|
||||
if len < 8 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "packet too short"))
|
||||
}
|
||||
let computed_crc = crc::crc32::checksum_ieee(&reader.get_ref()[0..len-4]);
|
||||
reader.set_position((len-4) as u64);
|
||||
let crc = read_u32(&mut reader)?;
|
||||
if crc != computed_crc {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "packet CRC failed"))
|
||||
}
|
||||
reader.set_position(0);
|
||||
|
||||
let packet_r = Packet::read_from(&mut reader);
|
||||
match packet_r {
|
||||
Ok(packet) => Ok(Some(packet)),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
None => Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recv_timeout_link(linkno: u8, timeout_ms: Option<u64>) -> io::Result<Packet> {
|
||||
let timeout_ms = timeout_ms.unwrap_or(10);
|
||||
let limit = board::clock::get_ms() + timeout_ms;
|
||||
while board::clock::get_ms() < limit {
|
||||
match recv_link(linkno) {
|
||||
Ok(None) => (),
|
||||
Ok(Some(packet)) => return Ok(packet),
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::TimedOut, "timed out waiting for data"))
|
||||
}
|
||||
|
||||
fn tx_get_buffer(linkno: u8) -> &'static mut [u8] {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
while (board::csr::DRTIO[linkno].aux_tx_read)() != 0 {}
|
||||
let base = board::mem::DRTIO_AUX[linkno].base;
|
||||
let size = board::mem::DRTIO_AUX[linkno].size/2;
|
||||
slice::from_raw_parts_mut(base as *mut u8, size)
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_ack_buffer(linkno: u8, length: u16) {
|
||||
let linkno = linkno as usize;
|
||||
unsafe {
|
||||
(board::csr::DRTIO[linkno].aux_tx_length_write)(length);
|
||||
(board::csr::DRTIO[linkno].aux_tx_write)(1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_link(linkno: u8, packet: &Packet) -> io::Result<()> {
|
||||
let sl = tx_get_buffer(linkno);
|
||||
|
||||
let mut writer = Cursor::new(sl);
|
||||
packet.write_to(&mut writer)?;
|
||||
let mut len = writer.position();
|
||||
|
||||
let padding = 4 - (len % 4);
|
||||
if padding != 4 {
|
||||
for _ in 0..padding {
|
||||
write_u8(&mut writer, 0)?;
|
||||
}
|
||||
len += padding;
|
||||
}
|
||||
|
||||
let crc = crc::crc32::checksum_ieee(&writer.get_ref()[0..len as usize]);
|
||||
write_u32(&mut writer, crc)?;
|
||||
len += 4;
|
||||
|
||||
tx_ack_buffer(linkno, len as u16);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: routing
|
||||
fn get_linkno(nodeno: u8) -> io::Result<u8> {
|
||||
if nodeno == 0 || nodeno as usize > board::csr::DRTIO.len() {
|
||||
return Err(io::Error::new(io::ErrorKind::NotFound, "invalid node number"))
|
||||
}
|
||||
Ok(nodeno - 1)
|
||||
}
|
||||
|
||||
pub fn recv(nodeno: u8) -> io::Result<Option<Packet>> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
recv_link(linkno)
|
||||
}
|
||||
|
||||
pub fn recv_timeout(nodeno: u8, timeout_ms: Option<u64>) -> io::Result<Packet> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
recv_timeout_link(linkno, timeout_ms)
|
||||
}
|
||||
|
||||
pub fn send(nodeno: u8, packet: &Packet) -> io::Result<()> {
|
||||
let linkno = get_linkno(nodeno)?;
|
||||
send_link(linkno, packet)
|
||||
}
|
||||
}
|
||||
pub mod hw;
|
||||
|
@ -1,106 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::vec::Vec;
|
||||
use std::string::String;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
// FIXME: replace these with byteorder core io traits once those are in
|
||||
#[inline(always)]
|
||||
pub fn read_u8(reader: &mut Read) -> io::Result<u8> {
|
||||
let mut bytes = [0; 1];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_u8(writer: &mut Write, value: u8) -> io::Result<()> {
|
||||
let bytes = [value; 1];
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_bool(reader: &mut Read) -> io::Result<bool> {
|
||||
if read_u8(reader)? == 0 {
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_bool(writer: &mut Write, value: bool) -> io::Result<()> {
|
||||
if value {
|
||||
write_u8(writer, 1)
|
||||
} else {
|
||||
write_u8(writer, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u16(reader: &mut Read) -> io::Result<u16> {
|
||||
let mut bytes = [0; 2];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_u16(writer: &mut Write, value: u16) -> io::Result<()> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u32(reader: &mut Read) -> io::Result<u32> {
|
||||
let mut bytes = [0; 4];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_u32(writer: &mut Write, value: u32) -> io::Result<()> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u64(reader: &mut Read) -> io::Result<u64> {
|
||||
let mut bytes = [0; 8];
|
||||
reader.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_u64(writer: &mut Write, value: u64) -> io::Result<()> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_u64(&mut bytes, value);
|
||||
writer.write_all(&bytes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_bytes(reader: &mut Read) -> io::Result<Vec<u8>> {
|
||||
let length = read_u32(reader)?;
|
||||
let mut value = vec![0; length as usize];
|
||||
reader.read_exact(&mut value)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> {
|
||||
write_u32(writer, value.len() as u32)?;
|
||||
writer.write_all(value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_string(reader: &mut Read) -> io::Result<String> {
|
||||
let bytes = read_bytes(reader)?;
|
||||
String::from_utf8(bytes)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8"))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> {
|
||||
write_bytes(writer, value.as_bytes())
|
||||
}
|
14
artiq/firmware/libio/Cargo.toml
Normal file
14
artiq/firmware/libio/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
authors = ["M-Labs"]
|
||||
name = "io"
|
||||
version = "0.0.0"
|
||||
|
||||
[lib]
|
||||
name = "io"
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
byteorder = { version = "1.0", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
alloc = []
|
176
artiq/firmware/libio/lib.rs
Normal file
176
artiq/firmware/libio/lib.rs
Normal file
@ -0,0 +1,176 @@
|
||||
#![no_std]
|
||||
#![feature(never_type)]
|
||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
#[cfg(feature = "byteorder")]
|
||||
extern crate byteorder;
|
||||
|
||||
use core::result;
|
||||
use core::fmt;
|
||||
|
||||
#[cfg(feature = "byteorder")]
|
||||
pub mod proto;
|
||||
|
||||
pub type Result<T, E> = result::Result<T, Error<E>>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error<T> {
|
||||
UnexpectedEof,
|
||||
Other(T)
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for Error<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::UnexpectedEof =>
|
||||
write!(f, "unexpected end of stream"),
|
||||
&Error::Other(ref err) =>
|
||||
write!(f, "{}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error<T> {
|
||||
fn from(value: T) -> Error<T> {
|
||||
Error::Other(value)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Read {
|
||||
type ReadError;
|
||||
|
||||
/// Pull some bytes from this source into the specified buffer, returning
|
||||
/// how many bytes were read.
|
||||
fn read(&mut self, buf: &mut [u8]) -> result::Result<usize, Self::ReadError>;
|
||||
|
||||
/// Read the exact number of bytes required to fill `buf`.
|
||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::ReadError> {
|
||||
while !buf.is_empty() {
|
||||
let read_bytes = self.read(buf)?;
|
||||
if read_bytes == 0 {
|
||||
return Err(Error::UnexpectedEof)
|
||||
}
|
||||
|
||||
buf = &mut { buf }[read_bytes..];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Write {
|
||||
type WriteError;
|
||||
type FlushError;
|
||||
|
||||
/// Write a buffer into this object, returning how many bytes were written.
|
||||
fn write(&mut self, buf: &[u8]) -> result::Result<usize, Self::WriteError>;
|
||||
|
||||
/// Flush this output stream, ensuring that all intermediately buffered contents
|
||||
/// reach their destination.
|
||||
fn flush(&mut self) -> result::Result<(), Self::FlushError>;
|
||||
|
||||
/// Attempts to write an entire buffer into `self`.
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::WriteError> {
|
||||
while buf.len() > 0 {
|
||||
let written_bytes = self.write(buf)?;
|
||||
if written_bytes == 0 {
|
||||
return Err(Error::UnexpectedEof)
|
||||
}
|
||||
|
||||
buf = &buf[written_bytes..];
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hints the writer how much bytes will be written after call to this function.
|
||||
///
|
||||
/// At least `min` bytes should be written after the call to this function and
|
||||
/// if `max` is `Some(x)` than at most `x` bytes should be written.
|
||||
fn size_hint(&mut self, _min: usize, _max: Option<usize>) {}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum CursorError {
|
||||
EndOfBuffer
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cursor<T> {
|
||||
inner: T,
|
||||
pos: usize
|
||||
}
|
||||
|
||||
impl<T> Cursor<T> {
|
||||
pub fn new(inner: T) -> Cursor<T> {
|
||||
Cursor { inner, pos: 0 }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
pub fn position(&self) -> usize {
|
||||
self.pos
|
||||
|
||||
}
|
||||
|
||||
pub fn set_position(&mut self, pos: usize) {
|
||||
self.pos = pos
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> Read for Cursor<T> {
|
||||
type ReadError = !;
|
||||
|
||||
fn read(&mut self, buf: &mut [u8]) -> result::Result<usize, Self::ReadError> {
|
||||
let data = &self.inner.as_ref()[self.pos..];
|
||||
let len = buf.len().min(data.len());
|
||||
buf[..len].copy_from_slice(&data[..len]);
|
||||
self.pos += len;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsMut<[u8]>> Write for Cursor<T> {
|
||||
type WriteError = !;
|
||||
type FlushError = !;
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> result::Result<usize, Self::WriteError> {
|
||||
let data = &mut self.inner.as_mut()[self.pos..];
|
||||
let len = buf.len().min(data.len());
|
||||
data[..len].copy_from_slice(&buf[..len]);
|
||||
self.pos += len;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> result::Result<(), Self::FlushError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<T: ::alloc::Vec<[u8]>> Write for Cursor<T> {
|
||||
type WriteError = !;
|
||||
type FlushError = !;
|
||||
|
||||
fn write(&mut self, buf: &[u8]) -> result::Result<usize, Self::WriteError> {
|
||||
self.inner.extend(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> result::Result<(), Self::FlushError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
132
artiq/firmware/libio/proto.rs
Normal file
132
artiq/firmware/libio/proto.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use ::{Read, Write, Error as IoError};
|
||||
|
||||
pub trait ProtoRead {
|
||||
type ReadError;
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>;
|
||||
|
||||
fn read_u8(&mut self) -> Result<u8, Self::ReadError> {
|
||||
let mut bytes = [0; 1];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(bytes[0])
|
||||
}
|
||||
|
||||
fn read_u16(&mut self) -> Result<u16, Self::ReadError> {
|
||||
let mut bytes = [0; 2];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u16(&bytes))
|
||||
}
|
||||
|
||||
fn read_u32(&mut self) -> Result<u32, Self::ReadError> {
|
||||
let mut bytes = [0; 4];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u32(&bytes))
|
||||
}
|
||||
|
||||
fn read_u64(&mut self) -> Result<u64, Self::ReadError> {
|
||||
let mut bytes = [0; 8];
|
||||
self.read_exact(&mut bytes)?;
|
||||
Ok(NetworkEndian::read_u64(&bytes))
|
||||
}
|
||||
|
||||
fn read_bool(&mut self) -> Result<bool, Self::ReadError> {
|
||||
Ok(self.read_u8()? != 0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_bytes(&mut self) -> Result<::alloc::Vec<u8>, Self::ReadError> {
|
||||
let length = self.read_u32()?;
|
||||
let mut value = vec![0; length as usize];
|
||||
self.read_exact(&mut value)?;
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
// fn read_string(&mut self) -> Result<String, Self::ReadError> {
|
||||
// let bytes = self.read_bytes()?;
|
||||
// String::from_utf8(bytes)
|
||||
// .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid UTF-8"))
|
||||
// }
|
||||
}
|
||||
|
||||
pub trait ProtoWrite {
|
||||
type WriteError;
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>;
|
||||
|
||||
fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> {
|
||||
let bytes = [value; 1];
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> {
|
||||
let bytes = [value as u8; 1];
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_u16(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 2];
|
||||
NetworkEndian::write_i16(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_u32(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 4];
|
||||
NetworkEndian::write_i32(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_u64(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> {
|
||||
let mut bytes = [0; 8];
|
||||
NetworkEndian::write_i64(&mut bytes, value);
|
||||
self.write_all(&bytes)
|
||||
}
|
||||
|
||||
fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> {
|
||||
self.write_u8(value as u8)
|
||||
}
|
||||
|
||||
fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> {
|
||||
self.write_u32(value.len() as u32)?;
|
||||
self.write_all(value)
|
||||
}
|
||||
|
||||
fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> {
|
||||
self.write_bytes(value.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ProtoRead for T where T: Read {
|
||||
type ReadError = IoError<T::ReadError>;
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> {
|
||||
T::read_exact(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ProtoWrite for T where T: Write {
|
||||
type WriteError = IoError<T::WriteError>;
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> {
|
||||
T::write_all(self, buf)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user