2024-05-29 18:39:54 +02:00
|
|
|
#![no_std]
|
|
|
|
|
|
|
|
#[cfg(feature = "alloc")]
|
|
|
|
extern crate alloc;
|
|
|
|
#[cfg(any(feature = "std", test))]
|
|
|
|
extern crate std;
|
|
|
|
|
2024-05-31 12:35:29 +02:00
|
|
|
pub mod config;
|
2024-05-31 12:52:55 +02:00
|
|
|
pub mod ffi;
|
2024-05-31 12:35:29 +02:00
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
use core::time::Duration;
|
|
|
|
|
|
|
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|
|
|
|
2024-05-29 18:39:54 +02:00
|
|
|
use bitflags::bitflags;
|
2024-05-31 10:43:16 +02:00
|
|
|
use ffi::{csp_conn_s, csp_packet_s, csp_socket_s};
|
2024-05-29 18:39:54 +02:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
|
|
pub enum ReservedPorts {
|
|
|
|
Cmp = 0,
|
|
|
|
Ping = 1,
|
|
|
|
Ps = 2,
|
|
|
|
Memfree = 3,
|
|
|
|
Reboot = 4,
|
|
|
|
BufFree = 5,
|
|
|
|
Uptime = 6,
|
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)]
|
2024-05-29 18:39:54 +02:00
|
|
|
#[repr(i32)]
|
|
|
|
pub enum CspError {
|
|
|
|
None = 0,
|
|
|
|
NoMem = -1,
|
|
|
|
Inval = -2,
|
|
|
|
TimedOut = -3,
|
|
|
|
Used = -4,
|
|
|
|
NotSup = -5,
|
|
|
|
Busy = -6,
|
|
|
|
Already = -7,
|
|
|
|
Reset = -8,
|
|
|
|
NoBufs = -9,
|
|
|
|
Tx = -10,
|
|
|
|
Driver = -11,
|
|
|
|
Again = -12,
|
|
|
|
NoSys = -38,
|
|
|
|
Hmac = -100,
|
|
|
|
Crc32 = -102,
|
|
|
|
Sfp = -103,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Listen on all ports, primarily used with [csp_bind]
|
2024-05-31 10:43:16 +02:00
|
|
|
pub const CSP_ANY: u8 = 255;
|
2024-05-31 15:43:54 +02:00
|
|
|
pub const CSP_LOOPBACK: u16 = 0;
|
2024-05-29 18:39:54 +02:00
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
pub struct SocketFlags: u32 {
|
|
|
|
const NONE = 0x0000;
|
|
|
|
/// RDP required.
|
|
|
|
const RDPREQ = 0x0001;
|
|
|
|
/// RDP prohibited.
|
|
|
|
const RDPPROHIB = 0x0002;
|
|
|
|
/// HMAC required
|
|
|
|
const HMACREQ = 0x0004;
|
|
|
|
/// HMAC prohibited.
|
|
|
|
const HMACPROHIB = 0x0008;
|
|
|
|
/// CRC32 required.
|
|
|
|
const CRC32REQ = 0x0040;
|
|
|
|
const CRC32PROHIB = 0x0080;
|
|
|
|
const CONN_LESS = 0x0100;
|
|
|
|
/// Copy opts from incoming packets. Only applies to [csp_sendto_reply]
|
|
|
|
const SAME = 0x8000;
|
|
|
|
|
|
|
|
// The source may set any bits
|
|
|
|
const _ = !0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bitflags! {
|
|
|
|
pub struct ConnectOpts: u32 {
|
|
|
|
const NONE = SocketFlags::NONE.bits();
|
|
|
|
|
|
|
|
const RDP = SocketFlags::RDPREQ.bits();
|
|
|
|
const NORDP = SocketFlags::RDPPROHIB.bits();
|
|
|
|
const HMAC = SocketFlags::HMACREQ.bits();
|
|
|
|
const NOHMAC = SocketFlags::HMACPROHIB.bits();
|
|
|
|
const CRC32 = SocketFlags::CRC32REQ.bits();
|
|
|
|
const NOCRC32 = SocketFlags::CRC32PROHIB.bits();
|
|
|
|
const SAME = SocketFlags::SAME.bits();
|
|
|
|
|
|
|
|
// The source may set any bits
|
|
|
|
const _ = !0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
|
|
|
pub enum MsgPriority {
|
|
|
|
Critical = 0,
|
|
|
|
High = 1,
|
|
|
|
Normal = 2,
|
|
|
|
Low = 3,
|
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
pub struct CspPacket(pub csp_packet_s);
|
2024-05-29 18:39:54 +02:00
|
|
|
|
2024-05-31 15:43:54 +02:00
|
|
|
pub struct CspPacketRef<'a>(&'a mut csp_packet_s);
|
2024-05-29 18:39:54 +02:00
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
impl<'a> CspPacketRef<'a> {
|
2024-05-31 15:43:54 +02:00
|
|
|
pub fn packet_data(&self) -> &[u8] {
|
|
|
|
unsafe { &self.0.packet_data_union.data[..self.packet_length()] }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn whole_data(&self) -> &[u8; ffi::CSP_BUFFER_SIZE] {
|
2024-05-31 10:43:16 +02:00
|
|
|
unsafe { &self.0.packet_data_union.data }
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 15:43:54 +02:00
|
|
|
pub fn whole_data_mut(&mut self) -> &mut [u8; ffi::CSP_BUFFER_SIZE] {
|
|
|
|
unsafe { &mut self.0.packet_data_union.data }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_data(&mut self, data: &[u8]) -> bool {
|
|
|
|
if data.len() > self.whole_data().len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
self.whole_data_mut()[0..data.len()].copy_from_slice(data);
|
|
|
|
self.0.length = data.len() as u16;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn packet_length(&self) -> usize {
|
|
|
|
self.0.length.into()
|
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
pub fn inner(&self) -> *const csp_packet_s {
|
|
|
|
self.0
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
pub fn inner_mut(&self) -> *const csp_packet_s {
|
|
|
|
self.0
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CspPacket {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for CspPacket {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self(csp_packet_s {
|
2024-05-29 20:14:53 +02:00
|
|
|
packet_info: Default::default(),
|
2024-05-29 18:39:54 +02:00
|
|
|
length: Default::default(),
|
|
|
|
id: Default::default(),
|
|
|
|
next: core::ptr::null_mut(),
|
|
|
|
header: Default::default(),
|
2024-05-29 20:14:53 +02:00
|
|
|
packet_data_union: Default::default(),
|
2024-05-29 18:39:54 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
#[derive(Default)]
|
|
|
|
pub struct CspSocket(pub csp_socket_s);
|
|
|
|
|
|
|
|
impl CspSocket {
|
|
|
|
pub fn inner_as_mut_ptr(&mut self) -> *mut csp_socket_s {
|
|
|
|
&mut self.0
|
|
|
|
}
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
/// Rust wrapper for [ffi::csp_init]. Initialize the CSP stack.
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// - You must call this function only once.
|
|
|
|
pub unsafe fn csp_init() {
|
|
|
|
// SAFETY: FFI call
|
|
|
|
unsafe {
|
|
|
|
ffi::csp_init();
|
|
|
|
}
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 10:43:16 +02:00
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_bind].
|
|
|
|
pub fn csp_bind(socket: &mut CspSocket, port: u8) {
|
|
|
|
// SAFETY: FFI call
|
|
|
|
unsafe {
|
|
|
|
ffi::csp_bind(socket.inner_as_mut_ptr(), port);
|
|
|
|
}
|
2024-05-29 13:56:26 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
/// Rust wrapper for [ffi::csp_listen].
|
|
|
|
pub fn csp_listen(socket: &mut CspSocket, backlog: usize) {
|
|
|
|
// SAFETY: FFI call
|
|
|
|
unsafe {
|
|
|
|
ffi::csp_listen(socket.inner_as_mut_ptr(), backlog);
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 10:43:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_route_work].
|
|
|
|
pub fn csp_route_work_raw() -> i32 {
|
|
|
|
unsafe { ffi::csp_route_work() }
|
|
|
|
}
|
2024-05-29 18:39:54 +02:00
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
/// Rust wrapper for [ffi::csp_route_work] which also converts errors to the [CspError] type.
|
|
|
|
/// This function will panic if the returned error type is not among the known values of
|
|
|
|
/// [CspError].
|
|
|
|
///
|
|
|
|
/// [csp_route_work_raw] can be used if this is not acceptable.
|
|
|
|
pub fn csp_route_work() -> Result<(), CspError> {
|
|
|
|
let result = unsafe { ffi::csp_route_work() };
|
|
|
|
if result == CspError::None as i32 {
|
|
|
|
return Ok(());
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 15:43:54 +02:00
|
|
|
Err(CspError::try_from(result)
|
|
|
|
.unwrap_or_else(|_| panic!("unexpected error value {} from csp_route_work", result)))
|
2024-05-31 10:43:16 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 15:43:54 +02:00
|
|
|
#[derive(Debug, Copy, Clone)]
|
2024-05-31 10:43:16 +02:00
|
|
|
pub struct CspConn(csp_conn_s);
|
2024-05-29 18:39:54 +02:00
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
impl CspConn {
|
|
|
|
fn new(address: u8) -> Self {
|
|
|
|
Self(csp_conn_s { address })
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
pub fn addr(&self) -> u8 {
|
|
|
|
self.0.address
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 10:43:16 +02:00
|
|
|
}
|
|
|
|
|
2024-05-31 15:43:54 +02:00
|
|
|
pub struct CspConnGuard(pub CspConn);
|
|
|
|
|
|
|
|
impl Drop for CspConnGuard {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
csp_close(self.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<CspConn> for CspConnGuard {
|
|
|
|
fn as_ref(&self) -> &CspConn {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsMut<CspConn> for CspConnGuard {
|
|
|
|
fn as_mut(&mut self) -> &mut CspConn {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn csp_accept_guarded(socket: &mut CspSocket, timeout: Duration) -> Option<CspConnGuard> {
|
|
|
|
Some(CspConnGuard(csp_accept(socket, timeout)?))
|
|
|
|
}
|
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
/// Rust wrapper for [ffi::csp_accept].
|
|
|
|
pub fn csp_accept(socket: &mut CspSocket, timeout: Duration) -> Option<CspConn> {
|
|
|
|
let timeout_millis = timeout.as_millis();
|
|
|
|
if timeout_millis > u32::MAX as u128 {
|
|
|
|
return None;
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 10:43:16 +02:00
|
|
|
Some(CspConn::new(unsafe {
|
|
|
|
let addr = ffi::csp_accept(socket.inner_as_mut_ptr(), timeout_millis as u32);
|
|
|
|
if addr.is_null() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
(*addr).address
|
|
|
|
}))
|
|
|
|
}
|
2024-05-29 18:39:54 +02:00
|
|
|
|
2024-05-31 10:43:16 +02:00
|
|
|
/// Rust wrapper for [ffi::csp_read].
|
2024-05-31 15:43:54 +02:00
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// - You MUST ensure that a connection is active when calling this function.
|
|
|
|
pub unsafe fn csp_read<'a>(mut conn: CspConn, timeout: Duration) -> Option<CspPacketRef<'a>> {
|
2024-05-31 10:43:16 +02:00
|
|
|
let timeout_millis = timeout.as_millis();
|
|
|
|
if timeout_millis > u32::MAX as u128 {
|
|
|
|
return None;
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 10:43:16 +02:00
|
|
|
let opt_packet = unsafe { ffi::csp_read(&mut conn.0, timeout_millis as u32) };
|
|
|
|
if opt_packet.is_null() {
|
|
|
|
return None;
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 15:43:54 +02:00
|
|
|
// SAFETY: FFI pointer.
|
2024-05-31 10:43:16 +02:00
|
|
|
Some(CspPacketRef(unsafe { &mut *opt_packet }))
|
2024-05-29 18:39:54 +02:00
|
|
|
}
|
2024-05-31 15:43:54 +02:00
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_conn_dport].
|
|
|
|
pub fn csp_conn_dport(mut conn: CspConn) -> i32 {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe { ffi::csp_conn_dport(&mut conn.0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn csp_service_handler(packet: &mut CspPacketRef) {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe { ffi::csp_service_handler(&mut *packet.0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_close].
|
|
|
|
pub fn csp_close(mut conn: CspConn) -> i32 {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe { ffi::csp_close(&mut conn.0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_ping], returns the result code directly.
|
|
|
|
pub fn csp_ping_raw(node: u16, timeout: Duration, size: usize, opts: SocketFlags) -> i32 {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe {
|
|
|
|
ffi::csp_ping(
|
|
|
|
node,
|
|
|
|
timeout.as_millis() as u32,
|
|
|
|
size as u32,
|
|
|
|
opts.bits() as u8,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_ping].
|
|
|
|
pub fn csp_ping(
|
|
|
|
node: u16,
|
|
|
|
timeout: Duration,
|
|
|
|
size: usize,
|
|
|
|
opts: SocketFlags,
|
|
|
|
) -> Result<(), CspError> {
|
|
|
|
let result = csp_ping_raw(node, timeout, size, opts);
|
|
|
|
if result == CspError::None as i32 {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
Err(CspError::try_from(result)
|
|
|
|
.unwrap_or_else(|_| panic!("unexpected error value {} from csp_ping", result)))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_reboot].
|
|
|
|
pub fn csp_reboot(node: u16) {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe { ffi::csp_reboot(node) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_connect].
|
|
|
|
pub fn csp_connect(
|
|
|
|
prio: MsgPriority,
|
|
|
|
dst: u16,
|
|
|
|
dst_port: u8,
|
|
|
|
timeout: Duration,
|
|
|
|
opts: ConnectOpts,
|
|
|
|
) -> Option<CspConn> {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
let conn = unsafe {
|
|
|
|
ffi::csp_connect(
|
|
|
|
prio as u8,
|
|
|
|
dst,
|
|
|
|
dst_port,
|
|
|
|
timeout.as_millis() as u32,
|
|
|
|
opts.bits(),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
if conn.is_null() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
// SAFETY: We checked that the pointer is valid.
|
|
|
|
Some(CspConn::new(unsafe { *conn }.address))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_connect] which returns a guard structure. The connection will be
|
|
|
|
/// be closed automatically when the guard structure is dropped.
|
|
|
|
pub fn csp_connect_guarded(
|
|
|
|
prio: MsgPriority,
|
|
|
|
dst: u16,
|
|
|
|
dst_port: u8,
|
|
|
|
timeout: Duration,
|
|
|
|
opts: ConnectOpts,
|
|
|
|
) -> Option<CspConnGuard> {
|
|
|
|
Some(CspConnGuard(csp_connect(
|
|
|
|
prio, dst, dst_port, timeout, opts,
|
|
|
|
)?))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_buffer_get].
|
|
|
|
pub fn csp_buffer_get() -> Option<CspPacketRef<'static>> {
|
|
|
|
let packet_ref = unsafe {
|
|
|
|
// The size argument is unused
|
|
|
|
ffi::csp_buffer_get(0)
|
|
|
|
};
|
|
|
|
if packet_ref.is_null() {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
// SAFETY: We checked that the pointer is valid.
|
|
|
|
Some(CspPacketRef(unsafe { &mut *packet_ref }))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_send].
|
|
|
|
pub fn csp_send(mut conn: CspConn, packet: CspPacketRef) {
|
|
|
|
// SAFETY: FFI call.
|
|
|
|
unsafe { ffi::csp_send(&mut conn.0, packet.0) }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_conn_print_table].
|
|
|
|
pub fn csp_conn_print_table() {
|
|
|
|
unsafe { ffi::csp_conn_print_table() }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Rust wrapper for [ffi::csp_iflist_print].
|
|
|
|
pub fn csp_iflist_print() {
|
|
|
|
unsafe { ffi::csp_iflist_print() }
|
|
|
|
}
|