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
use super::*;
use std::ffi::CString;
use std::mem;
use std::os::raw::c_int;
use std::ptr::NonNull;
#[derive(Debug)]
pub struct Avr {
inner: NonNull<ffi::avr_t>,
}
impl Avr {
pub fn new(mcu: &str, frequency: u32) -> Self {
let c_mcu = CString::new(mcu).unwrap();
// Safety: `c_mcu` points to a valid C-style string
let inner = unsafe { ffi::avr_make_mcu_by_name(c_mcu.as_ptr()) };
let inner = NonNull::new(inner)
.unwrap_or_else(|| panic!("Unknown AVR: {}", mcu));
let mut this = Self { inner };
// Safety: `inner` points to a valid `avr_t` which we've just received
// from simavr
let status = unsafe { ffi::avr_init(this.inner.as_ptr()) };
if status != 0 {
panic!("avr_init() failed (status={})", status);
}
// Safety: `inner` points to a valid `avr_t`; nothing else is writing
// there at the moment
unsafe {
this.inner.as_mut().frequency = frequency;
}
this
}
pub fn cycle(&self) -> u64 {
// Safety: `inner` points to a valid `avr_t`; nothing else is writing
// there at the moment, as guarded by `&mut self` on `fn run()`
unsafe { self.inner.as_ref().cycle }
}
pub fn frequency(&self) -> u32 {
// Safety: `inner` points to a valid `avr_t`; nothing else is writing
// there at the moment, as guarded by `&mut self` on `fn run()`
unsafe { self.inner.as_ref().frequency }
}
pub fn run(&mut self) -> AvrState {
// Safety: `inner` points to a valid `avr_t`; nothing else is writing
// there at the moment, as guarded by `&mut self` here
let state = unsafe { ffi::avr_run(self.inner.as_ptr()) };
AvrState::from_ffi(state)
}
pub fn load_firmware(&mut self, elf: NonNull<ffi::elf_firmware_t>) {
// Safety: `inner` points to a valid `avr_t`, `elf` points to a valid
// firmware; nothing else is writing into the AVR at the moment, as
// guarded by `&mut self` here
unsafe {
ffi::avr_load_firmware(self.inner.as_ptr(), elf.as_ptr());
}
}
/// # Safety
///
/// Callers must ensure that given `ioctl` and `T` match (that is: different
/// ioctls require parameters of different kinds, from u32 to structs).
pub unsafe fn ioctl<T>(&mut self, ioctl: IoCtl, param: &mut T) -> c_int {
unsafe {
ffi::avr_ioctl(
self.inner.as_ptr(),
ioctl.into_ffi(),
param as *mut _ as *mut _,
)
}
}
pub fn io_getirq(&self, ioctl: IoCtl, irq: u32) -> NonNull<ffi::avr_irq_t> {
self.try_io_getirq(ioctl, irq).unwrap_or_else(|| {
panic!("avr_io_getirq({ioctl:#?}, {irq}) failed")
})
}
pub fn try_io_getirq(
&self,
ioctl: IoCtl,
irq: u32,
) -> Option<NonNull<ffi::avr_irq_t>> {
// Safety: `avr_io_getirq()` only looks for the matching ioctl - it
// doesn't require any specific payload or anything
let ptr = unsafe {
ffi::avr_io_getirq(self.inner.as_ptr(), ioctl.into_ffi(), irq as _)
};
NonNull::new(ptr)
}
/// # Safety
///
/// Callers must ensure that given callback is meant for given `irq`.
pub unsafe fn irq_register_notify<T>(
irq: NonNull<ffi::avr_irq_t>,
notify: Option<
unsafe extern "C" fn(NonNull<ffi::avr_irq_t>, u32, NonNull<T>),
>,
param: *mut T,
) {
// Safety: We're transmuting two parameters:
//
// - `NonNull<ffi::avr_irq_t>` -> `*mut ffi::avr_irq_t`,
// - `NonNull<T>` -> `*mut c_void`
//
// ... where both conversions are legal.
#[allow(clippy::missing_transmute_annotations)]
let notify = unsafe { mem::transmute(notify) };
// Safety: We're transmuting `*mut T` -> `*mut c_void`, which is legal
#[allow(clippy::missing_transmute_annotations)]
let param = unsafe { mem::transmute(param) };
unsafe {
ffi::avr_irq_register_notify(irq.as_ptr(), notify, param);
}
}
}
impl Drop for Avr {
fn drop(&mut self) {
unsafe {
libc::free(self.inner.as_ptr() as *mut _);
}
}
}