Skip to content

Commit f98166d

Browse files
committed
zephyr: device: uart: Move UartIrq to own crate
Move this specific type into its own crate. Signed-off-by: David Brown <[email protected]>
1 parent 2ae7821 commit f98166d

File tree

2 files changed

+260
-245
lines changed

2 files changed

+260
-245
lines changed

zephyr/src/device/uart.rs

Lines changed: 3 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,18 @@
44
#![allow(dead_code)]
55
#![allow(unused_variables)]
66

7-
use arraydeque::ArrayDeque;
8-
97
use crate::raw;
108
use crate::error::{Error, Result, to_result_void, to_result};
119
use crate::printkln;
12-
use crate::sys::sync::Semaphore;
13-
use crate::sync::{Arc, SpinMutex};
14-
use crate::time::{NoWait, Timeout};
1510

1611
use core::ffi::{c_int, c_uchar, c_void};
1712
use core::ptr;
1813

1914
use super::Unique;
2015

16+
mod irq;
17+
pub use irq::UartIrq;
18+
2119
/// A wrapper around a UART device on Zephyr.
2220
pub struct Uart {
2321
/// The underlying device itself.
@@ -162,243 +160,3 @@ extern "C" fn async_callback(
162160
) {
163161
printkln!("Async");
164162
}
165-
166-
/// Size of the irq buffer used for UartIrq.
167-
///
168-
/// TODO: Make this a parameter of the type.
169-
const BUFFER_SIZE: usize = 256;
170-
171-
/// The "outer" struct holds the semaphore, and the mutex. The semaphore has to live outside of the
172-
/// mutex because it can only be waited on when the Mutex is not locked.
173-
struct IrqOuterData {
174-
read_sem: Semaphore,
175-
write_sem: Semaphore,
176-
inner: SpinMutex<IrqInnerData>,
177-
}
178-
179-
/// Data for communication with the UART IRQ.
180-
struct IrqInnerData {
181-
/// The Ring buffer holding incoming and read data.
182-
buffer: ArrayDeque<u8, BUFFER_SIZE>,
183-
/// Data to be written, if that is the case.
184-
///
185-
/// If this is Some, then the irq should be enabled.
186-
write: Option<WriteSlice>,
187-
}
188-
189-
/// Represents a slice of data that the irq is going to write.
190-
struct WriteSlice {
191-
data: *const u8,
192-
len: usize,
193-
}
194-
195-
impl WriteSlice {
196-
/// Add an offset to the beginning of this slice, returning a new slice. This is equivalent to
197-
/// &item[count..] with a slice.
198-
pub unsafe fn add(&self, count: usize) -> WriteSlice {
199-
WriteSlice {
200-
data: unsafe { self.data.add(count) },
201-
len: self.len - count,
202-
}
203-
}
204-
}
205-
206-
/// This is the irq-driven interface.
207-
pub struct UartIrq {
208-
/// Interior wrapped device, to be able to hand out lifetime managed references to it.
209-
uart: Uart,
210-
/// Critical section protected data.
211-
data: Arc<IrqOuterData>,
212-
}
213-
214-
// UartIrq is also Send, !Sync, for the same reasons as for Uart.
215-
unsafe impl Send for UartIrq {}
216-
217-
impl UartIrq {
218-
/// Convert uart into irq driven one.
219-
pub unsafe fn new(uart: Uart) -> Result<UartIrq> {
220-
let data = Arc::new(IrqOuterData {
221-
read_sem: Semaphore::new(0, 1)?,
222-
write_sem: Semaphore::new(0, 1)?,
223-
inner: SpinMutex::new(IrqInnerData {
224-
buffer: ArrayDeque::new(),
225-
write: None,
226-
}),
227-
});
228-
229-
// Clone the arc, and convert to a raw pointer, to give to the callback.
230-
// This will leak the Arc (which prevents deallocation).
231-
let data_raw = Arc::into_raw(data.clone());
232-
let data_raw = data_raw as *mut c_void;
233-
234-
let ret = unsafe {
235-
raw::uart_irq_callback_user_data_set(uart.device, Some(irq_callback), data_raw)
236-
};
237-
to_result_void(ret)?;
238-
// Should this be settable?
239-
unsafe {
240-
// raw::uart_irq_tx_enable(uart.device);
241-
raw::uart_irq_rx_enable(uart.device);
242-
}
243-
Ok(UartIrq {
244-
data,
245-
uart,
246-
})
247-
}
248-
249-
/// Get the underlying UART to be able to change line control and such.
250-
///
251-
/// TODO: This really should return something like `&Uart` to bind the lifetime. Otherwise the
252-
/// user can continue to use the uart handle beyond the lifetime of the driver.
253-
pub unsafe fn inner(&mut self) -> &Uart {
254-
&self.uart
255-
}
256-
257-
/// Attempt to read data from the UART into the buffer. If no data is available, it will
258-
/// attempt, once, to wait using the given timeout.
259-
///
260-
/// Returns the number of bytes that were read, with zero indicating that a timeout occurred.
261-
pub unsafe fn try_read<T>(&mut self, buf: &mut [u8], timeout: T) -> usize
262-
where T: Into<Timeout>,
263-
{
264-
// Start with a read, before any blocking.
265-
let count = self.data.try_read(buf);
266-
if count > 0 {
267-
return count;
268-
}
269-
270-
// Otherwise, wait for the semaphore. Ignore the result, as we will try to read again, in
271-
// case there was a race.
272-
let _ = self.data.read_sem.take(timeout);
273-
274-
self.data.try_read(buf)
275-
}
276-
277-
/// A blocking write to the UART.
278-
///
279-
/// By making this blocking, we don't need to make an extra copy of the data.
280-
///
281-
/// TODO: Async write.
282-
pub unsafe fn write<T>(&mut self, buf: &[u8], timeout: T) -> usize
283-
where T: Into<Timeout>
284-
{
285-
if buf.len() == 0 {
286-
return 0;
287-
}
288-
289-
// Make the data to be written available to the irq handler, and get it going.
290-
{
291-
let mut inner = self.data.inner.lock().unwrap();
292-
assert!(inner.write.is_none());
293-
294-
inner.write = Some(WriteSlice {
295-
data: buf.as_ptr(),
296-
len: buf.len(),
297-
});
298-
299-
unsafe { raw::uart_irq_tx_enable(self.uart.device) };
300-
}
301-
302-
// Wait for the transmission to complete. This shouldn't be racy, as the irq shouldn't be
303-
// giving the semaphore until there is 'write' data, and it has been consumed.
304-
let _ = self.data.write_sem.take(timeout);
305-
306-
// Depending on the driver, there might be a race here. This would result in the above
307-
// 'take' returning early, and no actual data being written.
308-
309-
{
310-
let mut inner = self.data.inner.lock().unwrap();
311-
312-
if let Some(write) = inner.write.take() {
313-
// First, make sure that no more interrupts will come in.
314-
unsafe { raw::uart_irq_tx_disable(self.uart.device) };
315-
316-
// The write did not complete, and this represents remaining data to write.
317-
buf.len() - write.len
318-
} else {
319-
// The write completed, the rx irq should be disabled. Just return the whole
320-
// buffer.
321-
buf.len()
322-
}
323-
}
324-
}
325-
}
326-
327-
impl IrqOuterData {
328-
/// Try reading from the inner data, filling the buffer with as much data as makes sense.
329-
/// Returns the number of bytes actually read, or Zero if none.
330-
fn try_read(&self, buf: &mut [u8]) -> usize {
331-
let mut inner = self.inner.lock().unwrap();
332-
let mut pos = 0;
333-
while pos < buf.len() {
334-
if let Some(elt) = inner.buffer.pop_front() {
335-
buf[pos] = elt;
336-
pos += 1;
337-
} else {
338-
break;
339-
}
340-
}
341-
342-
if pos > 0 {
343-
// Any time we do a read, clear the semaphore.
344-
let _ = self.read_sem.take(NoWait);
345-
}
346-
pos
347-
}
348-
}
349-
350-
extern "C" fn irq_callback(
351-
dev: *const raw::device,
352-
user_data: *mut c_void,
353-
) {
354-
// Convert our user data, back to the CS Mutex.
355-
let outer = unsafe { &*(user_data as *const IrqOuterData) };
356-
let mut inner = outer.inner.lock().unwrap();
357-
358-
// TODO: Make this more efficient.
359-
let mut byte = 0u8;
360-
let mut did_read = false;
361-
loop {
362-
match unsafe { raw::uart_fifo_read(dev, &mut byte, 1) } {
363-
0 => break,
364-
1 => {
365-
// TODO: should we warn about overflow here?
366-
let _ = inner.buffer.push_back(byte);
367-
did_read = true;
368-
}
369-
e => panic!("Uart fifo read not implemented: {}", e),
370-
}
371-
}
372-
373-
// This is safe (and important) to do while the mutex is held.
374-
if did_read {
375-
outer.read_sem.give();
376-
}
377-
378-
// If there is data to write, ensure the fifo is full, and when we run out of data, disable the
379-
// interrupt and signal the waiting thread.
380-
if let Some(write) = inner.write.take() {
381-
let count = unsafe {
382-
raw::uart_fifo_fill(dev, write.data, write.len as i32)
383-
};
384-
if count < 0 {
385-
panic!("Incorrect use of device fifo");
386-
}
387-
let count = count as usize;
388-
389-
if count == write.len {
390-
// The write finished, leave 'write' empty, and let the thread know we're done.
391-
outer.write_sem.give();
392-
393-
// Disable the tx fifo, as we don't need it any more.
394-
unsafe { raw::uart_irq_tx_disable(dev) };
395-
} else {
396-
// We're not finished, so remember how much is left.
397-
inner.write = Some(unsafe { write.add(count) });
398-
}
399-
}
400-
401-
unsafe {
402-
raw::uart_irq_update(dev);
403-
}
404-
}

0 commit comments

Comments
 (0)