1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//! Implementation of C/C++ bindings.
//!
//! All functions are unsafe since they dereference a context pointer
//! provided from C.
//!
//! TODO: put a simple example here.
//!

#![allow(clippy::missing_safety_doc)]

use crate::{Frame, Interface};

/// A CAN frame in a C representation
#[repr(C)]
pub struct CFrame {
    channel: u8,
    id: u32,
    dlc: u8,
    data: [u8; 8],
    // these types are boolean flags, but C FFI hates bools
    // use u8s instead: 1 = true, 0 = false
    ext: u8,
    fd: u8,
    loopback: u8,
    rtr: u8,
}
impl CFrame {
    fn from_frame(f: Frame) -> CFrame {
        CFrame {
            channel: f.channel,
            id: f.can_id,
            dlc: f.can_dlc,
            data: f.data,
            ext: if f.ext { 1 } else { 0 },
            fd: if f.fd { 1 } else { 0 },
            loopback: if f.loopback { 1 } else { 0 },
            rtr: if f.rtr { 1 } else { 0 },
        }
    }
}

/// Interface state. A pointer to this struct is provided when initializing the
/// library. All other functions require a pointer to this struct as the first
/// argument.
#[repr(C)]
pub struct CInterface {
    i: Option<Interface>,
    c_rx_cb: Option<extern "C" fn(*const CFrame)>,
}

/// Create a new CANtact interface, returning a pointer to the interface.
/// This pointer must be provided as the first argument to all other calls in
/// this library.
///
/// If this function fails, it returns a null pointer (0).
#[no_mangle]
pub extern "C" fn cantact_init() -> *mut CInterface {
    Box::into_raw(Box::new(CInterface {
        i: None,
        c_rx_cb: None,
    }))
}

/// Clean up a CANtact interface.
/// After calling, the pointer is no longer valid.
#[no_mangle]
pub unsafe extern "C" fn cantact_deinit(ptr: *mut CInterface) -> i32 {
    Box::from_raw(ptr);
    0
}

/// Set the receive callback function. This function will be called when a
/// frame is received.
#[no_mangle]
pub unsafe extern "C" fn cantact_set_rx_callback(
    ptr: *mut CInterface,
    cb: Option<extern "C" fn(*const CFrame)>,
) -> i32 {
    let mut ci = &mut *ptr;
    ci.c_rx_cb = cb;
    0
}

/// Open the device. This must be called before any interaction with the
/// device (changing settings, starting communication).
#[no_mangle]
pub unsafe extern "C" fn cantact_open(ptr: *mut CInterface) -> i32 {
    let i = match Interface::new() {
        Ok(i) => i,
        Err(_) => return -1,
    };
    let ci = &mut *ptr;
    ci.i = Some(i);
    0
}

/// Close the device. After closing, no interaction with the device
/// can be performed.
#[no_mangle]
pub unsafe extern "C" fn cantact_close(ptr: *mut CInterface) -> i32 {
    let mut ci = &mut *ptr;
    ci.i = None;
    0
}

/// Start CAN communication. This will enable all configured CAN channels.
///
/// This function starts a thread which will call the registered callback
/// when a frame is received.
#[no_mangle]
pub unsafe extern "C" fn cantact_start(ptr: *mut CInterface) -> i32 {
    let ci = &mut *ptr;

    let cb = ci.c_rx_cb;
    match &mut ci.i {
        Some(i) => i
            .start(move |f: Frame| {
                match cb {
                    None => {}
                    Some(cb) => {
                        cb(&CFrame::from_frame(f));
                    }
                };
            })
            .expect("failed to start device"),
        None => return -1,
    };
    0
}

/// Stop CAN communication. This will stop all configured CAN channels.
#[no_mangle]
pub unsafe extern "C" fn cantact_stop(ptr: *mut CInterface) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i.stop().expect("failed to stop device"),
        None => return -1,
    }
    0
}

/// Transmit a frame. Can only be called if the device is running.
#[no_mangle]
pub unsafe extern "C" fn cantact_transmit(ptr: *mut CInterface, cf: CFrame) -> i32 {
    let ci = &mut *ptr;
    let f = Frame {
        channel: 0, //cf.channel,
        can_id: cf.id,
        can_dlc: cf.dlc,
        data: cf.data,
        ext: cf.ext > 0,
        fd: cf.fd > 0,
        loopback: false,
        rtr: cf.rtr > 0,
    };
    match &mut ci.i {
        Some(i) => i.send(f).expect("failed to transmit frame"),
        None => return -1,
    };
    0
}

/// Sets the bitrate for a chanel to the given value in bits per second.
#[no_mangle]
pub unsafe extern "C" fn cantact_set_bitrate(
    ptr: *mut CInterface,
    channel: u8,
    bitrate: u32,
) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i
            .set_bitrate(channel as usize, bitrate)
            .expect("failed to set bitrate"),
        None => return -1,
    }
    0
}

/// Enable or disable a channel.
#[no_mangle]
pub unsafe extern "C" fn cantact_set_enabled(
    ptr: *mut CInterface,
    channel: u8,
    enabled: u8,
) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i
            .set_enabled(channel as usize, enabled > 0)
            .expect("failed to enable channel"),
        None => return -1,
    }
    0
}

/// Enable or disable bus monitoring mode for a channel. When enabled, channel
/// will not transmit frames or acknoweldgements.
#[no_mangle]
pub unsafe extern "C" fn cantact_set_monitor(
    ptr: *mut CInterface,
    channel: u8,
    enabled: u8,
) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i
            .set_monitor(channel as usize, enabled > 0)
            .expect("failed to set monitoring mode"),
        None => return -1,
    }
    0
}

/// Enable or disable hardware loopback for a channel. This will cause sent
/// frames to be received. This mode is mostly intended for device testing.
#[no_mangle]
pub unsafe extern "C" fn cantact_set_hw_loopback(
    ptr: *mut CInterface,
    channel: u8,
    enabled: u8,
) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i
            .set_loopback(channel as usize, enabled > 0)
            .expect("failed to enable channel"),
        None => return -1,
    }
    0
}

/// Get the number of CAN channels the device has.
///
/// Returns the number of channels or a negative error code on failure.
#[no_mangle]
pub unsafe extern "C" fn cantact_get_channel_count(ptr: *mut CInterface) -> i32 {
    let ci = &mut *ptr;
    match &mut ci.i {
        Some(i) => i.channels() as i32,
        None => -1,
    }
}