start writing a README
This commit is contained in:
parent
30666f8692
commit
f2610dcbb6
30
README.md
Normal file
30
README.md
Normal file
@ -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`.
|
@ -8,6 +8,7 @@ fn main() {
|
|||||||
let manifest_path = PathBuf::from(&manifest_dir);
|
let manifest_path = PathBuf::from(&manifest_dir);
|
||||||
let libcsp_path = "../lib/libcsp";
|
let libcsp_path = "../lib/libcsp";
|
||||||
let mut csp_builder = Builder::new(PathBuf::from(libcsp_path), PathBuf::from(&out_dir));
|
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.
|
// We always re-generate the header file.
|
||||||
generate_autoconf_header_file(manifest_path.clone(), &csp_builder.cfg)
|
generate_autoconf_header_file(manifest_path.clone(), &csp_builder.cfg)
|
||||||
.expect("generating header file failed");
|
.expect("generating header file failed");
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
ffi::CStr,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, AtomicU32},
|
atomic::{AtomicBool, AtomicU32},
|
||||||
Arc,
|
Arc,
|
||||||
@ -8,10 +9,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use libcsp_rust::{
|
use libcsp_rust::{
|
||||||
csp_accept_guarded, csp_bind, csp_buffer_get, csp_conn_dport, csp_conn_print_table,
|
csp_accept_guarded, csp_bind, csp_buffer_free, csp_buffer_get, csp_conn_dport,
|
||||||
csp_connect_guarded, csp_iflist_print, csp_init, csp_listen, csp_ping, csp_read, csp_reboot,
|
csp_conn_print_table, csp_connect_guarded, csp_iflist_print, csp_init, csp_listen, csp_ping,
|
||||||
csp_route_work, csp_send, csp_service_handler, ConnectOpts, CspSocket, MsgPriority,
|
csp_read, csp_reboot, csp_route_work, csp_send, csp_service_handler, ConnectOpts, CspSocket,
|
||||||
SocketFlags, CSP_ANY, CSP_LOOPBACK,
|
MsgPriority, SocketFlags, CSP_ANY, CSP_LOOPBACK,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MY_SERVER_PORT: i32 = 10;
|
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 = Arc::new(AtomicBool::new(false));
|
||||||
let stop_signal_server = stop_signal.clone();
|
let stop_signal_server = stop_signal.clone();
|
||||||
let stop_signal_client = 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_received = Arc::new(AtomicU32::new(0));
|
||||||
let server_recv_copy = server_received.clone();
|
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() {
|
if let Err(e) = csp_route_work() {
|
||||||
match e {
|
match e {
|
||||||
libcsp_rust::CspError::TimedOut => continue,
|
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);
|
server(server_received, stop_signal_server);
|
||||||
});
|
});
|
||||||
|
|
||||||
let csp_client_jh = thread::spawn(|| {
|
let csp_client_jh = thread::spawn(move || {
|
||||||
client(stop_signal_client);
|
client(stop_signal_client);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -102,7 +107,6 @@ fn server(server_received: Arc<AtomicU32>, stop_signal: Arc<AtomicBool>) {
|
|||||||
if conn.is_none() {
|
if conn.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
println!("server accepted conn");
|
|
||||||
let mut conn = conn.unwrap();
|
let mut conn = conn.unwrap();
|
||||||
|
|
||||||
// Read packets on connection, timout is 100 mS
|
// Read packets on connection, timout is 100 mS
|
||||||
@ -111,26 +115,22 @@ fn server(server_received: Arc<AtomicU32>, stop_signal: Arc<AtomicBool>) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("server trying read");
|
|
||||||
// SAFETY: Connection is active while we read here.
|
// 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() {
|
if packet.is_none() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
println!("server read a packet");
|
|
||||||
let mut packet = packet.unwrap();
|
let mut packet = packet.unwrap();
|
||||||
match csp_conn_dport(&conn.0) {
|
match csp_conn_dport(&conn.0) {
|
||||||
MY_SERVER_PORT => {
|
MY_SERVER_PORT => {
|
||||||
println!("server received packet on custom port");
|
|
||||||
server_received.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
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.
|
// Process packet here.
|
||||||
println!(
|
println!("packet received on MY_SERVER_PORT: {:?}", cstr);
|
||||||
"packet received on MY_SERVER_PORT: {:x?}\n",
|
csp_buffer_free(packet);
|
||||||
packet.packet_data()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
println!("calling CSP service handler");
|
|
||||||
csp_service_handler(&mut packet);
|
csp_service_handler(&mut packet);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -152,13 +152,12 @@ fn client(stop_signal: Arc<AtomicBool>) {
|
|||||||
} else {
|
} else {
|
||||||
thread::sleep(Duration::from_millis(100));
|
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(
|
if let Err(e) = csp_ping(
|
||||||
CSP_LOOPBACK,
|
CSP_LOOPBACK,
|
||||||
Duration::from_millis(1000),
|
Duration::from_millis(1000),
|
||||||
100,
|
20,
|
||||||
SocketFlags::NONE,
|
SocketFlags::NONE,
|
||||||
) {
|
) {
|
||||||
println!("ping error: {:?}", e);
|
println!("ping error: {:?}", e);
|
||||||
@ -167,7 +166,6 @@ fn client(stop_signal: Arc<AtomicBool>) {
|
|||||||
// Send reboot request to server, the server has no actual implementation of
|
// Send reboot request to server, the server has no actual implementation of
|
||||||
// csp_sys_reboot() and fails to reboot.
|
// csp_sys_reboot() and fails to reboot.
|
||||||
csp_reboot(CSP_LOOPBACK);
|
csp_reboot(CSP_LOOPBACK);
|
||||||
println!("reboot system request sent to address: {}", CSP_LOOPBACK);
|
|
||||||
|
|
||||||
// Send data packet (string) to server
|
// Send data packet (string) to server
|
||||||
|
|
||||||
|
@ -115,6 +115,7 @@ pub struct Builder {
|
|||||||
libcsp_src_path_base: PathBuf,
|
libcsp_src_path_base: PathBuf,
|
||||||
out_dir: PathBuf,
|
out_dir: PathBuf,
|
||||||
pub cfg: Config,
|
pub cfg: Config,
|
||||||
|
pub compiler_warnings: bool,
|
||||||
build: cc::Build,
|
build: cc::Build,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +129,7 @@ impl Builder {
|
|||||||
libcsp_src_path_base,
|
libcsp_src_path_base,
|
||||||
out_dir,
|
out_dir,
|
||||||
cfg: Default::default(),
|
cfg: Default::default(),
|
||||||
|
compiler_warnings: true,
|
||||||
build: Default::default(),
|
build: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,6 +184,7 @@ impl Builder {
|
|||||||
inc_path.push("include");
|
inc_path.push("include");
|
||||||
self.build.include(inc_path);
|
self.build.include(inc_path);
|
||||||
self.build.include(&self.libcsp_src_path_base);
|
self.build.include(&self.libcsp_src_path_base);
|
||||||
|
self.build.cargo_warnings(self.compiler_warnings);
|
||||||
|
|
||||||
self.build.compile("csp");
|
self.build.compile("csp");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -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"]
|
#[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);
|
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."]
|
#[doc = " Print connection table to stdout."]
|
||||||
pub fn csp_conn_print_table();
|
pub fn csp_conn_print_table();
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ use bitflags::bitflags;
|
|||||||
use ffi::{csp_conn_s, csp_packet_s, csp_socket_s};
|
use ffi::{csp_conn_s, csp_packet_s, csp_socket_s};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum ReservedPorts {
|
pub enum ReservedPort {
|
||||||
Cmp = 0,
|
Cmp = 0,
|
||||||
Ping = 1,
|
Ping = 1,
|
||||||
Ps = 2,
|
Ps = 2,
|
||||||
@ -310,11 +310,7 @@ pub fn csp_accept(socket: &mut CspSocket, timeout: Duration) -> Option<CspConnRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Rust wrapper for [ffi::csp_read].
|
/// Rust wrapper for [ffi::csp_read].
|
||||||
///
|
pub fn csp_read(conn: &mut CspConnRef, timeout: Duration) -> Option<CspPacketRef> {
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// - You MUST ensure that a connection is active when calling this function.
|
|
||||||
pub unsafe fn csp_read(conn: &mut CspConnRef, timeout: Duration) -> Option<CspPacketRef> {
|
|
||||||
let timeout_millis = timeout.as_millis();
|
let timeout_millis = timeout.as_millis();
|
||||||
if timeout_millis > u32::MAX as u128 {
|
if timeout_millis > u32::MAX as u128 {
|
||||||
return None;
|
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].
|
/// Rust wrapper for [ffi::csp_ping].
|
||||||
pub fn csp_ping(
|
pub fn csp_ping(
|
||||||
node: u16,
|
node: u16,
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
size: usize,
|
size: usize,
|
||||||
opts: SocketFlags,
|
opts: SocketFlags,
|
||||||
) -> Result<(), CspError> {
|
) -> Result<Duration, PingError> {
|
||||||
let result = csp_ping_raw(node, timeout, size, opts);
|
let result = csp_ping_raw(node, timeout, size, opts);
|
||||||
if result == CspError::None as i32 {
|
if result < 0 {
|
||||||
return Ok(());
|
return Err(PingError);
|
||||||
}
|
}
|
||||||
Err(CspError::try_from(result)
|
Ok(Duration::from_millis(result as u64))
|
||||||
.unwrap_or_else(|_| panic!("unexpected error value {} from csp_ping", result)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rust wrapper for [ffi::csp_reboot].
|
/// Rust wrapper for [ffi::csp_reboot].
|
||||||
@ -438,10 +436,18 @@ pub fn csp_send(conn: &mut CspConnRef, packet: impl Into<CspPacketRef>) {
|
|||||||
|
|
||||||
/// Rust wrapper for [ffi::csp_conn_print_table].
|
/// Rust wrapper for [ffi::csp_conn_print_table].
|
||||||
pub fn csp_conn_print_table() {
|
pub fn csp_conn_print_table() {
|
||||||
|
// SAFETY: FFI call.
|
||||||
unsafe { ffi::csp_conn_print_table() }
|
unsafe { ffi::csp_conn_print_table() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rust wrapper for [ffi::csp_iflist_print].
|
/// Rust wrapper for [ffi::csp_iflist_print].
|
||||||
pub fn csp_iflist_print() {
|
pub fn csp_iflist_print() {
|
||||||
|
// SAFETY: FFI call.
|
||||||
unsafe { ffi::csp_iflist_print() }
|
unsafe { ffi::csp_iflist_print() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn csp_buffer_free(packet: impl Into<CspPacketRef>) {
|
||||||
|
// 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) }
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user