Skip to main content

cantact/c/
mod.rs

1//! Implementation of C/C++ bindings.
2//!
3//! All functions are unsafe since they dereference a context pointer
4//! provided from C.
5//!
6//! TODO: put a simple example here.
7//!
8
9#![allow(clippy::missing_safety_doc)]
10
11use crate::{Frame, Interface};
12
13/// A CAN frame in a C representation
14#[repr(C)]
15pub struct CFrame {
16    channel: u8,
17    id: u32,
18    dlc: u8,
19    data: [u8; 64],
20    // these types are boolean flags, but C FFI hates bools
21    // use u8s instead: 1 = true, 0 = false
22    ext: u8,
23    fd: u8,
24    brs: u8,
25    esi: u8,
26    loopback: u8,
27    rtr: u8,
28    err: u8,
29}
30impl CFrame {
31    fn from_frame(f: Frame) -> CFrame {
32        CFrame {
33            channel: f.channel,
34            id: f.can_id,
35            dlc: f.can_dlc,
36            data: f.data_as_array(),
37            ext: if f.ext { 1 } else { 0 },
38            fd: if f.fd { 1 } else { 0 },
39            brs: if f.brs { 1 } else { 0 },
40            esi: if f.esi { 1 } else { 0 },
41            loopback: if f.loopback { 1 } else { 0 },
42            rtr: if f.rtr { 1 } else { 0 },
43            err: if f.err { 1 } else { 0 },
44        }
45    }
46}
47
48/// Interface state. A pointer to this struct is provided when initializing the
49/// library. All other functions require a pointer to this struct as the first
50/// argument.
51#[repr(C)]
52pub struct CInterface {
53    i: Option<Interface>,
54    c_rx_cb: Option<extern "C" fn(*const CFrame)>,
55}
56
57/// Create a new CANtact interface, returning a pointer to the interface.
58/// This pointer must be provided as the first argument to all other calls in
59/// this library.
60///
61/// If this function fails, it returns a null pointer (0).
62#[no_mangle]
63pub extern "C" fn cantact_init() -> *mut CInterface {
64    Box::into_raw(Box::new(CInterface {
65        i: None,
66        c_rx_cb: None,
67    }))
68}
69
70/// Clean up a CANtact interface.
71/// After calling, the pointer is no longer valid.
72#[no_mangle]
73pub unsafe extern "C" fn cantact_deinit(ptr: *mut CInterface) -> i32 {
74    Box::from_raw(ptr);
75    0
76}
77
78/// Set the receive callback function. This function will be called when a
79/// frame is received.
80#[no_mangle]
81pub unsafe extern "C" fn cantact_set_rx_callback(
82    ptr: *mut CInterface,
83    cb: Option<extern "C" fn(*const CFrame)>,
84) -> i32 {
85    let mut ci = &mut *ptr;
86    ci.c_rx_cb = cb;
87    0
88}
89
90/// Open the device. This must be called before any interaction with the
91/// device (changing settings, starting communication).
92#[no_mangle]
93pub unsafe extern "C" fn cantact_open(ptr: *mut CInterface) -> i32 {
94    let i = match Interface::new() {
95        Ok(i) => i,
96        Err(_) => return -1,
97    };
98    let ci = &mut *ptr;
99    ci.i = Some(i);
100    0
101}
102
103/// Close the device. After closing, no interaction with the device
104/// can be performed.
105#[no_mangle]
106pub unsafe extern "C" fn cantact_close(ptr: *mut CInterface) -> i32 {
107    let mut ci = &mut *ptr;
108    ci.i = None;
109    0
110}
111
112/// Start CAN communication. This will enable all configured CAN channels.
113///
114/// This function starts a thread which will call the registered callback
115/// when a frame is received.
116#[no_mangle]
117pub unsafe extern "C" fn cantact_start(ptr: *mut CInterface) -> i32 {
118    let ci = &mut *ptr;
119
120    let cb = ci.c_rx_cb;
121    match &mut ci.i {
122        Some(i) => i
123            .start(move |f: Frame| {
124                match cb {
125                    None => {}
126                    Some(cb) => {
127                        cb(&CFrame::from_frame(f));
128                    }
129                };
130            })
131            .expect("failed to start device"),
132        None => return -1,
133    };
134    0
135}
136
137/// Stop CAN communication. This will stop all configured CAN channels.
138#[no_mangle]
139pub unsafe extern "C" fn cantact_stop(ptr: *mut CInterface) -> i32 {
140    let ci = &mut *ptr;
141    match &mut ci.i {
142        Some(i) => i.stop().expect("failed to stop device"),
143        None => return -1,
144    }
145    0
146}
147
148/// Transmit a frame. Can only be called if the device is running.
149#[no_mangle]
150pub unsafe extern "C" fn cantact_transmit(ptr: *mut CInterface, cf: CFrame) -> i32 {
151    let ci = &mut *ptr;
152    let f = Frame {
153        channel: 0, //cf.channel,
154        can_id: cf.id,
155        can_dlc: cf.dlc,
156        data: cf.data.to_vec(),
157        ext: cf.ext > 0,
158        fd: cf.fd > 0,
159        brs: cf.brs > 0,
160        esi: cf.esi > 0,
161        loopback: false,
162        rtr: cf.rtr > 0,
163        err: cf.err > 0,
164        timestamp: None,
165    };
166    match &mut ci.i {
167        Some(i) => i.send(f).expect("failed to transmit frame"),
168        None => return -1,
169    };
170    0
171}
172
173/// Sets the bitrate for a chanel to the given value in bits per second.
174#[no_mangle]
175pub unsafe extern "C" fn cantact_set_bitrate(
176    ptr: *mut CInterface,
177    channel: u8,
178    bitrate: u32,
179) -> i32 {
180    let ci = &mut *ptr;
181    match &mut ci.i {
182        Some(i) => i
183            .set_bitrate(channel as usize, bitrate)
184            .expect("failed to set bitrate"),
185        None => return -1,
186    }
187    0
188}
189
190/// Enable or disable a channel.
191#[no_mangle]
192pub unsafe extern "C" fn cantact_set_enabled(
193    ptr: *mut CInterface,
194    channel: u8,
195    enabled: u8,
196) -> i32 {
197    let ci = &mut *ptr;
198    match &mut ci.i {
199        Some(i) => i
200            .set_enabled(channel as usize, enabled > 0)
201            .expect("failed to enable channel"),
202        None => return -1,
203    }
204    0
205}
206
207/// Enable or disable bus monitoring mode for a channel. When enabled, channel
208/// will not transmit frames or acknoweldgements.
209#[no_mangle]
210pub unsafe extern "C" fn cantact_set_monitor(
211    ptr: *mut CInterface,
212    channel: u8,
213    enabled: u8,
214) -> i32 {
215    let ci = &mut *ptr;
216    match &mut ci.i {
217        Some(i) => i
218            .set_monitor(channel as usize, enabled > 0)
219            .expect("failed to set monitoring mode"),
220        None => return -1,
221    }
222    0
223}
224
225/// Enable or disable hardware loopback for a channel. This will cause sent
226/// frames to be received. This mode is mostly intended for device testing.
227#[no_mangle]
228pub unsafe extern "C" fn cantact_set_hw_loopback(
229    ptr: *mut CInterface,
230    channel: u8,
231    enabled: u8,
232) -> i32 {
233    let ci = &mut *ptr;
234    match &mut ci.i {
235        Some(i) => i
236            .set_loopback(channel as usize, enabled > 0)
237            .expect("failed to enable channel"),
238        None => return -1,
239    }
240    0
241}
242
243/// Get the number of CAN channels the device has.
244///
245/// Returns the number of channels or a negative error code on failure.
246#[no_mangle]
247pub unsafe extern "C" fn cantact_get_channel_count(ptr: *mut CInterface) -> i32 {
248    let ci = &mut *ptr;
249    match &mut ci.i {
250        Some(i) => i.channels() as i32,
251        None => -1,
252    }
253}