@@ -99,6 +99,33 @@ const DEFAULT_MODEM_CONTROL: u8 = MCR_OUT2_BIT;
99
99
const DEFAULT_MODEM_STATUS : u8 = MSR_DSR_BIT | MSR_CTS_BIT | MSR_DCD_BIT ;
100
100
const DEFAULT_SCRATCH : u8 = 0x00 ;
101
101
102
+ /// Defines a series of callbacks that are invoked in response to the occurrence of specific
103
+ /// events as part of the serial emulation logic (for example, when the driver reads data). The
104
+ /// methods below can be implemented by a backend that keeps track of such events by incrementing
105
+ /// metrics, logging messages, or any other action.
106
+ ///
107
+ /// We're using a trait to avoid constraining the concrete characteristics of the backend in
108
+ /// any way, enabling zero-cost abstractions and use case-specific implementations.
109
+ // TODO: The events defined below are just some examples for now to validate the approach. If
110
+ // things look good, we can move on to establishing the initial list. It's also worth mentioning
111
+ // the methods can have extra parameters that provide additional information about the event.
112
+ pub trait SerialEvents {
113
+ /// The driver reads data from the input buffer.
114
+ fn buffer_read ( & self ) ;
115
+ /// The driver successfully wrote one byte to serial output.
116
+ fn out_byte ( & self ) ;
117
+ }
118
+
119
+ /// Provides a no-op implementation of `SerialEvents` which can be used in situations that
120
+ /// do not require logging or otherwise doing anything in response to the events defined
121
+ /// as part of `SerialEvents`.
122
+ pub struct NoEvents ;
123
+
124
+ impl SerialEvents for NoEvents {
125
+ fn buffer_read ( & self ) { }
126
+ fn out_byte ( & self ) { }
127
+ }
128
+
102
129
/// The serial console emulation is done by emulating a serial COM port.
103
130
///
104
131
/// Each serial COM port (COM1-4) has an associated Port I/O address base and
@@ -159,7 +186,7 @@ const DEFAULT_SCRATCH: u8 = 0x00;
159
186
/// serial.enqueue_raw_bytes(input).unwrap();
160
187
/// }
161
188
/// ```
162
- pub struct Serial < T : Trigger , W : Write > {
189
+ pub struct Serial < T : Trigger , EV : SerialEvents , W : Write > {
163
190
// Some UART registers.
164
191
baud_divisor_low : u8 ,
165
192
baud_divisor_high : u8 ,
@@ -178,6 +205,7 @@ pub struct Serial<T: Trigger, W: Write> {
178
205
179
206
// Used for notifying the driver about some in/out events.
180
207
interrupt_evt : T ,
208
+ serial_evts : EV ,
181
209
out : W ,
182
210
}
183
211
@@ -192,7 +220,7 @@ pub enum Error<E> {
192
220
FullFifo ,
193
221
}
194
222
195
- impl < T : Trigger , W : Write > Serial < T , W > {
223
+ impl < T : Trigger , W : Write > Serial < T , NoEvents , W > {
196
224
/// Creates a new `Serial` instance which writes the guest's output to
197
225
/// `out` and uses `trigger` object to notify the driver about new
198
226
/// events.
@@ -209,7 +237,27 @@ impl<T: Trigger, W: Write> Serial<T, W> {
209
237
///
210
238
/// You can see an example of how to use this function in the
211
239
/// [`Example` section from `Serial`](struct.Serial.html#example).
212
- pub fn new ( trigger : T , out : W ) -> Serial < T , W > {
240
+ pub fn new ( trigger : T , out : W ) -> Serial < T , NoEvents , W > {
241
+ Self :: with_events ( trigger, NoEvents , out)
242
+ }
243
+ }
244
+
245
+ impl < T : Trigger , EV : SerialEvents , W : Write > Serial < T , EV , W > {
246
+ /// Creates a new `Serial` instance which writes the guest's output to
247
+ /// `out`, uses `trigger` object to notify the driver about new
248
+ /// events, and invokes the `serial_evts` implementation of `SerialEvents`
249
+ /// during operation.
250
+ ///
251
+ /// # Arguments
252
+ /// * `trigger` - The `Trigger` object that will be used to notify the driver
253
+ /// about events.
254
+ /// * `serial_evts` - The `SerialEvents` implementation used to track the occurrence
255
+ /// of significant events in the serial operation logic.
256
+ /// * `out` - An object for writing guest's output to. In case the output
257
+ /// is not of interest,
258
+ /// [std::io::Sink](https://doc.rust-lang.org/std/io/struct.Sink.html)
259
+ /// can be used here.
260
+ pub fn with_events ( trigger : T , serial_evts : EV , out : W ) -> Self {
213
261
Serial {
214
262
baud_divisor_low : DEFAULT_BAUD_DIVISOR_LOW ,
215
263
baud_divisor_high : DEFAULT_BAUD_DIVISOR_HIGH ,
@@ -222,6 +270,7 @@ impl<T: Trigger, W: Write> Serial<T, W> {
222
270
scratch : DEFAULT_SCRATCH ,
223
271
in_buffer : VecDeque :: new ( ) ,
224
272
interrupt_evt : trigger,
273
+ serial_evts,
225
274
out,
226
275
}
227
276
}
@@ -331,6 +380,8 @@ impl<T: Trigger, W: Write> Serial<T, W> {
331
380
} else {
332
381
self . out . write_all ( & [ value] ) . map_err ( Error :: IOError ) ?;
333
382
self . out . flush ( ) . map_err ( Error :: IOError ) ?;
383
+ self . serial_evts . out_byte ( ) ;
384
+
334
385
self . thr_empty_interrupt ( ) . map_err ( Error :: Trigger ) ?;
335
386
}
336
387
}
@@ -371,6 +422,8 @@ impl<T: Trigger, W: Write> Serial<T, W> {
371
422
if self . in_buffer . len ( ) <= 1 {
372
423
self . clear_lsr_rda_bit ( ) ;
373
424
}
425
+
426
+ self . serial_evts . buffer_read ( ) ;
374
427
self . in_buffer . pop_front ( ) . unwrap_or_default ( )
375
428
}
376
429
IER_OFFSET => self . interrupt_enable ,
@@ -464,9 +517,13 @@ impl<T: Trigger, W: Write> Serial<T, W> {
464
517
#[ cfg( test) ]
465
518
mod tests {
466
519
use super :: * ;
520
+
467
521
use std:: io:: { sink, Result } ;
522
+ use std:: sync:: atomic:: AtomicU64 ;
523
+ use std:: sync:: Arc ;
468
524
469
525
use vmm_sys_util:: eventfd:: EventFd ;
526
+ use vmm_sys_util:: metric:: Metric ;
470
527
471
528
const RAW_INPUT_BUF : [ u8 ; 3 ] = [ b'a' , b'b' , b'c' ] ;
472
529
@@ -478,6 +535,25 @@ mod tests {
478
535
}
479
536
}
480
537
538
+ #[ derive( Default ) ]
539
+ struct ExampleSerialMetrics {
540
+ read_count : AtomicU64 ,
541
+ out_byte_count : AtomicU64 ,
542
+ }
543
+
544
+ // We define this for `Arc<ExampleSerialMetrics>` because we expect to share a reference to
545
+ // the same object with other threads.
546
+ impl SerialEvents for Arc < ExampleSerialMetrics > {
547
+ fn buffer_read ( & self ) {
548
+ self . read_count . inc ( ) ;
549
+ // We can also log a message here, or as part of any of the other methods.
550
+ }
551
+
552
+ fn out_byte ( & self ) {
553
+ self . out_byte_count . inc ( ) ;
554
+ }
555
+ }
556
+
481
557
#[ test]
482
558
fn test_serial_output ( ) {
483
559
let intr_evt = EventFd :: new ( libc:: EFD_NONBLOCK ) . unwrap ( ) ;
@@ -698,4 +774,28 @@ mod tests {
698
774
assert_eq ! ( written_bytes, 1 ) ;
699
775
assert_eq ! ( serial. in_buffer. len( ) , FIFO_SIZE ) ;
700
776
}
777
+
778
+ #[ test]
779
+ fn test_serial_events ( ) {
780
+ let intr_evt = EventFd :: new ( libc:: EFD_NONBLOCK ) . unwrap ( ) ;
781
+ let metrics = Arc :: new ( ExampleSerialMetrics :: default ( ) ) ;
782
+ let mut serial = Serial :: with_events ( intr_evt, metrics, sink ( ) ) ;
783
+
784
+ // Check everything is equal to 0 at the beginning.
785
+ assert_eq ! ( serial. serial_evts. read_count. count( ) , 0 ) ;
786
+ assert_eq ! ( serial. serial_evts. out_byte_count. count( ) , 0 ) ;
787
+
788
+ // This DATA read should cause the `SerialEvents::buffer_read` method to be invoked.
789
+ serial. read ( DATA_OFFSET ) ;
790
+ assert_eq ! ( serial. serial_evts. read_count. count( ) , 1 ) ;
791
+
792
+ // This DATA write should cause `SerialEvents::out_byte` to be called.
793
+ serial. write ( DATA_OFFSET , 1 ) . unwrap ( ) ;
794
+ assert_eq ! ( serial. serial_evts. out_byte_count. count( ) , 1 ) ;
795
+
796
+ // Check that every metric has the expected value at the end, to ensure we didn't
797
+ // unexpectedly invoked any extra callbacks.
798
+ assert_eq ! ( serial. serial_evts. read_count. count( ) , 1 ) ;
799
+ assert_eq ! ( serial. serial_evts. out_byte_count. count( ) , 1 ) ;
800
+ }
701
801
}
0 commit comments