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
|
use core::{
ptr,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{MODE_BLOCK_IF_FULL, MODE_MASK, SIZE};
#[repr(C)]
pub(crate) struct Channel {
pub name: *const u8,
pub buffer: *mut u8,
pub size: usize,
pub write: AtomicUsize,
pub read: AtomicUsize,
/// Channel properties.
///
/// Currently, only the lowest 2 bits are used to set the channel mode (see constants below).
pub flags: AtomicUsize,
}
impl Channel {
pub fn write_all(&self, mut bytes: &[u8]) {
// the host-connection-status is only modified after RAM initialization while the device is
// halted, so we only need to check it once before the write-loop
let write = match self.host_is_connected() {
true => Channel::blocking_write,
false => Channel::nonblocking_write,
};
while !bytes.is_empty() {
let consumed = write(self, bytes);
if consumed != 0 {
bytes = &bytes[consumed..];
}
}
}
fn blocking_write(&self, bytes: &[u8]) -> usize {
if bytes.is_empty() {
return 0;
}
let read = self.read.load(Ordering::Relaxed);
let write = self.write.load(Ordering::Acquire);
let available = if read > write {
read - write - 1
} else if read == 0 {
SIZE - write - 1
} else {
SIZE - write
};
if available == 0 {
return 0;
}
let cursor = write;
let len = bytes.len().min(available);
unsafe {
if cursor + len > SIZE {
// split memcpy
let pivot = SIZE - cursor;
ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), pivot);
ptr::copy_nonoverlapping(bytes.as_ptr().add(pivot), self.buffer, len - pivot);
} else {
// single memcpy
ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), len);
}
}
self.write
.store(write.wrapping_add(len) % SIZE, Ordering::Release);
len
}
fn nonblocking_write(&self, bytes: &[u8]) -> usize {
let write = self.write.load(Ordering::Acquire);
let cursor = write;
// NOTE truncate at SIZE to avoid more than one "wrap-around" in a single `write` call
let len = bytes.len().min(SIZE);
unsafe {
if cursor + len > SIZE {
// split memcpy
let pivot = SIZE - cursor;
ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), pivot);
ptr::copy_nonoverlapping(bytes.as_ptr().add(pivot), self.buffer, len - pivot);
} else {
// single memcpy
ptr::copy_nonoverlapping(bytes.as_ptr(), self.buffer.add(cursor), len);
}
}
self.write
.store(write.wrapping_add(len) % SIZE, Ordering::Release);
len
}
pub fn flush(&self) {
// return early, if host is disconnected
if !self.host_is_connected() {
return;
}
// busy wait, until the read- catches up with the write-pointer
let read = || self.read.load(Ordering::Relaxed);
let write = || self.write.load(Ordering::Relaxed);
while read() != write() {}
}
fn host_is_connected(&self) -> bool {
// we assume that a host is connected if we are in blocking-mode. this is what probe-run does.
self.flags.load(Ordering::Relaxed) & MODE_MASK == MODE_BLOCK_IF_FULL
}
}
|