xref: /openbmc/qemu/rust/hw/char/pl011/src/device.rs (revision b8ee011e40e4b83a32ea0e7dca24e1ab089f1e7f)
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 core::ptr::{addr_of, addr_of_mut, NonNull};
6  use std::{
7      ffi::CStr,
8      os::raw::{c_int, c_uchar, c_uint, c_void},
9  };
10  
11  use qemu_api::{
12      bindings::{self, *},
13      c_str,
14      definitions::ObjectImpl,
15      device_class::TYPE_SYS_BUS_DEVICE,
16  };
17  
18  use crate::{
19      memory_ops::PL011_OPS,
20      registers::{self, Interrupt},
21      RegisterOffset,
22  };
23  
24  /// Integer Baud Rate Divider, `UARTIBRD`
25  const IBRD_MASK: u32 = 0xffff;
26  
27  /// Fractional Baud Rate Divider, `UARTFBRD`
28  const FBRD_MASK: u32 = 0x3f;
29  
30  const DATA_BREAK: u32 = 1 << 10;
31  
32  /// QEMU sourced constant.
33  pub const PL011_FIFO_DEPTH: usize = 16_usize;
34  
35  #[derive(Clone, Copy, Debug)]
36  enum DeviceId {
37      #[allow(dead_code)]
38      Arm = 0,
39      Luminary,
40  }
41  
42  impl std::ops::Index<hwaddr> for DeviceId {
43      type Output = c_uchar;
44  
index(&self, idx: hwaddr) -> &Self::Output45      fn index(&self, idx: hwaddr) -> &Self::Output {
46          match self {
47              Self::Arm => &Self::PL011_ID_ARM[idx as usize],
48              Self::Luminary => &Self::PL011_ID_LUMINARY[idx as usize],
49          }
50      }
51  }
52  
53  impl DeviceId {
54      const PL011_ID_ARM: [c_uchar; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1];
55      const PL011_ID_LUMINARY: [c_uchar; 8] = [0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1];
56  }
57  
58  #[repr(C)]
59  #[derive(Debug, qemu_api_macros::Object, qemu_api_macros::offsets)]
60  /// PL011 Device Model in QEMU
61  pub struct PL011State {
62      pub parent_obj: SysBusDevice,
63      pub iomem: MemoryRegion,
64      #[doc(alias = "fr")]
65      pub flags: registers::Flags,
66      #[doc(alias = "lcr")]
67      pub line_control: registers::LineControl,
68      #[doc(alias = "rsr")]
69      pub receive_status_error_clear: registers::ReceiveStatusErrorClear,
70      #[doc(alias = "cr")]
71      pub control: registers::Control,
72      pub dmacr: u32,
73      pub int_enabled: u32,
74      pub int_level: u32,
75      pub read_fifo: [u32; PL011_FIFO_DEPTH],
76      pub ilpr: u32,
77      pub ibrd: u32,
78      pub fbrd: u32,
79      pub ifl: u32,
80      pub read_pos: usize,
81      pub read_count: usize,
82      pub read_trigger: usize,
83      #[doc(alias = "chr")]
84      pub char_backend: CharBackend,
85      /// QEMU interrupts
86      ///
87      /// ```text
88      ///  * sysbus MMIO region 0: device registers
89      ///  * sysbus IRQ 0: `UARTINTR` (combined interrupt line)
90      ///  * sysbus IRQ 1: `UARTRXINTR` (receive FIFO interrupt line)
91      ///  * sysbus IRQ 2: `UARTTXINTR` (transmit FIFO interrupt line)
92      ///  * sysbus IRQ 3: `UARTRTINTR` (receive timeout interrupt line)
93      ///  * sysbus IRQ 4: `UARTMSINTR` (momem status interrupt line)
94      ///  * sysbus IRQ 5: `UARTEINTR` (error interrupt line)
95      /// ```
96      #[doc(alias = "irq")]
97      pub interrupts: [qemu_irq; 6usize],
98      #[doc(alias = "clk")]
99      pub clock: NonNull<Clock>,
100      #[doc(alias = "migrate_clk")]
101      pub migrate_clock: bool,
102      /// The byte string that identifies the device.
103      device_id: DeviceId,
104  }
105  
106  impl ObjectImpl for PL011State {
107      type Class = PL011Class;
108      const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
109      const TYPE_NAME: &'static CStr = crate::TYPE_PL011;
110      const PARENT_TYPE_NAME: Option<&'static CStr> = Some(TYPE_SYS_BUS_DEVICE);
111      const ABSTRACT: bool = false;
112      const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_init);
113      const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
114      const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
115  }
116  
117  #[repr(C)]
118  pub struct PL011Class {
119      _inner: [u8; 0],
120  }
121  
122  impl qemu_api::definitions::Class for PL011Class {
123      const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
124          Some(crate::device_class::pl011_class_init);
125      const CLASS_BASE_INIT: Option<
126          unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
127      > = None;
128  }
129  
130  impl PL011State {
131      /// Initializes a pre-allocated, unitialized instance of `PL011State`.
132      ///
133      /// # Safety
134      ///
135      /// `self` must point to a correctly sized and aligned location for the
136      /// `PL011State` type. It must not be called more than once on the same
137      /// location/instance. All its fields are expected to hold unitialized
138      /// values with the sole exception of `parent_obj`.
init(&mut self)139      unsafe fn init(&mut self) {
140          const CLK_NAME: &CStr = c_str!("clk");
141  
142          let dev = addr_of_mut!(*self).cast::<DeviceState>();
143          // SAFETY:
144          //
145          // self and self.iomem are guaranteed to be valid at this point since callers
146          // must make sure the `self` reference is valid.
147          unsafe {
148              memory_region_init_io(
149                  addr_of_mut!(self.iomem),
150                  addr_of_mut!(*self).cast::<Object>(),
151                  &PL011_OPS,
152                  addr_of_mut!(*self).cast::<c_void>(),
153                  Self::TYPE_INFO.name,
154                  0x1000,
155              );
156              let sbd = addr_of_mut!(*self).cast::<SysBusDevice>();
157              sysbus_init_mmio(sbd, addr_of_mut!(self.iomem));
158              for irq in self.interrupts.iter_mut() {
159                  sysbus_init_irq(sbd, irq);
160              }
161          }
162          // SAFETY:
163          //
164          // self.clock is not initialized at this point; but since `NonNull<_>` is Copy,
165          // we can overwrite the undefined value without side effects. This is
166          // safe since all PL011State instances are created by QOM code which
167          // calls this function to initialize the fields; therefore no code is
168          // able to access an invalid self.clock value.
169          unsafe {
170              self.clock = NonNull::new(qdev_init_clock_in(
171                  dev,
172                  CLK_NAME.as_ptr(),
173                  None, /* pl011_clock_update */
174                  addr_of_mut!(*self).cast::<c_void>(),
175                  ClockEvent::ClockUpdate.0,
176              ))
177              .unwrap();
178          }
179      }
180  
read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64>181      pub fn read(&mut self, offset: hwaddr, _size: c_uint) -> std::ops::ControlFlow<u64, u64> {
182          use RegisterOffset::*;
183  
184          std::ops::ControlFlow::Break(match RegisterOffset::try_from(offset) {
185              Err(v) if (0x3f8..0x400).contains(&(v >> 2)) => {
186                  u64::from(self.device_id[(offset - 0xfe0) >> 2])
187              }
188              Err(_) => {
189                  // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset);
190                  0
191              }
192              Ok(DR) => {
193                  self.flags.set_receive_fifo_full(false);
194                  let c = self.read_fifo[self.read_pos];
195                  if self.read_count > 0 {
196                      self.read_count -= 1;
197                      self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1);
198                  }
199                  if self.read_count == 0 {
200                      self.flags.set_receive_fifo_empty(true);
201                  }
202                  if self.read_count + 1 == self.read_trigger {
203                      self.int_level &= !registers::INT_RX;
204                  }
205                  // Update error bits.
206                  self.receive_status_error_clear = c.to_be_bytes()[3].into();
207                  self.update();
208                  // Must call qemu_chr_fe_accept_input, so return Continue:
209                  return std::ops::ControlFlow::Continue(c.into());
210              }
211              Ok(RSR) => u8::from(self.receive_status_error_clear).into(),
212              Ok(FR) => u16::from(self.flags).into(),
213              Ok(FBRD) => self.fbrd.into(),
214              Ok(ILPR) => self.ilpr.into(),
215              Ok(IBRD) => self.ibrd.into(),
216              Ok(LCR_H) => u16::from(self.line_control).into(),
217              Ok(CR) => {
218                  // We exercise our self-control.
219                  u16::from(self.control).into()
220              }
221              Ok(FLS) => self.ifl.into(),
222              Ok(IMSC) => self.int_enabled.into(),
223              Ok(RIS) => self.int_level.into(),
224              Ok(MIS) => u64::from(self.int_level & self.int_enabled),
225              Ok(ICR) => {
226                  // "The UARTICR Register is the interrupt clear register and is write-only"
227                  // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR
228                  0
229              }
230              Ok(DMACR) => self.dmacr.into(),
231          })
232      }
233  
write(&mut self, offset: hwaddr, value: u64)234      pub fn write(&mut self, offset: hwaddr, value: u64) {
235          // eprintln!("write offset {offset} value {value}");
236          use RegisterOffset::*;
237          let value: u32 = value as u32;
238          match RegisterOffset::try_from(offset) {
239              Err(_bad_offset) => {
240                  eprintln!("write bad offset {offset} value {value}");
241              }
242              Ok(DR) => {
243                  // ??? Check if transmitter is enabled.
244                  let ch: u8 = value as u8;
245                  // XXX this blocks entire thread. Rewrite to use
246                  // qemu_chr_fe_write and background I/O callbacks
247  
248                  // SAFETY: self.char_backend is a valid CharBackend instance after it's been
249                  // initialized in realize().
250                  unsafe {
251                      qemu_chr_fe_write_all(addr_of_mut!(self.char_backend), &ch, 1);
252                  }
253                  self.loopback_tx(value);
254                  self.int_level |= registers::INT_TX;
255                  self.update();
256              }
257              Ok(RSR) => {
258                  self.receive_status_error_clear = 0.into();
259              }
260              Ok(FR) => {
261                  // flag writes are ignored
262              }
263              Ok(ILPR) => {
264                  self.ilpr = value;
265              }
266              Ok(IBRD) => {
267                  self.ibrd = value;
268              }
269              Ok(FBRD) => {
270                  self.fbrd = value;
271              }
272              Ok(LCR_H) => {
273                  let value = value as u16;
274                  let new_val: registers::LineControl = value.into();
275                  // Reset the FIFO state on FIFO enable or disable
276                  if bool::from(self.line_control.fifos_enabled())
277                      ^ bool::from(new_val.fifos_enabled())
278                  {
279                      self.reset_fifo();
280                  }
281                  if self.line_control.send_break() ^ new_val.send_break() {
282                      let mut break_enable: c_int = new_val.send_break().into();
283                      // SAFETY: self.char_backend is a valid CharBackend instance after it's been
284                      // initialized in realize().
285                      unsafe {
286                          qemu_chr_fe_ioctl(
287                              addr_of_mut!(self.char_backend),
288                              CHR_IOCTL_SERIAL_SET_BREAK as i32,
289                              addr_of_mut!(break_enable).cast::<c_void>(),
290                          );
291                      }
292                      self.loopback_break(break_enable > 0);
293                  }
294                  self.line_control = new_val;
295                  self.set_read_trigger();
296              }
297              Ok(CR) => {
298                  // ??? Need to implement the enable bit.
299                  let value = value as u16;
300                  self.control = value.into();
301                  self.loopback_mdmctrl();
302              }
303              Ok(FLS) => {
304                  self.ifl = value;
305                  self.set_read_trigger();
306              }
307              Ok(IMSC) => {
308                  self.int_enabled = value;
309                  self.update();
310              }
311              Ok(RIS) => {}
312              Ok(MIS) => {}
313              Ok(ICR) => {
314                  self.int_level &= !value;
315                  self.update();
316              }
317              Ok(DMACR) => {
318                  self.dmacr = value;
319                  if value & 3 > 0 {
320                      // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n");
321                      eprintln!("pl011: DMA not implemented");
322                  }
323              }
324          }
325      }
326  
327      #[inline]
loopback_tx(&mut self, value: u32)328      fn loopback_tx(&mut self, value: u32) {
329          if !self.loopback_enabled() {
330              return;
331          }
332  
333          // Caveat:
334          //
335          // In real hardware, TX loopback happens at the serial-bit level
336          // and then reassembled by the RX logics back into bytes and placed
337          // into the RX fifo. That is, loopback happens after TX fifo.
338          //
339          // Because the real hardware TX fifo is time-drained at the frame
340          // rate governed by the configured serial format, some loopback
341          // bytes in TX fifo may still be able to get into the RX fifo
342          // that could be full at times while being drained at software
343          // pace.
344          //
345          // In such scenario, the RX draining pace is the major factor
346          // deciding which loopback bytes get into the RX fifo, unless
347          // hardware flow-control is enabled.
348          //
349          // For simplicity, the above described is not emulated.
350          self.put_fifo(value);
351      }
352  
loopback_mdmctrl(&mut self)353      fn loopback_mdmctrl(&mut self) {
354          if !self.loopback_enabled() {
355              return;
356          }
357  
358          /*
359           * Loopback software-driven modem control outputs to modem status inputs:
360           *   FR.RI  <= CR.Out2
361           *   FR.DCD <= CR.Out1
362           *   FR.CTS <= CR.RTS
363           *   FR.DSR <= CR.DTR
364           *
365           * The loopback happens immediately even if this call is triggered
366           * by setting only CR.LBE.
367           *
368           * CTS/RTS updates due to enabled hardware flow controls are not
369           * dealt with here.
370           */
371  
372          self.flags.set_ring_indicator(self.control.out_2());
373          self.flags.set_data_carrier_detect(self.control.out_1());
374          self.flags.set_clear_to_send(self.control.request_to_send());
375          self.flags
376              .set_data_set_ready(self.control.data_transmit_ready());
377  
378          // Change interrupts based on updated FR
379          let mut il = self.int_level;
380  
381          il &= !Interrupt::MS;
382  
383          if self.flags.data_set_ready() {
384              il |= Interrupt::DSR as u32;
385          }
386          if self.flags.data_carrier_detect() {
387              il |= Interrupt::DCD as u32;
388          }
389          if self.flags.clear_to_send() {
390              il |= Interrupt::CTS as u32;
391          }
392          if self.flags.ring_indicator() {
393              il |= Interrupt::RI as u32;
394          }
395          self.int_level = il;
396          self.update();
397      }
398  
loopback_break(&mut self, enable: bool)399      fn loopback_break(&mut self, enable: bool) {
400          if enable {
401              self.loopback_tx(DATA_BREAK);
402          }
403      }
404  
set_read_trigger(&mut self)405      fn set_read_trigger(&mut self) {
406          self.read_trigger = 1;
407      }
408  
realize(&mut self)409      pub fn realize(&mut self) {
410          // SAFETY: self.char_backend has the correct size and alignment for a
411          // CharBackend object, and its callbacks are of the correct types.
412          unsafe {
413              qemu_chr_fe_set_handlers(
414                  addr_of_mut!(self.char_backend),
415                  Some(pl011_can_receive),
416                  Some(pl011_receive),
417                  Some(pl011_event),
418                  None,
419                  addr_of_mut!(*self).cast::<c_void>(),
420                  core::ptr::null_mut(),
421                  true,
422              );
423          }
424      }
425  
reset(&mut self)426      pub fn reset(&mut self) {
427          self.line_control.reset();
428          self.receive_status_error_clear.reset();
429          self.dmacr = 0;
430          self.int_enabled = 0;
431          self.int_level = 0;
432          self.ilpr = 0;
433          self.ibrd = 0;
434          self.fbrd = 0;
435          self.read_trigger = 1;
436          self.ifl = 0x12;
437          self.control.reset();
438          self.flags = 0.into();
439          self.reset_fifo();
440      }
441  
reset_fifo(&mut self)442      pub fn reset_fifo(&mut self) {
443          self.read_count = 0;
444          self.read_pos = 0;
445  
446          /* Reset FIFO flags */
447          self.flags.reset();
448      }
449  
can_receive(&self) -> bool450      pub fn can_receive(&self) -> bool {
451          // trace_pl011_can_receive(s->lcr, s->read_count, r);
452          self.read_count < self.fifo_depth()
453      }
454  
event(&mut self, event: QEMUChrEvent)455      pub fn event(&mut self, event: QEMUChrEvent) {
456          if event == bindings::QEMUChrEvent::CHR_EVENT_BREAK && !self.fifo_enabled() {
457              self.put_fifo(DATA_BREAK);
458              self.receive_status_error_clear.set_break_error(true);
459          }
460      }
461  
462      #[inline]
fifo_enabled(&self) -> bool463      pub fn fifo_enabled(&self) -> bool {
464          matches!(self.line_control.fifos_enabled(), registers::Mode::FIFO)
465      }
466  
467      #[inline]
loopback_enabled(&self) -> bool468      pub fn loopback_enabled(&self) -> bool {
469          self.control.enable_loopback()
470      }
471  
472      #[inline]
fifo_depth(&self) -> usize473      pub fn fifo_depth(&self) -> usize {
474          // Note: FIFO depth is expected to be power-of-2
475          if self.fifo_enabled() {
476              return PL011_FIFO_DEPTH;
477          }
478          1
479      }
480  
put_fifo(&mut self, value: c_uint)481      pub fn put_fifo(&mut self, value: c_uint) {
482          let depth = self.fifo_depth();
483          assert!(depth > 0);
484          let slot = (self.read_pos + self.read_count) & (depth - 1);
485          self.read_fifo[slot] = value;
486          self.read_count += 1;
487          self.flags.set_receive_fifo_empty(false);
488          if self.read_count == depth {
489              self.flags.set_receive_fifo_full(true);
490          }
491  
492          if self.read_count == self.read_trigger {
493              self.int_level |= registers::INT_RX;
494              self.update();
495          }
496      }
497  
update(&self)498      pub fn update(&self) {
499          let flags = self.int_level & self.int_enabled;
500          for (irq, i) in self.interrupts.iter().zip(IRQMASK) {
501              // SAFETY: self.interrupts have been initialized in init().
502              unsafe { qemu_set_irq(*irq, i32::from(flags & i != 0)) };
503          }
504      }
505  
post_load(&mut self, _version_id: u32) -> Result<(), ()>506      pub fn post_load(&mut self, _version_id: u32) -> Result<(), ()> {
507          /* Sanity-check input state */
508          if self.read_pos >= self.read_fifo.len() || self.read_count > self.read_fifo.len() {
509              return Err(());
510          }
511  
512          if !self.fifo_enabled() && self.read_count > 0 && self.read_pos > 0 {
513              // Older versions of PL011 didn't ensure that the single
514              // character in the FIFO in FIFO-disabled mode is in
515              // element 0 of the array; convert to follow the current
516              // code's assumptions.
517              self.read_fifo[0] = self.read_fifo[self.read_pos];
518              self.read_pos = 0;
519          }
520  
521          self.ibrd &= IBRD_MASK;
522          self.fbrd &= FBRD_MASK;
523  
524          Ok(())
525      }
526  }
527  
528  /// Which bits in the interrupt status matter for each outbound IRQ line ?
529  pub const IRQMASK: [u32; 6] = [
530      /* combined IRQ */
531      Interrupt::E
532          | Interrupt::MS
533          | Interrupt::RT as u32
534          | Interrupt::TX as u32
535          | Interrupt::RX as u32,
536      Interrupt::RX as u32,
537      Interrupt::TX as u32,
538      Interrupt::RT as u32,
539      Interrupt::MS,
540      Interrupt::E,
541  ];
542  
543  /// # Safety
544  ///
545  /// We expect the FFI user of this function to pass a valid pointer, that has
546  /// the same size as [`PL011State`]. We also expect the device is
547  /// readable/writeable from one thread at any time.
pl011_can_receive(opaque: *mut c_void) -> c_int548  pub unsafe extern "C" fn pl011_can_receive(opaque: *mut c_void) -> c_int {
549      unsafe {
550          debug_assert!(!opaque.is_null());
551          let state = NonNull::new_unchecked(opaque.cast::<PL011State>());
552          state.as_ref().can_receive().into()
553      }
554  }
555  
556  /// # Safety
557  ///
558  /// We expect the FFI user of this function to pass a valid pointer, that has
559  /// the same size as [`PL011State`]. We also expect the device is
560  /// readable/writeable from one thread at any time.
561  ///
562  /// The buffer and size arguments must also be valid.
pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int)563  pub unsafe extern "C" fn pl011_receive(opaque: *mut c_void, buf: *const u8, size: c_int) {
564      unsafe {
565          debug_assert!(!opaque.is_null());
566          let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
567          if state.as_ref().loopback_enabled() {
568              return;
569          }
570          if size > 0 {
571              debug_assert!(!buf.is_null());
572              state.as_mut().put_fifo(c_uint::from(buf.read_volatile()))
573          }
574      }
575  }
576  
577  /// # Safety
578  ///
579  /// We expect the FFI user of this function to pass a valid pointer, that has
580  /// the same size as [`PL011State`]. We also expect the device is
581  /// readable/writeable from one thread at any time.
pl011_event(opaque: *mut c_void, event: QEMUChrEvent)582  pub unsafe extern "C" fn pl011_event(opaque: *mut c_void, event: QEMUChrEvent) {
583      unsafe {
584          debug_assert!(!opaque.is_null());
585          let mut state = NonNull::new_unchecked(opaque.cast::<PL011State>());
586          state.as_mut().event(event)
587      }
588  }
589  
590  /// # Safety
591  ///
592  /// We expect the FFI user of this function to pass a valid pointer for `chr`.
593  #[no_mangle]
pl011_create( addr: u64, irq: qemu_irq, chr: *mut Chardev, ) -> *mut DeviceState594  pub unsafe extern "C" fn pl011_create(
595      addr: u64,
596      irq: qemu_irq,
597      chr: *mut Chardev,
598  ) -> *mut DeviceState {
599      unsafe {
600          let dev: *mut DeviceState = qdev_new(PL011State::TYPE_INFO.name);
601          let sysbus: *mut SysBusDevice = dev.cast::<SysBusDevice>();
602  
603          qdev_prop_set_chr(dev, c_str!("chardev").as_ptr(), chr);
604          sysbus_realize_and_unref(sysbus, addr_of!(error_fatal) as *mut *mut Error);
605          sysbus_mmio_map(sysbus, 0, addr);
606          sysbus_connect_irq(sysbus, 0, irq);
607          dev
608      }
609  }
610  
611  /// # Safety
612  ///
613  /// We expect the FFI user of this function to pass a valid pointer, that has
614  /// the same size as [`PL011State`]. We also expect the device is
615  /// readable/writeable from one thread at any time.
pl011_init(obj: *mut Object)616  pub unsafe extern "C" fn pl011_init(obj: *mut Object) {
617      unsafe {
618          debug_assert!(!obj.is_null());
619          let mut state = NonNull::new_unchecked(obj.cast::<PL011State>());
620          state.as_mut().init();
621      }
622  }
623  
624  #[repr(C)]
625  #[derive(Debug, qemu_api_macros::Object)]
626  /// PL011 Luminary device model.
627  pub struct PL011Luminary {
628      parent_obj: PL011State,
629  }
630  
631  #[repr(C)]
632  pub struct PL011LuminaryClass {
633      _inner: [u8; 0],
634  }
635  
636  /// Initializes a pre-allocated, unitialized instance of `PL011Luminary`.
637  ///
638  /// # Safety
639  ///
640  /// We expect the FFI user of this function to pass a valid pointer, that has
641  /// the same size as [`PL011Luminary`]. We also expect the device is
642  /// readable/writeable from one thread at any time.
pl011_luminary_init(obj: *mut Object)643  pub unsafe extern "C" fn pl011_luminary_init(obj: *mut Object) {
644      unsafe {
645          debug_assert!(!obj.is_null());
646          let mut state = NonNull::new_unchecked(obj.cast::<PL011Luminary>());
647          let state = state.as_mut();
648          state.parent_obj.device_id = DeviceId::Luminary;
649      }
650  }
651  
652  impl qemu_api::definitions::Class for PL011LuminaryClass {
653      const CLASS_INIT: Option<unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void)> =
654          None;
655      const CLASS_BASE_INIT: Option<
656          unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void),
657      > = None;
658  }
659  
660  impl ObjectImpl for PL011Luminary {
661      type Class = PL011LuminaryClass;
662      const TYPE_INFO: qemu_api::bindings::TypeInfo = qemu_api::type_info! { Self };
663      const TYPE_NAME: &'static CStr = crate::TYPE_PL011_LUMINARY;
664      const PARENT_TYPE_NAME: Option<&'static CStr> = Some(crate::TYPE_PL011);
665      const ABSTRACT: bool = false;
666      const INSTANCE_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = Some(pl011_luminary_init);
667      const INSTANCE_POST_INIT: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
668      const INSTANCE_FINALIZE: Option<unsafe extern "C" fn(obj: *mut Object)> = None;
669  }
670