@@ -11,7 +11,7 @@ use crate::error::{Error, Result, to_result_void, to_result};
11
11
use crate :: printkln;
12
12
use crate :: sys:: sync:: Semaphore ;
13
13
use crate :: sync:: { Arc , SpinMutex } ;
14
- use crate :: time:: { NoWait , Timeout } ;
14
+ use crate :: time:: { Forever , NoWait , Timeout } ;
15
15
16
16
use core:: ffi:: { c_int, c_uchar, c_void} ;
17
17
use core:: ptr;
@@ -164,13 +164,35 @@ const BUFFER_SIZE: usize = 256;
164
164
/// mutex because it can only be waited on when the Mutex is not locked.
165
165
struct IrqOuterData {
166
166
read_sem : Semaphore ,
167
+ write_sem : Semaphore ,
167
168
inner : SpinMutex < IrqInnerData > ,
168
169
}
169
170
170
171
/// Data for communication with the UART IRQ.
171
172
struct IrqInnerData {
172
173
/// The Ring buffer holding incoming and read data.
173
174
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
+ }
174
196
}
175
197
176
198
/// This is the irq-driven interface.
@@ -189,8 +211,10 @@ impl UartIrq {
189
211
pub unsafe fn new ( uart : Uart ) -> Result < UartIrq > {
190
212
let data = Arc :: new ( IrqOuterData {
191
213
read_sem : Semaphore :: new ( 0 , 1 ) ?,
214
+ write_sem : Semaphore :: new ( 0 , 1 ) ?,
192
215
inner : SpinMutex :: new ( IrqInnerData {
193
216
buffer : ArrayDeque :: new ( ) ,
217
+ write : None ,
194
218
} ) ,
195
219
} ) ;
196
220
@@ -205,7 +229,7 @@ impl UartIrq {
205
229
to_result_void ( ret) ?;
206
230
// Should this be settable?
207
231
unsafe {
208
- raw:: uart_irq_tx_enable ( uart. device ) ;
232
+ // raw::uart_irq_tx_enable(uart.device);
209
233
raw:: uart_irq_rx_enable ( uart. device ) ;
210
234
}
211
235
Ok ( UartIrq {
@@ -240,6 +264,36 @@ impl UartIrq {
240
264
241
265
self . data . try_read ( buf)
242
266
}
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
+ }
243
297
}
244
298
245
299
impl IrqOuterData {
@@ -292,4 +346,31 @@ extern "C" fn irq_callback(
292
346
if did_read {
293
347
outer. read_sem . give ( ) ;
294
348
}
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
+ }
295
376
}
0 commit comments