From f2610dcbb6c0e06547797f62401e751afe3f0fce Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Fri, 31 May 2024 19:48:12 +0200 Subject: [PATCH] start writing a README --- README.md | 30 ++++++++++++++++++++++++++ examples/build.rs | 1 + examples/src/main.rs | 40 +++++++++++++++++------------------ libcsp-cargo-build/src/lib.rs | 3 +++ libcsp-rust/src/ffi.rs | 3 +++ libcsp-rust/src/lib.rs | 28 ++++++++++++++---------- 6 files changed, 73 insertions(+), 32 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e92e62 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +libcsp-rust +========= + +This project aims to provide libraries and tools to use +[`libcsp`](https://github.com/libcsp/libcsp) in your Rust project. + +It provides 2 crates for this: + +- [`libcsp-cargo-build`] provides an API to build the `libcsp` using `cargo` with the + [`cc`](https://docs.rs/cc/latest/cc/) crate. +- [`libcsp-rust`] provides the Rust bindings to `libcsp` and a safe and ergonomic Rust interface. + +In addition, it provides a workspace to allow updating the `libcsp` and the corresponding bindings +more easily inside the `lib` directory. Some of the examples `libcsp` provides were ported to Rust +and are showcases in the `examples` directory. + +## Getting started + +We assume that cargo should also take care of building the library. + +1. Add the `libcsp-cargo-build` as a build dependency inside your `Cargo.toml`. +2. Add the `libcsp-rust` as a regular dependency inside your `Cargo.toml`. +3. Create a custom `build.rs` script which takes care of building `libcsp` using the API + provided by `libcsp-cargo-build`. You have to provide the source code for `libcsp` inside some + directory and pass that director to a builder API. +4. You can now write regular Rust code and use the API provided by `libcsp-rust` to use `libcsp` + in a safe and Rusty way. + +It is recommended to have a look at the [example build script]() which should give you a general +idea of how a build script might look like to integrate `libcsp`. diff --git a/examples/build.rs b/examples/build.rs index 2c4454a..fc2a85d 100644 --- a/examples/build.rs +++ b/examples/build.rs @@ -8,6 +8,7 @@ fn main() { let manifest_path = PathBuf::from(&manifest_dir); let libcsp_path = "../lib/libcsp"; let mut csp_builder = Builder::new(PathBuf::from(libcsp_path), PathBuf::from(&out_dir)); + csp_builder.compiler_warnings = false; // We always re-generate the header file. generate_autoconf_header_file(manifest_path.clone(), &csp_builder.cfg) .expect("generating header file failed"); diff --git a/examples/src/main.rs b/examples/src/main.rs index f6b864c..3a7eda9 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -1,4 +1,5 @@ use std::{ + ffi::CStr, sync::{ atomic::{AtomicBool, AtomicU32}, Arc, @@ -8,10 +9,10 @@ use std::{ }; use libcsp_rust::{ - csp_accept_guarded, csp_bind, csp_buffer_get, csp_conn_dport, csp_conn_print_table, - csp_connect_guarded, csp_iflist_print, csp_init, csp_listen, csp_ping, csp_read, csp_reboot, - csp_route_work, csp_send, csp_service_handler, ConnectOpts, CspSocket, MsgPriority, - SocketFlags, CSP_ANY, CSP_LOOPBACK, + csp_accept_guarded, csp_bind, csp_buffer_free, csp_buffer_get, csp_conn_dport, + csp_conn_print_table, csp_connect_guarded, csp_iflist_print, csp_init, csp_listen, csp_ping, + csp_read, csp_reboot, csp_route_work, csp_send, csp_service_handler, ConnectOpts, CspSocket, + MsgPriority, SocketFlags, CSP_ANY, CSP_LOOPBACK, }; const MY_SERVER_PORT: i32 = 10; @@ -27,10 +28,14 @@ fn main() -> Result<(), u32> { let stop_signal = Arc::new(AtomicBool::new(false)); let stop_signal_server = stop_signal.clone(); let stop_signal_client = stop_signal.clone(); + let stop_signal_router = stop_signal.clone(); let server_received = Arc::new(AtomicU32::new(0)); let server_recv_copy = server_received.clone(); - let csp_router_jh = thread::spawn(|| loop { + let csp_router_jh = thread::spawn(move || loop { + if stop_signal_router.load(std::sync::atomic::Ordering::Relaxed) { + break; + } if let Err(e) = csp_route_work() { match e { libcsp_rust::CspError::TimedOut => continue, @@ -42,11 +47,11 @@ fn main() -> Result<(), u32> { } }); - let csp_server_jh = thread::spawn(|| { + let csp_server_jh = thread::spawn(move || { server(server_received, stop_signal_server); }); - let csp_client_jh = thread::spawn(|| { + let csp_client_jh = thread::spawn(move || { client(stop_signal_client); }); @@ -102,7 +107,6 @@ fn server(server_received: Arc, stop_signal: Arc) { if conn.is_none() { continue; } - println!("server accepted conn"); let mut conn = conn.unwrap(); // Read packets on connection, timout is 100 mS @@ -111,26 +115,22 @@ fn server(server_received: Arc, stop_signal: Arc) { break; } - println!("server trying read"); // SAFETY: Connection is active while we read here. - let packet = unsafe { csp_read(&mut conn.0, Duration::from_millis(100)) }; + let packet = csp_read(&mut conn.0, Duration::from_millis(100)); if packet.is_none() { break; } - println!("server read a packet"); let mut packet = packet.unwrap(); match csp_conn_dport(&conn.0) { MY_SERVER_PORT => { - println!("server received packet on custom port"); server_received.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let cstr = CStr::from_bytes_with_nul(packet.packet_data()) + .expect("invalid packet data format, is not C string"); // Process packet here. - println!( - "packet received on MY_SERVER_PORT: {:x?}\n", - packet.packet_data() - ); + println!("packet received on MY_SERVER_PORT: {:?}", cstr); + csp_buffer_free(packet); } _ => { - println!("calling CSP service handler"); csp_service_handler(&mut packet); } }; @@ -152,13 +152,12 @@ fn client(stop_signal: Arc) { } else { thread::sleep(Duration::from_millis(100)); } - println!("client trying to ping"); - // Send ping to server, timeout 1000 mS, ping size 100 bytes + // Send ping to server, timeout 1000 mS, ping size 20 bytes if let Err(e) = csp_ping( CSP_LOOPBACK, Duration::from_millis(1000), - 100, + 20, SocketFlags::NONE, ) { println!("ping error: {:?}", e); @@ -167,7 +166,6 @@ fn client(stop_signal: Arc) { // Send reboot request to server, the server has no actual implementation of // csp_sys_reboot() and fails to reboot. csp_reboot(CSP_LOOPBACK); - println!("reboot system request sent to address: {}", CSP_LOOPBACK); // Send data packet (string) to server diff --git a/libcsp-cargo-build/src/lib.rs b/libcsp-cargo-build/src/lib.rs index 819570f..297659b 100644 --- a/libcsp-cargo-build/src/lib.rs +++ b/libcsp-cargo-build/src/lib.rs @@ -115,6 +115,7 @@ pub struct Builder { libcsp_src_path_base: PathBuf, out_dir: PathBuf, pub cfg: Config, + pub compiler_warnings: bool, build: cc::Build, } @@ -128,6 +129,7 @@ impl Builder { libcsp_src_path_base, out_dir, cfg: Default::default(), + compiler_warnings: true, build: Default::default(), } } @@ -182,6 +184,7 @@ impl Builder { inc_path.push("include"); self.build.include(inc_path); self.build.include(&self.libcsp_src_path_base); + self.build.cargo_warnings(self.compiler_warnings); self.build.compile("csp"); Ok(()) diff --git a/libcsp-rust/src/ffi.rs b/libcsp-rust/src/ffi.rs index aabb57b..a3a856f 100644 --- a/libcsp-rust/src/ffi.rs +++ b/libcsp-rust/src/ffi.rs @@ -290,6 +290,9 @@ extern "C" { #[doc = " Send packet on a connection.\n The packet buffer is automatically freed, and cannot be used after the call to csp_send()\n\n @param[in] conn connection\n @param[in] packet packet to send"] pub fn csp_send(conn: *mut csp_conn_t, packet: *mut csp_packet_t); + #[doc = " Free buffer (from task context).\n\n @param[in] buffer buffer to free. NULL is handled gracefully."] + pub fn csp_buffer_free(buffer: *mut ::core::ffi::c_void); + #[doc = " Print connection table to stdout."] pub fn csp_conn_print_table(); diff --git a/libcsp-rust/src/lib.rs b/libcsp-rust/src/lib.rs index 6afdffd..7d368ff 100644 --- a/libcsp-rust/src/lib.rs +++ b/libcsp-rust/src/lib.rs @@ -16,7 +16,7 @@ use bitflags::bitflags; use ffi::{csp_conn_s, csp_packet_s, csp_socket_s}; #[derive(Debug, PartialEq, Eq, Copy, Clone)] -pub enum ReservedPorts { +pub enum ReservedPort { Cmp = 0, Ping = 1, Ps = 2, @@ -310,11 +310,7 @@ pub fn csp_accept(socket: &mut CspSocket, timeout: Duration) -> Option Option { +pub fn csp_read(conn: &mut CspConnRef, timeout: Duration) -> Option { let timeout_millis = timeout.as_millis(); if timeout_millis > u32::MAX as u128 { return None; @@ -357,19 +353,21 @@ pub fn csp_ping_raw(node: u16, timeout: Duration, size: usize, opts: SocketFlags } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct PingError; + /// Rust wrapper for [ffi::csp_ping]. pub fn csp_ping( node: u16, timeout: Duration, size: usize, opts: SocketFlags, -) -> Result<(), CspError> { +) -> Result { let result = csp_ping_raw(node, timeout, size, opts); - if result == CspError::None as i32 { - return Ok(()); + if result < 0 { + return Err(PingError); } - Err(CspError::try_from(result) - .unwrap_or_else(|_| panic!("unexpected error value {} from csp_ping", result))) + Ok(Duration::from_millis(result as u64)) } /// Rust wrapper for [ffi::csp_reboot]. @@ -438,10 +436,18 @@ pub fn csp_send(conn: &mut CspConnRef, packet: impl Into) { /// Rust wrapper for [ffi::csp_conn_print_table]. pub fn csp_conn_print_table() { + // SAFETY: FFI call. unsafe { ffi::csp_conn_print_table() } } /// Rust wrapper for [ffi::csp_iflist_print]. pub fn csp_iflist_print() { + // SAFETY: FFI call. unsafe { ffi::csp_iflist_print() } } + +pub fn csp_buffer_free(packet: impl Into) { + // SAFETY: FFI call and the Rust type system actually ensure the correct type + // is free'd here. + unsafe { ffi::csp_buffer_free(packet.into().0 as *mut libc::c_void) } +}