Skip to content

Commit 4985edf

Browse files
committed
zephyr: device: uart: Implement a blocking transmit
A blocking write allows us to do the write without needing an intermediate copy of the data. The thread's buffer is directly copied into the uart's fifo. To make this safe, the write call must block until all of the data has been placed in the fifo. The uart.h irq interface is a little quirky. Namely, the uart fifo can only be accessed from the irq handler. To make this possible, the drivers will implement usually a soft interrupt to the irq handler, for the initial transmission. There is also an async interface in uart.h, but numerous devices don't support it. Signed-off-by: David Brown <[email protected]>
1 parent e81c744 commit 4985edf

File tree

1 file changed

+83
-2
lines changed

1 file changed

+83
-2
lines changed

zephyr/src/device/uart.rs

+83-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::error::{Error, Result, to_result_void, to_result};
1111
use crate::printkln;
1212
use crate::sys::sync::Semaphore;
1313
use crate::sync::{Arc, SpinMutex};
14-
use crate::time::{NoWait, Timeout};
14+
use crate::time::{Forever, NoWait, Timeout};
1515

1616
use core::ffi::{c_int, c_uchar, c_void};
1717
use core::ptr;
@@ -164,13 +164,35 @@ const BUFFER_SIZE: usize = 256;
164164
/// mutex because it can only be waited on when the Mutex is not locked.
165165
struct IrqOuterData {
166166
read_sem: Semaphore,
167+
write_sem: Semaphore,
167168
inner: SpinMutex<IrqInnerData>,
168169
}
169170

170171
/// Data for communication with the UART IRQ.
171172
struct IrqInnerData {
172173
/// The Ring buffer holding incoming and read data.
173174
buffer: ArrayDeque<u8, BUFFER_SIZE>,
175+
/// Data to be written, if that is the case.
176+
///
177+
/// If this is Some, then the irq should be enabled.
178+
write: Option<WriteSlice>,
179+
}
180+
181+
/// Represents a slice of data that the irq is going to write.
182+
struct WriteSlice {
183+
data: *const u8,
184+
len: usize,
185+
}
186+
187+
impl WriteSlice {
188+
/// Add an offset to the beginning of this slice, returning a new slice. This is equivalent to
189+
/// &item[count..] with a slice.
190+
pub unsafe fn add(&self, count: usize) -> WriteSlice {
191+
WriteSlice {
192+
data: unsafe { self.data.add(count) },
193+
len: self.len - count,
194+
}
195+
}
174196
}
175197

176198
/// This is the irq-driven interface.
@@ -189,8 +211,10 @@ impl UartIrq {
189211
pub unsafe fn new(uart: Uart) -> Result<UartIrq> {
190212
let data = Arc::new(IrqOuterData {
191213
read_sem: Semaphore::new(0, 1)?,
214+
write_sem: Semaphore::new(0, 1)?,
192215
inner: SpinMutex::new(IrqInnerData {
193216
buffer: ArrayDeque::new(),
217+
write: None,
194218
}),
195219
});
196220

@@ -205,7 +229,7 @@ impl UartIrq {
205229
to_result_void(ret)?;
206230
// Should this be settable?
207231
unsafe {
208-
raw::uart_irq_tx_enable(uart.device);
232+
// raw::uart_irq_tx_enable(uart.device);
209233
raw::uart_irq_rx_enable(uart.device);
210234
}
211235
Ok(UartIrq {
@@ -240,6 +264,36 @@ impl UartIrq {
240264

241265
self.data.try_read(buf)
242266
}
267+
268+
/// A blocking write to the UART.
269+
///
270+
/// By making this blocking, we don't need to make an extra copy of the data.
271+
///
272+
/// TODO: Async write.
273+
pub unsafe fn write(&mut self, buf: &[u8]) {
274+
if buf.len() == 0 {
275+
return;
276+
}
277+
278+
// Make the data to be written available to the irq handler, and get it going.
279+
{
280+
let mut inner = self.data.inner.lock().unwrap();
281+
assert!(inner.write.is_none());
282+
283+
inner.write = Some(WriteSlice {
284+
data: buf.as_ptr(),
285+
len: buf.len(),
286+
});
287+
288+
unsafe { raw::uart_irq_tx_enable(self.device) };
289+
}
290+
291+
// Wait for the transmission to complete. This shouldn't be racy, as the irq shouldn't be
292+
// giving the semaphore until there is 'write' data, and it has been consumed.
293+
let _ = self.data.write_sem.take(Forever);
294+
295+
// TODO: Should we check that the write actually finished?
296+
}
243297
}
244298

245299
impl IrqOuterData {
@@ -292,4 +346,31 @@ extern "C" fn irq_callback(
292346
if did_read {
293347
outer.read_sem.give();
294348
}
349+
350+
// If there is data to write, ensure the fifo is full, and when we run out of data, disable the
351+
// interrupt and signal the waiting thread.
352+
if let Some(write) = inner.write.take() {
353+
let count = unsafe {
354+
raw::uart_fifo_fill(dev, write.data, write.len as i32)
355+
};
356+
if count < 0 {
357+
panic!("Incorrect use of device fifo");
358+
}
359+
let count = count as usize;
360+
361+
if count == write.len {
362+
// The write finished, leave 'write' empty, and let the thread know we're done.
363+
outer.write_sem.give();
364+
365+
// Disable the tx fifo, as we don't need it any more.
366+
unsafe { raw::uart_irq_tx_disable(dev) };
367+
} else {
368+
// We're not finished, so remember how much is left.
369+
inner.write = Some(unsafe { write.add(count) });
370+
}
371+
}
372+
373+
unsafe {
374+
raw::uart_irq_update(dev);
375+
}
295376
}

0 commit comments

Comments
 (0)