1 // Copyright 2024, Linaro Limited
2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
3 // SPDX-License-Identifier: GPL-2.0-or-later
4
5 use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut};
6
7 use qemu_api::{
8 chardev::{CharBackend, Chardev, Event},
9 impl_vmstate_forward,
10 irq::{IRQState, InterruptSource},
11 memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder},
12 prelude::*,
13 qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl},
14 qom::{ObjectImpl, Owned, ParentField},
15 static_assert,
16 sysbus::{SysBusDevice, SysBusDeviceImpl},
17 vmstate::VMStateDescription,
18 };
19
20 use crate::{
21 device_class,
22 registers::{self, Interrupt, RegisterOffset},
23 };
24
25 // TODO: You must disable the UART before any of the control registers are
26 // reprogrammed. When the UART is disabled in the middle of transmission or
27 // reception, it completes the current character before stopping
28
29 /// Integer Baud Rate Divider, `UARTIBRD`
30 const IBRD_MASK: u32 = 0xffff;
31
32 /// Fractional Baud Rate Divider, `UARTFBRD`
33 const FBRD_MASK: u32 = 0x3f;
34
35 /// QEMU sourced constant.
36 pub const PL011_FIFO_DEPTH: u32 = 16;
37
38 #[derive(Clone, Copy)]
39 struct DeviceId(&'static [u8; 8]);
40
41 impl std::ops::Index<hwaddr> for DeviceId {
42 type Output = u8;
43
index(&self, idx: hwaddr) -> &Self::Output44 fn index(&self, idx: hwaddr) -> &Self::Output {
45 &self.0[idx as usize]
46 }
47 }
48
49 // FIFOs use 32-bit indices instead of usize, for compatibility with
50 // the migration stream produced by the C version of this device.
51 #[repr(transparent)]
52 #[derive(Debug, Default)]
53 pub struct Fifo([registers::Data; PL011_FIFO_DEPTH as usize]);
54 impl_vmstate_forward!(Fifo);
55
56 impl Fifo {
len(&self) -> u3257 const fn len(&self) -> u32 {
58 self.0.len() as u32
59 }
60 }
61
62 impl std::ops::IndexMut<u32> for Fifo {
index_mut(&mut self, idx: u32) -> &mut Self::Output63 fn index_mut(&mut self, idx: u32) -> &mut Self::Output {
64 &mut self.0[idx as usize]
65 }
66 }
67
68 impl std::ops::Index<u32> for Fifo {
69 type Output = registers::Data;
70
index(&self, idx: u32) -> &Self::Output71 fn index(&self, idx: u32) -> &Self::Output {
72 &self.0[idx as usize]
73 }
74 }
75
76 #[repr(C)]
77 #[derive(Debug, Default)]
78 pub struct PL011Registers {
79 #[doc(alias = "fr")]
80 pub flags: registers::Flags,
81 #[doc(alias = "lcr")]
82 pub line_control: registers::LineControl,
83 #[doc(alias = "rsr")]
84 pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
85 #[doc(alias = "cr")]
86 pub control: registers::Control,
87 pub dmacr: u32,
88 pub int_enabled: Interrupt,
89 pub int_level: Interrupt,
90 pub read_fifo: Fifo,
91 pub ilpr: u32,
92 pub ibrd: u32,
93 pub fbrd: u32,
94 pub ifl: u32,
95 pub read_pos: u32,
96 pub read_count: u32,
97 pub read_trigger: u32,
98 }
99
100 #[repr(C)]
101 #[derive(qemu_api_macros::Object)]
102 /// PL011 Device Model in QEMU
103 pub struct PL011State {
104 pub parent_obj: ParentField<SysBusDevice>,
105 pub iomem: MemoryRegion,
106 #[doc(alias = "chr")]
107 pub char_backend: CharBackend,
108 pub regs: BqlRefCell<PL011Registers>,
109 /// QEMU interrupts
110 ///
111 /// ```text
112 /// * sysbus MMIO region 0: device registers
113 /// * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
114 /// * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
115 /// * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
116 /// * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
117 /// * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
118 /// * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
119 /// ```
120 #[doc(alias = "irq")]
121 pub interrupts: [InterruptSource; IRQMASK.len()],
122 #[doc(alias = "clk")]
123 pub clock: Owned<Clock>,
124 #[doc(alias = "migrate_clk")]
125 pub migrate_clock: bool,
126 }
127
128 // Some C users of this device embed its state struct into their own
129 // structs, so the size of the Rust version must not be any larger
130 // than the size of the C one. If this assert triggers you need to
131 // expand the padding_for_rust[] array in the C PL011State struct.
132 static_assert!(size_of::<PL011State>() <= size_of::<qemu_api::bindings::PL011State>());
133
134 qom_isa!(PL011State : SysBusDevice, DeviceState, Object);
135
136 #[repr(C)]
137 pub struct PL011Class {
138 parent_class: <SysBusDevice as ObjectType>::Class,
139 /// The byte string that identifies the device.
140 device_id: DeviceId,
141 }
142
143 trait PL011Impl: SysBusDeviceImpl + IsA<PL011State> {
144 const DEVICE_ID: DeviceId;
145 }
146
147 impl PL011Class {
class_init<T: PL011Impl>(&mut self)148 fn class_init<T: PL011Impl>(&mut self) {
149 self.device_id = T::DEVICE_ID;
150 self.parent_class.class_init::<T>();
151 }
152 }
153
154 unsafe impl ObjectType for PL011State {
155 type Class = PL011Class;
156 const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
157 }
158
159 impl PL011Impl for PL011State {
160 const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]);
161 }
162
163 impl ObjectImpl for PL011State {
164 type ParentType = SysBusDevice;
165
166 const INSTANCE_INIT: Option<unsafe fn(&mut Self)> = Some(Self::init);
167 const INSTANCE_POST_INIT: Option<fn(&Self)> = Some(Self::post_init);
168 const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
169 }
170
171 impl DeviceImpl for PL011State {
properties() -> &'static [Property]172 fn properties() -> &'static [Property] {
173 &device_class::PL011_PROPERTIES
174 }
vmsd() -> Option<&'static VMStateDescription>175 fn vmsd() -> Option<&'static VMStateDescription> {
176 Some(&device_class::VMSTATE_PL011)
177 }
178 const REALIZE: Option<fn(&Self)> = Some(Self::realize);
179 }
180
181 impl ResettablePhasesImpl for PL011State {
182 const HOLD: Option<fn(&Self, ResetType)> = Some(Self::reset_hold);
183 }
184
185 impl SysBusDeviceImpl for PL011State {}
186
187 impl PL011Registers {
read(&mut self, offset: RegisterOffset) -> (bool, u32)188 pub(self) fn read(&mut self, offset: RegisterOffset) -> (bool, u32) {
189 use RegisterOffset::*;
190
191 let mut update = false;
192 let result = match offset {
193 DR => self.read_data_register(&mut update),
194 RSR => u32::from(self.receive_status_error_clear),
195 FR => u32::from(self.flags),
196 FBRD => self.fbrd,
197 ILPR => self.ilpr,
198 IBRD => self.ibrd,
199 LCR_H => u32::from(self.line_control),
200 CR => u32::from(self.control),
201 FLS => self.ifl,
202 IMSC => u32::from(self.int_enabled),
203 RIS => u32::from(self.int_level),
204 MIS => u32::from(self.int_level & self.int_enabled),
205 ICR => {
206 // "The UARTICR Register is the interrupt clear register and is write-only"
207 // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
208 0
209 }
210 DMACR => self.dmacr,
211 };
212 (update, result)
213 }
214
write( &mut self, offset: RegisterOffset, value: u32, char_backend: &CharBackend, ) -> bool215 pub(self) fn write(
216 &mut self,
217 offset: RegisterOffset,
218 value: u32,
219 char_backend: &CharBackend,
220 ) -> bool {
221 // eprintln!("write offset {offset} value {value}");
222 use RegisterOffset::*;
223 match offset {
224 DR => return self.write_data_register(value),
225 RSR => {
226 self.receive_status_error_clear = 0.into();
227 }
228 FR => {
229 // flag writes are ignored
230 }
231 ILPR => {
232 self.ilpr = value;
233 }
234 IBRD => {
235 self.ibrd = value;
236 }
237 FBRD => {
238 self.fbrd = value;
239 }
240 LCR_H => {
241 let new_val: registers::LineControl = value.into();
242 // Reset the FIFO state on FIFO enable or disable
243 if self.line_control.fifos_enabled() != new_val.fifos_enabled() {
244 self.reset_rx_fifo();
245 self.reset_tx_fifo();
246 }
247 let update = (self.line_control.send_break() != new_val.send_break()) && {
248 let break_enable = new_val.send_break();
249 let _ = char_backend.send_break(break_enable);
250 self.loopback_break(break_enable)
251 };
252 self.line_control = new_val;
253 self.set_read_trigger();
254 return update;
255 }
256 CR => {
257 // ??? Need to implement the enable bit.
258 self.control = value.into();
259 return self.loopback_mdmctrl();
260 }
261 FLS => {
262 self.ifl = value;
263 self.set_read_trigger();
264 }
265 IMSC => {
266 self.int_enabled = Interrupt::from(value);
267 return true;
268 }
269 RIS => {}
270 MIS => {}
271 ICR => {
272 self.int_level &= !Interrupt::from(value);
273 return true;
274 }
275 DMACR => {
276 self.dmacr = value;
277 if value & 3 > 0 {
278 // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
279 eprintln!("pl011: DMA not implemented");
280 }
281 }
282 }
283 false
284 }
285
read_data_register(&mut self, update: &mut bool) -> u32286 fn read_data_register(&mut self, update: &mut bool) -> u32 {
287 self.flags.set_receive_fifo_full(false);
288 let c = self.read_fifo[self.read_pos];
289
290 if self.read_count > 0 {
291 self.read_count -= 1;
292 self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
293 }
294 if self.read_count == 0 {
295 self.flags.set_receive_fifo_empty(true);
296 }
297 if self.read_count + 1 == self.read_trigger {
298 self.int_level &= !Interrupt::RX;
299 }
300 self.receive_status_error_clear.set_from_data(c);
301 *update = true;
302 u32::from(c)
303 }
304
write_data_register(&mut self, value: u32) -> bool305 fn write_data_register(&mut self, value: u32) -> bool {
306 // interrupts always checked
307 let _ = self.loopback_tx(value.into());
308 self.int_level |= Interrupt::TX;
309 true
310 }
311
312 #[inline]
313 #[must_use]
loopback_tx(&mut self, value: registers::Data) -> bool314 fn loopback_tx(&mut self, value: registers::Data) -> bool {
315 // Caveat:
316 //
317 // In real hardware, TX loopback happens at the serial-bit level
318 // and then reassembled by the RX logics back into bytes and placed
319 // into the RX fifo. That is, loopback happens after TX fifo.
320 //
321 // Because the real hardware TX fifo is time-drained at the frame
322 // rate governed by the configured serial format, some loopback
323 // bytes in TX fifo may still be able to get into the RX fifo
324 // that could be full at times while being drained at software
325 // pace.
326 //
327 // In such scenario, the RX draining pace is the major factor
328 // deciding which loopback bytes get into the RX fifo, unless
329 // hardware flow-control is enabled.
330 //
331 // For simplicity, the above described is not emulated.
332 self.loopback_enabled() && self.fifo_rx_put(value)
333 }
334
335 #[must_use]
loopback_mdmctrl(&mut self) -> bool336 fn loopback_mdmctrl(&mut self) -> bool {
337 if !self.loopback_enabled() {
338 return false;
339 }
340
341 /*
342 * Loopback software-driven modem control outputs to modem status inputs:
343 * FR.RI <= CR.Out2
344 * FR.DCD <= CR.Out1
345 * FR.CTS <= CR.RTS
346 * FR.DSR <= CR.DTR
347 *
348 * The loopback happens immediately even if this call is triggered
349 * by setting only CR.LBE.
350 *
351 * CTS/RTS updates due to enabled hardware flow controls are not
352 * dealt with here.
353 */
354
355 self.flags.set_ring_indicator(self.control.out_2());
356 self.flags.set_data_carrier_detect(self.control.out_1());
357 self.flags.set_clear_to_send(self.control.request_to_send());
358 self.flags
359 .set_data_set_ready(self.control.data_transmit_ready());
360
361 // Change interrupts based on updated FR
362 let mut il = self.int_level;
363
364 il &= !Interrupt::MS;
365
366 if self.flags.data_set_ready() {
367 il |= Interrupt::DSR;
368 }
369 if self.flags.data_carrier_detect() {
370 il |= Interrupt::DCD;
371 }
372 if self.flags.clear_to_send() {
373 il |= Interrupt::CTS;
374 }
375 if self.flags.ring_indicator() {
376 il |= Interrupt::RI;
377 }
378 self.int_level = il;
379 true
380 }
381
loopback_break(&mut self, enable: bool) -> bool382 fn loopback_break(&mut self, enable: bool) -> bool {
383 enable && self.loopback_tx(registers::Data::BREAK)
384 }
385
set_read_trigger(&mut self)386 fn set_read_trigger(&mut self) {
387 self.read_trigger = 1;
388 }
389
reset(&mut self)390 pub fn reset(&mut self) {
391 self.line_control.reset();
392 self.receive_status_error_clear.reset();
393 self.dmacr = 0;
394 self.int_enabled = 0.into();
395 self.int_level = 0.into();
396 self.ilpr = 0;
397 self.ibrd = 0;
398 self.fbrd = 0;
399 self.read_trigger = 1;
400 self.ifl = 0x12;
401 self.control.reset();
402 self.flags.reset();
403 self.reset_rx_fifo();
404 self.reset_tx_fifo();
405 }
406
reset_rx_fifo(&mut self)407 pub fn reset_rx_fifo(&mut self) {
408 self.read_count = 0;
409 self.read_pos = 0;
410
411 // Reset FIFO flags
412 self.flags.set_receive_fifo_full(false);
413 self.flags.set_receive_fifo_empty(true);
414 }
415
reset_tx_fifo(&mut self)416 pub fn reset_tx_fifo(&mut self) {
417 // Reset FIFO flags
418 self.flags.set_transmit_fifo_full(false);
419 self.flags.set_transmit_fifo_empty(true);
420 }
421
422 #[inline]
fifo_enabled(&self) -> bool423 pub fn fifo_enabled(&self) -> bool {
424 self.line_control.fifos_enabled() == registers::Mode::FIFO
425 }
426
427 #[inline]
loopback_enabled(&self) -> bool428 pub fn loopback_enabled(&self) -> bool {
429 self.control.enable_loopback()
430 }
431
432 #[inline]
fifo_depth(&self) -> u32433 pub fn fifo_depth(&self) -> u32 {
434 // Note: FIFO depth is expected to be power-of-2
435 if self.fifo_enabled() {
436 return PL011_FIFO_DEPTH;
437 }
438 1
439 }
440
441 #[must_use]
fifo_rx_put(&mut self, value: registers::Data) -> bool442 pub fn fifo_rx_put(&mut self, value: registers::Data) -> bool {
443 let depth = self.fifo_depth();
444 assert!(depth > 0);
445 let slot = (self.read_pos + self.read_count) & (depth - 1);
446 self.read_fifo[slot] = value;
447 self.read_count += 1;
448 self.flags.set_receive_fifo_empty(false);
449 if self.read_count == depth {
450 self.flags.set_receive_fifo_full(true);
451 }
452
453 if self.read_count == self.read_trigger {
454 self.int_level |= Interrupt::RX;
455 return true;
456 }
457 false
458 }
459
post_load(&mut self) -> Result<(), ()>460 pub fn post_load(&mut self) -> Result<(), ()> {
461 /* Sanity-check input state */
462 if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
463 return Err(());
464 }
465
466 if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
467 // Older versions of PL011 didn't ensure that the single
468 // character in the FIFO in FIFO-disabled mode is in
469 // element 0 of the array; convert to follow the current
470 // code's assumptions.
471 self.read_fifo[0] = self.read_fifo[self.read_pos];
472 self.read_pos = 0;
473 }
474
475 self.ibrd &= IBRD_MASK;
476 self.fbrd &= FBRD_MASK;
477
478 Ok(())
479 }
480 }
481
482 impl PL011State {
483 /// Initializes a pre-allocated, uninitialized instance of `PL011State`.
484 ///
485 /// # Safety
486 ///
487 /// `self` must point to a correctly sized and aligned location for the
488 /// `PL011State` type. It must not be called more than once on the same
489 /// location/instance. All its fields are expected to hold uninitialized
490 /// values with the sole exception of `parent_obj`.
init(&mut self)491 unsafe fn init(&mut self) {
492 static PL011_OPS: MemoryRegionOps<PL011State> = MemoryRegionOpsBuilder::<PL011State>::new()
493 .read(&PL011State::read)
494 .write(&PL011State::write)
495 .native_endian()
496 .impl_sizes(4, 4)
497 .build();
498
499 // SAFETY:
500 //
501 // self and self.iomem are guaranteed to be valid at this point since callers
502 // must make sure the `self` reference is valid.
503 MemoryRegion::init_io(
504 unsafe { &mut *addr_of_mut!(self.iomem) },
505 addr_of_mut!(*self),
506 &PL011_OPS,
507 "pl011",
508 0x1000,
509 );
510
511 self.regs = Default::default();
512
513 // SAFETY:
514 //
515 // self.clock is not initialized at this point; but since `Owned<_>` is
516 // not Drop, we can overwrite the undefined value without side effects;
517 // it's not sound but, because for all PL011State instances are created
518 // by QOM code which calls this function to initialize the fields, at
519 // leastno code is able to access an invalid self.clock value.
520 self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate);
521 }
522
clock_update(&self, _event: ClockEvent)523 const fn clock_update(&self, _event: ClockEvent) {
524 /* pl011_trace_baudrate_change(s); */
525 }
526
post_init(&self)527 fn post_init(&self) {
528 self.init_mmio(&self.iomem);
529 for irq in self.interrupts.iter() {
530 self.init_irq(irq);
531 }
532 }
533
read(&self, offset: hwaddr, _size: u32) -> u64534 fn read(&self, offset: hwaddr, _size: u32) -> u64 {
535 match RegisterOffset::try_from(offset) {
536 Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
537 let device_id = self.get_class().device_id;
538 u64::from(device_id[(offset - 0xfe0) >> 2])
539 }
540 Err(_) => {
541 // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
542 0
543 }
544 Ok(field) => {
545 let (update_irq, result) = self.regs.borrow_mut().read(field);
546 if update_irq {
547 self.update();
548 self.char_backend.accept_input();
549 }
550 result.into()
551 }
552 }
553 }
554
write(&self, offset: hwaddr, value: u64, _size: u32)555 fn write(&self, offset: hwaddr, value: u64, _size: u32) {
556 let mut update_irq = false;
557 if let Ok(field) = RegisterOffset::try_from(offset) {
558 // qemu_chr_fe_write_all() calls into the can_receive
559 // callback, so handle writes before entering PL011Registers.
560 if field == RegisterOffset::DR {
561 // ??? Check if transmitter is enabled.
562 let ch: [u8; 1] = [value as u8];
563 // XXX this blocks entire thread. Rewrite to use
564 // qemu_chr_fe_write and background I/O callbacks
565 let _ = self.char_backend.write_all(&ch);
566 }
567
568 update_irq = self
569 .regs
570 .borrow_mut()
571 .write(field, value as u32, &self.char_backend);
572 } else {
573 eprintln!("write bad offset {offset} value {value}");
574 }
575 if update_irq {
576 self.update();
577 }
578 }
579
can_receive(&self) -> u32580 fn can_receive(&self) -> u32 {
581 let regs = self.regs.borrow();
582 // trace_pl011_can_receive(s->lcr, s->read_count, r);
583 regs.fifo_depth() - regs.read_count
584 }
585
receive(&self, buf: &[u8])586 fn receive(&self, buf: &[u8]) {
587 let mut regs = self.regs.borrow_mut();
588 if regs.loopback_enabled() {
589 // In loopback mode, the RX input signal is internally disconnected
590 // from the entire receiving logics; thus, all inputs are ignored,
591 // and BREAK detection on RX input signal is also not performed.
592 return;
593 }
594
595 let mut update_irq = false;
596 for &c in buf {
597 let c: u32 = c.into();
598 update_irq |= regs.fifo_rx_put(c.into());
599 }
600
601 // Release the BqlRefCell before calling self.update()
602 drop(regs);
603 if update_irq {
604 self.update();
605 }
606 }
607
event(&self, event: Event)608 fn event(&self, event: Event) {
609 let mut update_irq = false;
610 let mut regs = self.regs.borrow_mut();
611 if event == Event::CHR_EVENT_BREAK && !regs.loopback_enabled() {
612 update_irq = regs.fifo_rx_put(registers::Data::BREAK);
613 }
614 // Release the BqlRefCell before calling self.update()
615 drop(regs);
616
617 if update_irq {
618 self.update()
619 }
620 }
621
realize(&self)622 fn realize(&self) {
623 self.char_backend
624 .enable_handlers(self, Self::can_receive, Self::receive, Self::event);
625 }
626
reset_hold(&self, _type: ResetType)627 fn reset_hold(&self, _type: ResetType) {
628 self.regs.borrow_mut().reset();
629 }
630
update(&self)631 fn update(&self) {
632 let regs = self.regs.borrow();
633 let flags = regs.int_level & regs.int_enabled;
634 for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
635 irq.set(flags.any_set(i));
636 }
637 }
638
post_load(&self, _version_id: u32) -> Result<(), ()>639 pub fn post_load(&self, _version_id: u32) -> Result<(), ()> {
640 self.regs.borrow_mut().post_load()
641 }
642 }
643
644 /// Which bits in the interrupt status matter for each outbound IRQ line ?
645 const IRQMASK: [Interrupt; 6] = [
646 Interrupt::all(),
647 Interrupt::RX,
648 Interrupt::TX,
649 Interrupt::RT,
650 Interrupt::MS,
651 Interrupt::E,
652 ];
653
654 /// # Safety
655 ///
656 /// We expect the FFI user of this function to pass a valid pointer for `chr`
657 /// and `irq`.
658 #[no_mangle]
pl011_create( addr: u64, irq: *mut IRQState, chr: *mut Chardev, ) -> *mut DeviceState659 pub unsafe extern "C" fn pl011_create(
660 addr: u64,
661 irq: *mut IRQState,
662 chr: *mut Chardev,
663 ) -> *mut DeviceState {
664 // SAFETY: The callers promise that they have owned references.
665 // They do not gift them to pl011_create, so use `Owned::from`.
666 let irq = unsafe { Owned::<IRQState>::from(&*irq) };
667
668 let dev = PL011State::new();
669 if !chr.is_null() {
670 let chr = unsafe { Owned::<Chardev>::from(&*chr) };
671 dev.prop_set_chr("chardev", &chr);
672 }
673 dev.sysbus_realize();
674 dev.mmio_map(0, addr);
675 dev.connect_irq(0, &irq);
676
677 // The pointer is kept alive by the QOM tree; drop the owned ref
678 dev.as_mut_ptr()
679 }
680
681 #[repr(C)]
682 #[derive(qemu_api_macros::Object)]
683 /// PL011 Luminary device model.
684 pub struct PL011Luminary {
685 parent_obj: ParentField<PL011State>,
686 }
687
688 qom_isa!(PL011Luminary : PL011State, SysBusDevice, DeviceState, Object);
689
690 unsafe impl ObjectType for PL011Luminary {
691 type Class = <PL011State as ObjectType>::Class;
692 const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
693 }
694
695 impl ObjectImpl for PL011Luminary {
696 type ParentType = PL011State;
697
698 const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::<Self>;
699 }
700
701 impl PL011Impl for PL011Luminary {
702 const DEVICE_ID: DeviceId = DeviceId(&[0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1]);
703 }
704
705 impl DeviceImpl for PL011Luminary {}
706 impl ResettablePhasesImpl for PL011Luminary {}
707 impl SysBusDeviceImpl for PL011Luminary {}
708