diff --git a/Cargo.lock b/Cargo.lock index 05cf2ab..9097735 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,28 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "libcsp-cargo-build" version = "0.1.0" @@ -26,6 +48,7 @@ name = "libcsp-rust" version = "0.1.0" dependencies = [ "bitflags", + "num_enum", ] [[package]] @@ -35,3 +58,100 @@ dependencies = [ "libcsp-cargo-build", "libcsp-rust", ] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] diff --git a/examples/src/main.rs b/examples/src/main.rs index e5396ec..8c8a860 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -1,12 +1,303 @@ -use std::ffi::CString; +use std::{thread, time::Duration}; -use libcsp_rust::{csp_init, csp_print_func}; +use libcsp_rust::{ + csp_accept, csp_bind, csp_init, csp_listen, csp_read, csp_route_work, CspSocket, CSP_ANY, +}; + +/* + +#include +#include +#include +#include + +#include +#include +#include +#include + + +/* These three functions must be provided in arch specific way */ +int router_start(void); +int server_start(void); +int client_start(void); + +/* Server port, the port the server listens on for incoming connections from the client. */ +#define MY_SERVER_PORT 10 + +/* Commandline options */ +static uint8_t server_address = 255; + +/* test mode, used for verifying that host & client can exchange packets over the loopback interface */ +static bool test_mode = false; +static unsigned int server_received = 0; +static unsigned int run_duration_in_sec = 3; + +/* Server task - handles requests from clients */ +void server(void) { + + csp_print("Server task started\n"); + + /* Create socket with no specific socket options, e.g. accepts CRC32, HMAC, etc. if enabled during compilation */ + csp_socket_t sock = {0}; + + /* Bind socket to all ports, e.g. all incoming connections will be handled here */ + csp_bind(&sock, CSP_ANY); + + /* Create a backlog of 10 connections, i.e. up to 10 new connections can be queued */ + csp_listen(&sock, 10); + + /* Wait for connections and then process packets on the connection */ + while (1) { + + /* Wait for a new connection, 10000 mS timeout */ + csp_conn_t *conn; + if ((conn = csp_accept(&sock, 10000)) == NULL) { + /* timeout */ + continue; + } + + /* Read packets on connection, timout is 100 mS */ + csp_packet_t *packet; + while ((packet = csp_read(conn, 50)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_SERVER_PORT: + /* Process packet here */ + csp_print("Packet received on MY_SERVER_PORT: %s\n", (char *) packet->data); + csp_buffer_free(packet); + ++server_received; + break; + + default: + /* Call the default CSP service handler, handle pings, buffer use, etc. */ + csp_service_handler(packet); + break; + } + } + + /* Close current connection */ + csp_close(conn); + + } + + return; + +} +/* End of server task */ + +/* Client task sending requests to server task */ +void client(void) { + + csp_print("Client task started\n"); + + unsigned int count = 'A'; + + while (1) { + + usleep(test_mode ? 200000 : 1000000); + + /* Send ping to server, timeout 1000 mS, ping size 100 bytes */ + int result = csp_ping(server_address, 1000, 100, CSP_O_NONE); + csp_print("Ping address: %u, result %d [mS]\n", server_address, result); + (void) result; + + /* Send reboot request to server, the server has no actual implementation of csp_sys_reboot() and fails to reboot */ + csp_reboot(server_address); + csp_print("reboot system request sent to address: %u\n", server_address); + + /* Send data packet (string) to server */ + + /* 1. Connect to host on 'server_address', port MY_SERVER_PORT with regular UDP-like protocol and 1000 ms timeout */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, server_address, MY_SERVER_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + /* Connect failed */ + csp_print("Connection failed\n"); + return; + } + + /* 2. Get packet buffer for message/data */ + csp_packet_t * packet = csp_buffer_get(100); + if (packet == NULL) { + /* Could not get buffer element */ + csp_print("Failed to get CSP buffer\n"); + return; + } + + /* 3. Copy data to packet */ + memcpy(packet->data, "Hello world ", 12); + memcpy(packet->data + 12, &count, 1); + memset(packet->data + 13, 0, 1); + count++; + + /* 4. Set packet length */ + packet->length = (strlen((char *) packet->data) + 1); /* include the 0 termination */ + + /* 5. Send packet */ + csp_send(conn, packet); + + /* 6. Close connection */ + csp_close(conn); + } + + return; +} +/* End of client task */ + +static void print_usage(void) +{ + csp_print("Usage:\n" + " -t enable test mode\n" + " -T enable test mode with running time in seconds\n" + " -h print help\n"); +} + +/* main - initialization of CSP and start of server/client tasks */ +int main(int argc, char * argv[]) { + + uint8_t address = 0; + int opt; + while ((opt = getopt(argc, argv, "tT:h")) != -1) { + switch (opt) { + case 'a': + address = atoi(optarg); + break; + case 'r': + server_address = atoi(optarg); + break; + case 't': + test_mode = true; + break; + case 'T': + test_mode = true; + run_duration_in_sec = atoi(optarg); + break; + case 'h': + print_usage(); + exit(0); + break; + default: + print_usage(); + exit(1); + break; + } + } + + csp_print("Initialising CSP"); + + /* Init CSP */ + csp_init(); + + /* Start router */ + router_start(); + + /* Add interface(s) */ + csp_iface_t * default_iface = NULL; + if (!default_iface) { + /* no interfaces configured - run server and client in process, using loopback interface */ + server_address = address; + } + + csp_print("Connection table\r\n"); + csp_conn_print_table(); + + csp_print("Interfaces\r\n"); + csp_iflist_print(); + + /* Start server thread */ + server_start(); + + /* Start client thread */ + client_start(); + + /* Wait for execution to end (ctrl+c) */ + while(1) { + sleep(run_duration_in_sec); + + if (test_mode) { + /* Test mode is intended for checking that host & client can exchange packets over loopback */ + if (server_received < 5) { + csp_print("Server received %u packets\n", server_received); + exit(1); + } + csp_print("Server received %u packets\n", server_received); + exit(0); + } + } + + return 0; +} +*/ fn main() { - println!("Hello, world!"); - unsafe { - csp_init(); - let c_str = CString::new("hello world\n").unwrap(); - csp_print_func(c_str.as_ptr()); + println!("CSP server example"); + // SAFETY: We only call this once. + unsafe { csp_init() }; + + let csp_router_jh = thread::spawn(|| loop { + if let Err(e) = csp_route_work() { + match e { + libcsp_rust::CspError::TimedOut => continue, + e => { + println!("CSP router error: {:?}", e); + break; + } + } + } + }); + + server(); + + csp_router_jh.join().unwrap(); +} + +fn server() { + // Create socket with no specific socket options, e.g. accepts CRC32, HMAC, etc. if enabled + // during compilation + let mut csp_socket = CspSocket::default(); + + // Bind socket to all ports, e.g. all incoming connections will be handled here + csp_bind(&mut csp_socket, CSP_ANY); + + // Create a backlog of 10 connections, i.e. up to 10 new connections can be queued + csp_listen(&mut csp_socket, 10); + + // Wait for connections and then process packets on the connection + loop { + // Wait for a new connection, 10000 mS timeout + + let conn = csp_accept(&mut csp_socket, Duration::from_millis(10000)); + if conn.is_none() { + continue; + } + let mut conn = conn.unwrap(); + + // Read packets on connection, timout is 100 mS + // csp_packet_t *packet; + loop { + let packet = csp_read(&mut conn, Duration::from_millis(100)); + if packet.is_none() { + break; + } + } + /* + while ((packet = csp_read(conn, 50)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_SERVER_PORT: + /* Process packet here */ + csp_print("Packet received on MY_SERVER_PORT: %s\n", (char *) packet->data); + csp_buffer_free(packet); + ++server_received; + break; + + default: + /* Call the default CSP service handler, handle pings, buffer use, etc. */ + csp_service_handler(packet); + break; + } + } + */ + + /* Close current connection */ + // csp_close(conn); } } diff --git a/libcsp-cargo-build/src/lib.rs b/libcsp-cargo-build/src/lib.rs index 3806e44..1ba5686 100644 --- a/libcsp-cargo-build/src/lib.rs +++ b/libcsp-cargo-build/src/lib.rs @@ -45,10 +45,13 @@ const SRCS_LIST: &[&str] = &[ "csp_qfifo.c", "csp_port.c", "csp_route.c", + "csp_dedup.c", "interfaces/csp_if_lo.c", "interfaces/csp_if_kiss.c", "interfaces/csp_if_tun.c", "interfaces/csp_if_udp.c", + "crypto/csp_hmac.c", + "crypto/csp_sha1.c", ]; const ARCH_SRCS_UNIX: &[&str] = &[ diff --git a/libcsp-rust/Cargo.toml b/libcsp-rust/Cargo.toml index f1b195e..3831407 100644 --- a/libcsp-rust/Cargo.toml +++ b/libcsp-rust/Cargo.toml @@ -6,3 +6,4 @@ links = "csp" [dependencies] bitflags = "2" +num_enum = "0.7" diff --git a/libcsp-rust/build.rs b/libcsp-rust/build.rs index f328e4d..ea1fe36 100644 --- a/libcsp-rust/build.rs +++ b/libcsp-rust/build.rs @@ -1 +1,3 @@ -fn main() {} +fn main() { + println!("cargo:rustc-link-lib=csp") +} diff --git a/libcsp-rust/src/ffi.rs b/libcsp-rust/src/ffi.rs new file mode 100644 index 0000000..65ec200 --- /dev/null +++ b/libcsp-rust/src/ffi.rs @@ -0,0 +1,624 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +// These constants come from the autoconfig file, which is supposed to be configurable.. +// How do we deal with this? Rust libraries are not supposed to be configured like this. +pub const CSP_BUFFER_SIZE: usize = 256; +pub const CSP_CONN_RXQUEUE_LEN: usize = 16; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct csp_timestamp_t { + pub tv_sec: u32, + pub tv_nsec: u32, +} + +#[doc = "CSP identifier/header."] +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct csp_id_t { + pub pri: u8, + pub flags: u8, + pub src: u16, + pub dst: u16, + pub dport: u8, + pub sport: u8, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct csp_conn_s { + pub address: u8, +} + +#[doc = " CSP Packet.\n\n This structure is constructed to fit with all interface and protocols to prevent the\n need to copy data (zero copy).\n\n .. note:: In most cases a CSP packet cannot be reused in case of send failure, because the\n \t\t\t lower layers may add additional data causing increased length (e.g. CRC32), convert\n \t\t\t the CSP id to different endian (e.g. I2C), etc.\n"] +#[repr(C)] +#[derive(Copy, Clone)] +pub struct csp_packet_s { + pub packet_info: csp_packet_s_anon_union, + pub length: u16, + pub id: csp_id_t, + pub next: *mut csp_packet_s, + #[doc = " Additional header bytes, to prepend packed data before transmission\n This must be minimum 6 bytes to accomodate CSP 2.0. But some implementations\n require much more scratch working area for encryption for example.\n\n Ultimately after csp_id_pack() this area will be filled with the CSP header"] + pub header: [u8; 8usize], + pub packet_data_union: csp_packet_s_data_union, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub union csp_packet_s_anon_union { + pub rdp_only: csp_packet_s_anon_union_field_rdp_only, + pub rx_tx_only: csp_packet_s_anon_union_field_rx_tx_only, +} + +impl Default for csp_packet_s_anon_union { + fn default() -> Self { + Self { + rdp_only: Default::default(), + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct csp_packet_s_anon_union_field_rdp_only { + pub rdp_quarantine: u32, + pub timestamp_tx: u32, + pub timestamp_rx: u32, + pub conn: *mut csp_conn_s, +} + +impl Default for csp_packet_s_anon_union_field_rdp_only { + fn default() -> Self { + Self { + rdp_quarantine: Default::default(), + timestamp_tx: Default::default(), + timestamp_rx: Default::default(), + conn: core::ptr::null_mut(), + } + } +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct csp_packet_s_anon_union_field_rx_tx_only { + pub rx_count: u16, + pub remain: u16, + pub cfpid: u32, + pub last_used: u32, + pub frame_begin: *mut u8, + pub frame_length: u16, +} + +impl Default for csp_packet_s_anon_union_field_rx_tx_only { + fn default() -> Self { + Self { + rx_count: Default::default(), + remain: Default::default(), + cfpid: Default::default(), + last_used: Default::default(), + frame_begin: core::ptr::null_mut(), + frame_length: Default::default(), + } + } +} + +#[doc = " Data part of packet:"] +#[repr(C)] +#[derive(Copy, Clone)] +pub union csp_packet_s_data_union { + pub data: [u8; CSP_BUFFER_SIZE], + pub data16: [u16; CSP_BUFFER_SIZE / 2usize], + pub data32: [u32; CSP_BUFFER_SIZE / 4usize], +} + +impl Default for csp_packet_s_data_union { + fn default() -> Self { + Self { + data: [0; CSP_BUFFER_SIZE], + } + } +} + +#[doc = " CSP Packet.\n\n This structure is constructed to fit with all interface and protocols to prevent the\n need to copy data (zero copy).\n\n .. note:: In most cases a CSP packet cannot be reused in case of send failure, because the\n \t\t\t lower layers may add additional data causing increased length (e.g. CRC32), convert\n \t\t\t the CSP id to different endian (e.g. I2C), etc.\n"] +pub type csp_packet_t = csp_packet_s; + +pub type csp_queue_handle_t = *mut core::ffi::c_void; +pub type csp_static_queue_t = *mut core::ffi::c_void; + +#[doc = " @brief Connection struct"] +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct csp_socket_s { + pub rx_queue: csp_queue_handle_t, + pub rx_queue_static: csp_static_queue_t, + pub rx_queue_static_data: + [core::ffi::c_char; CSP_CONN_RXQUEUE_LEN * core::mem::size_of::<*const csp_packet_s>()], + pub opts: u32, +} + +impl Default for csp_socket_s { + fn default() -> Self { + Self { + rx_queue: core::ptr::null_mut(), + rx_queue_static: core::ptr::null_mut(), + rx_queue_static_data: [0; CSP_CONN_RXQUEUE_LEN + * core::mem::size_of::<*const csp_packet_s>()], + opts: Default::default(), + } + } +} + +#[doc = " Forward declaration of socket structure"] +pub type csp_socket_t = csp_socket_s; + +#[doc = " Forward declaration of connection structure"] +pub type csp_conn_t = csp_conn_s; + +extern "C" { + #[doc = " Error counters"] + pub static mut csp_dbg_buffer_out: u8; + pub static mut csp_dbg_conn_out: u8; + pub static mut csp_dbg_conn_ovf: u8; + pub static mut csp_dbg_conn_noroute: u8; + pub static mut csp_dbg_inval_reply: u8; + pub static mut csp_dbg_errno: u8; + pub static mut csp_dbg_can_errno: u8; + pub static mut csp_dbg_eth_errno: u8; + pub static mut csp_dbg_rdp_print: u8; + pub static mut csp_dbg_packet_print: u8; + + #[doc = " Initialize CSP.\n This will configure basic structures."] + pub fn csp_init(); + + pub fn csp_print_func(fmt: *const core::ffi::c_char, ...); + + #[doc = " Bind port to socket.\n\n @param[in] socket socket to bind port to\n @param[in] port port number to bind, use #CSP_ANY for all ports. Bindnig to a specific will take precedence over #CSP_ANY.\n @return #CSP_ERR_NONE on success, otherwise an error code."] + pub fn csp_bind(socket: *mut csp_socket_t, port: u8) -> core::ffi::c_int; + + #[doc = " Set socket to listen for incoming connections.\n\n @param[in] socket socket\n @param[in] backlog max length of backlog queue. The backlog queue holds incoming connections, waiting to be returned by call to csp_accept().\n @return #CSP_ERR_NONE on success, otherwise an error code."] + pub fn csp_listen(socket: *mut csp_socket_t, backlog: usize) -> ::core::ffi::c_int; + + #[doc = " Route packet from the incoming router queue and check RDP timeouts.\n In order for incoming packets to routed and RDP timeouts to be checked, this function must be called reguarly.\n @return #CSP_ERR_NONE on success, otherwise an error code."] + pub fn csp_route_work() -> ::core::ffi::c_int; + + #[doc = " Wait/accept a new connection.\n\n @param[in] socket socket to accept connections on, created by calling csp_socket().\n @param[in] timeout timeout in mS to wait for a connection, use CSP_MAX_TIMEOUT for infinite timeout.\n @return New connection on success, NULL on failure or timeout."] + pub fn csp_accept(socket: *mut csp_socket_t, timeout: u32) -> *mut csp_conn_t; + + #[doc = " Read packet from a connection.\n This fuction will wait on the connection's RX queue for the specified timeout.\n\n @param[in] conn connection\n @param[in] timeout timeout in mS to wait for a packet, use CSP_MAX_TIMEOUT for infinite timeout.\n @return Packet or NULL in case of failure or timeout."] + pub fn csp_read(conn: *mut csp_conn_t, timeout: u32) -> *mut csp_packet_t; +} + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::{align_of, size_of}; + use std::mem::MaybeUninit; + + #[test] + fn bindgen_test_layout_csp_timestamp_t() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::core::mem::size_of::(), + 8usize, + concat!("Size of: ", stringify!(csp_timestamp_t)) + ); + assert_eq!( + std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(csp_timestamp_t)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).tv_sec) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_timestamp_t), + "::", + stringify!(tv_sec) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).tv_nsec) as usize - ptr as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(csp_timestamp_t), + "::", + stringify!(tv_nsec) + ) + ); + } + + #[test] + fn bindgen_test_layout_csp_id() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + size_of::(), + 8usize, + concat!("Size of: ", stringify!(__packed)) + ); + assert_eq!( + align_of::(), + 2usize, + concat!("Alignment of ", stringify!(__packed)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).pri) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(pri) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, + 1usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(flags) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).src) as usize - ptr as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(src) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).dst) as usize - ptr as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(dst) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).dport) as usize - ptr as usize }, + 6usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(dport) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).sport) as usize - ptr as usize }, + 7usize, + concat!( + "Offset of field: ", + stringify!(__packed), + "::", + stringify!(sport) + ) + ); + } + + #[test] + fn bindgen_test_layout_csp_packet_s__bindgen_ty_1__bindgen_ty_1() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + size_of::(), + 24usize, + concat!( + "Size of: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rdp_quarantine) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(rdp_quarantine) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).timestamp_tx) as usize - ptr as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(timestamp_tx) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).timestamp_rx) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(timestamp_rx) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).conn) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), + "::", + stringify!(conn) + ) + ); + } + + #[test] + fn bindgen_test_layout_csp_packet_s__bindgen_ty_1__bindgen_ty_2() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + size_of::(), + 32usize, + concat!( + "Size of: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + align_of::(), + 8usize, + concat!( + "Alignment of ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rx_count) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(rx_count) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).remain) as usize - ptr as usize }, + 2usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(remain) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).cfpid) as usize - ptr as usize }, + 4usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(cfpid) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).last_used) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(last_used) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).frame_begin) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(frame_begin) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).frame_length) as usize - ptr as usize }, + 24usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), + "::", + stringify!(frame_length) + ) + ); + } + #[test] + fn bindgen_test_layout_csp_packet_s__bindgen_ty_1() { + assert_eq!( + size_of::(), + 32usize, + concat!("Size of: ", stringify!(csp_packet_s_anon_union)) + ); + assert_eq!( + align_of::(), + 8usize, + concat!("Alignment of ", stringify!(csp_packet_s_anon_union)) + ); + } + + #[test] + fn bindgen_test_layout_csp_packet_s__bindgen_ty_2() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + size_of::(), + 256usize, + concat!("Size of: ", stringify!(csp_packet_s__bindgen_ty_2)) + ); + assert_eq!( + align_of::(), + 4usize, + concat!("Alignment of ", stringify!(csp_packet_s__bindgen_ty_2)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_2), + "::", + stringify!(data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).data16) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_2), + "::", + stringify!(data16) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).data32) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s__bindgen_ty_2), + "::", + stringify!(data32) + ) + ); + } + #[test] + fn bindgen_test_layout_csp_packet_s() { + const UNINIT: MaybeUninit = MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + size_of::(), + 320usize, + concat!("Size of: ", stringify!(csp_packet_s)) + ); + assert_eq!( + align_of::(), + 8usize, + concat!("Alignment of ", stringify!(csp_packet_s)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize }, + 32usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s), + "::", + stringify!(length) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, + 34usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s), + "::", + stringify!(id) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).next) as usize - ptr as usize }, + 48usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s), + "::", + stringify!(next) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).header) as usize - ptr as usize }, + 56usize, + concat!( + "Offset of field: ", + stringify!(csp_packet_s), + "::", + stringify!(header) + ) + ); + } + + #[test] + fn bindgen_test_layout_csp_socket_s() { + const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); + let ptr = UNINIT.as_ptr(); + assert_eq!( + ::std::mem::size_of::(), + 152usize, + concat!("Size of: ", stringify!(csp_socket_s)) + ); + assert_eq!( + ::std::mem::align_of::(), + 8usize, + concat!("Alignment of ", stringify!(csp_socket_s)) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rx_queue) as usize - ptr as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(csp_socket_s), + "::", + stringify!(rx_queue) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rx_queue_static) as usize - ptr as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(csp_socket_s), + "::", + stringify!(rx_queue_static) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).rx_queue_static_data) as usize - ptr as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(csp_socket_s), + "::", + stringify!(rx_queue_static_data) + ) + ); + assert_eq!( + unsafe { ::std::ptr::addr_of!((*ptr).opts) as usize - ptr as usize }, + 144usize, + concat!( + "Offset of field: ", + stringify!(csp_socket_s), + "::", + stringify!(opts) + ) + ); + } +} diff --git a/libcsp-rust/src/lib.rs b/libcsp-rust/src/lib.rs index cedeeef..30848cf 100644 --- a/libcsp-rust/src/lib.rs +++ b/libcsp-rust/src/lib.rs @@ -1,6 +1,3 @@ -#![allow(non_upper_case_globals)] -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] #![no_std] #[cfg(feature = "alloc")] @@ -8,7 +5,13 @@ extern crate alloc; #[cfg(any(feature = "std", test))] extern crate std; +pub mod ffi; +use core::time::Duration; + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + use bitflags::bitflags; +use ffi::{csp_conn_s, csp_packet_s, csp_socket_s}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ReservedPorts { @@ -21,7 +24,7 @@ pub enum ReservedPorts { Uptime = 6, } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, TryFromPrimitive, IntoPrimitive)] #[repr(i32)] pub enum CspError { None = 0, @@ -44,14 +47,7 @@ pub enum CspError { } /// Listen on all ports, primarily used with [csp_bind] -pub const CSP_ANY: u32 = 255; - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct csp_timestamp_t { - pub tv_sec: u32, - pub tv_nsec: u32, -} +pub const CSP_ANY: u8 = 255; bitflags! { pub struct SocketFlags: u32 { @@ -93,18 +89,6 @@ bitflags! { } } -#[doc = "CSP identifier/header."] -#[repr(C)] -#[derive(Debug, Copy, Clone, Default)] -pub struct csp_id_t { - pub pri: u8, - pub flags: u8, - pub src: u16, - pub dst: u16, - pub dport: u8, - pub sport: u8, -} - #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum MsgPriority { Critical = 0, @@ -113,106 +97,24 @@ pub enum MsgPriority { Low = 3, } -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct csp_conn_s { - pub _address: u8, -} - -#[doc = " CSP Packet.\n\n This structure is constructed to fit with all interface and protocols to prevent the\n need to copy data (zero copy).\n\n .. note:: In most cases a CSP packet cannot be reused in case of send failure, because the\n \t\t\t lower layers may add additional data causing increased length (e.g. CRC32), convert\n \t\t\t the CSP id to different endian (e.g. I2C), etc.\n"] -#[repr(C)] -#[derive(Copy, Clone)] -pub struct csp_packet_s { - pub packet_info: csp_packet_s_anon_union, - pub length: u16, - pub id: csp_id_t, - pub next: *mut csp_packet_s, - #[doc = " Additional header bytes, to prepend packed data before transmission\n This must be minimum 6 bytes to accomodate CSP 2.0. But some implementations\n require much more scratch working area for encryption for example.\n\n Ultimately after csp_id_pack() this area will be filled with the CSP header"] - pub header: [u8; 8usize], - pub packet_data_union: csp_packet_s_data_union, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub union csp_packet_s_anon_union { - pub rdp_only: csp_packet_s_anon_union_field_rdp_only, - pub rx_tx_only: csp_packet_s_anon_union_field_rx_tx_only, -} - -impl Default for csp_packet_s_anon_union { - fn default() -> Self { - Self { - rdp_only: Default::default(), - } - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct csp_packet_s_anon_union_field_rdp_only { - pub rdp_quarantine: u32, - pub timestamp_tx: u32, - pub timestamp_rx: u32, - pub conn: *mut csp_conn_s, -} - -impl Default for csp_packet_s_anon_union_field_rdp_only { - fn default() -> Self { - Self { - rdp_quarantine: Default::default(), - timestamp_tx: Default::default(), - timestamp_rx: Default::default(), - conn: core::ptr::null_mut(), - } - } -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct csp_packet_s_anon_union_field_rx_tx_only { - pub rx_count: u16, - pub remain: u16, - pub cfpid: u32, - pub last_used: u32, - pub frame_begin: *mut u8, - pub frame_length: u16, -} - -impl Default for csp_packet_s_anon_union_field_rx_tx_only { - fn default() -> Self { - Self { - rx_count: Default::default(), - remain: Default::default(), - cfpid: Default::default(), - last_used: Default::default(), - frame_begin: core::ptr::null_mut(), - frame_length: Default::default(), - } - } -} - -#[doc = " Data part of packet:"] -#[repr(C)] -#[derive(Copy, Clone)] -pub union csp_packet_s_data_union { - pub data: [u8; 256usize], - pub data16: [u16; 128usize], - pub data32: [u32; 64usize], -} - -impl Default for csp_packet_s_data_union { - fn default() -> Self { - Self { - data: [0; 256usize], - } - } -} - -#[doc = " CSP Packet.\n\n This structure is constructed to fit with all interface and protocols to prevent the\n need to copy data (zero copy).\n\n .. note:: In most cases a CSP packet cannot be reused in case of send failure, because the\n \t\t\t lower layers may add additional data causing increased length (e.g. CRC32), convert\n \t\t\t the CSP id to different endian (e.g. I2C), etc.\n"] -pub type csp_packet_t = csp_packet_s; - pub struct CspPacket(pub csp_packet_s); +pub struct CspPacketRef<'a>(&'a csp_packet_s); + +impl<'a> CspPacketRef<'a> { + pub fn packet_data(&self) -> &'a [u8; ffi::CSP_BUFFER_SIZE] { + unsafe { &self.0.packet_data_union.data } + } + + pub fn inner(&self) -> *const csp_packet_s { + self.0 + } + + pub fn inner_mut(&self) -> *const csp_packet_s { + self.0 + } +} + impl CspPacket { pub fn new() -> Self { Self::default() @@ -232,472 +134,100 @@ impl Default for CspPacket { } } -pub type csp_queue_handle_t = *mut core::ffi::c_void; -pub type csp_static_queue_t = *mut core::ffi::c_void; +#[derive(Default)] +pub struct CspSocket(pub csp_socket_s); -#[doc = " @brief Connection struct"] -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct csp_socket_s { - pub rx_queue: csp_queue_handle_t, - pub rx_queue_static: csp_static_queue_t, - pub rx_queue_static_data: [core::ffi::c_char; 128usize], - pub opts: u32, -} - -#[test] -fn bindgen_test_layout_csp_socket_s() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 152usize, - concat!("Size of: ", stringify!(csp_socket_s)) - ); - assert_eq!( - ::std::mem::align_of::(), - 8usize, - concat!("Alignment of ", stringify!(csp_socket_s)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rx_queue) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_socket_s), - "::", - stringify!(rx_queue) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rx_queue_static) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(csp_socket_s), - "::", - stringify!(rx_queue_static) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rx_queue_static_data) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(csp_socket_s), - "::", - stringify!(rx_queue_static_data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).opts) as usize - ptr as usize }, - 144usize, - concat!( - "Offset of field: ", - stringify!(csp_socket_s), - "::", - stringify!(opts) - ) - ); -} -#[doc = " Forward declaration of socket structure"] -pub type csp_socket_t = csp_socket_s; - -extern "C" { - #[doc = " Error counters"] - pub static mut csp_dbg_buffer_out: u8; - pub static mut csp_dbg_conn_out: u8; - pub static mut csp_dbg_conn_ovf: u8; - pub static mut csp_dbg_conn_noroute: u8; - pub static mut csp_dbg_inval_reply: u8; - pub static mut csp_dbg_errno: u8; - pub static mut csp_dbg_can_errno: u8; - pub static mut csp_dbg_eth_errno: u8; - pub static mut csp_dbg_rdp_print: u8; - pub static mut csp_dbg_packet_print: u8; - - #[doc = " Initialize CSP.\n This will configure basic structures."] - pub fn csp_init(); - - pub fn csp_print_func(fmt: *const core::ffi::c_char, ...); - - #[doc = " Bind port to socket.\n\n @param[in] socket socket to bind port to\n @param[in] port port number to bind, use #CSP_ANY for all ports. Bindnig to a specific will take precedence over #CSP_ANY.\n @return #CSP_ERR_NONE on success, otherwise an error code."] - pub fn csp_bind(socket: *mut csp_socket_t, port: u8) -> core::ffi::c_int; -} - -#[cfg(test)] -mod tests { - use super::*; - use core::mem::{align_of, size_of}; - use std::mem::MaybeUninit; - - #[test] - fn bindgen_test_layout_csp_timestamp_t() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::core::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(csp_timestamp_t)) - ); - assert_eq!( - std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(csp_timestamp_t)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tv_sec) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_timestamp_t), - "::", - stringify!(tv_sec) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tv_nsec) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(csp_timestamp_t), - "::", - stringify!(tv_nsec) - ) - ); - } - - #[test] - fn bindgen_test_layout_csp_id() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - size_of::(), - 8usize, - concat!("Size of: ", stringify!(__packed)) - ); - assert_eq!( - align_of::(), - 2usize, - concat!("Alignment of ", stringify!(__packed)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pri) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(pri) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).flags) as usize - ptr as usize }, - 1usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).src) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(src) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dst) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(dst) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).dport) as usize - ptr as usize }, - 6usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(dport) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).sport) as usize - ptr as usize }, - 7usize, - concat!( - "Offset of field: ", - stringify!(__packed), - "::", - stringify!(sport) - ) - ); - } - - #[test] - fn bindgen_test_layout_csp_packet_s__bindgen_ty_1__bindgen_ty_1() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - size_of::(), - 24usize, - concat!( - "Size of: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - align_of::(), - 8usize, - concat!( - "Alignment of ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rdp_quarantine) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(rdp_quarantine) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).timestamp_tx) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(timestamp_tx) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).timestamp_rx) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(timestamp_rx) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).conn) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_1), - "::", - stringify!(conn) - ) - ); - } - - #[test] - fn bindgen_test_layout_csp_packet_s__bindgen_ty_1__bindgen_ty_2() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - size_of::(), - 32usize, - concat!( - "Size of: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - align_of::(), - 8usize, - concat!( - "Alignment of ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).rx_count) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(rx_count) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).remain) as usize - ptr as usize }, - 2usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(remain) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).cfpid) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(cfpid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).last_used) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(last_used) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).frame_begin) as usize - ptr as usize }, - 16usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(frame_begin) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).frame_length) as usize - ptr as usize }, - 24usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_1__bindgen_ty_2), - "::", - stringify!(frame_length) - ) - ); - } - #[test] - fn bindgen_test_layout_csp_packet_s__bindgen_ty_1() { - assert_eq!( - size_of::(), - 32usize, - concat!("Size of: ", stringify!(csp_packet_s_anon_union)) - ); - assert_eq!( - align_of::(), - 8usize, - concat!("Alignment of ", stringify!(csp_packet_s_anon_union)) - ); - } - - #[test] - fn bindgen_test_layout_csp_packet_s__bindgen_ty_2() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - size_of::(), - 256usize, - concat!("Size of: ", stringify!(csp_packet_s__bindgen_ty_2)) - ); - assert_eq!( - align_of::(), - 4usize, - concat!("Alignment of ", stringify!(csp_packet_s__bindgen_ty_2)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_2), - "::", - stringify!(data) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data16) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_2), - "::", - stringify!(data16) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).data32) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s__bindgen_ty_2), - "::", - stringify!(data32) - ) - ); - } - #[test] - fn bindgen_test_layout_csp_packet_s() { - const UNINIT: MaybeUninit = MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - size_of::(), - 320usize, - concat!("Size of: ", stringify!(csp_packet_s)) - ); - assert_eq!( - align_of::(), - 8usize, - concat!("Alignment of ", stringify!(csp_packet_s)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).length) as usize - ptr as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s), - "::", - stringify!(length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).id) as usize - ptr as usize }, - 34usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s), - "::", - stringify!(id) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).next) as usize - ptr as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s), - "::", - stringify!(next) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).header) as usize - ptr as usize }, - 56usize, - concat!( - "Offset of field: ", - stringify!(csp_packet_s), - "::", - stringify!(header) - ) - ); +impl CspSocket { + pub fn inner_as_mut_ptr(&mut self) -> *mut csp_socket_s { + &mut self.0 } } + +/// 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(); + } +} + +/// 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); + } +} + +/// 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); + } +} + +/// Rust wrapper for [ffi::csp_route_work]. +pub fn csp_route_work_raw() -> i32 { + unsafe { ffi::csp_route_work() } +} + +/// 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(()); + } + Err(CspError::try_from(result).expect("unexpected error type from csp_route_work")) +} + +#[derive(Debug, Clone)] +pub struct CspConn(csp_conn_s); + +impl CspConn { + fn new(address: u8) -> Self { + Self(csp_conn_s { address }) + } + + pub fn addr(&self) -> u8 { + self.0.address + } +} + +/// Rust wrapper for [ffi::csp_accept]. +pub fn csp_accept(socket: &mut CspSocket, timeout: Duration) -> Option { + let timeout_millis = timeout.as_millis(); + if timeout_millis > u32::MAX as u128 { + return None; + } + 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 + })) +} + +/// Rust wrapper for [ffi::csp_read]. +pub fn csp_read(conn: &mut CspConn, timeout: Duration) -> Option> { + let timeout_millis = timeout.as_millis(); + if timeout_millis > u32::MAX as u128 { + return None; + } + let opt_packet = unsafe { ffi::csp_read(&mut conn.0, timeout_millis as u32) }; + if opt_packet.is_null() { + return None; + } + // SAFETY: FFI pointer. As long as it is used beyond the lifetime of the connection, this + // should be fine. The passed [CspConn] value should ensure that. + Some(CspPacketRef(unsafe { &mut *opt_packet })) +}