refactor: 📦 Update Mailbox code
This commit is contained in:
parent
7f4e9de6d5
commit
e5a3ea6998
|
@ -1,107 +1,70 @@
|
|||
use {
|
||||
super::{
|
||||
mailbox::{channel, read, write, MailboxOps, RegisterBlock, Result},
|
||||
BcmHost,
|
||||
},
|
||||
core::ops::Deref,
|
||||
};
|
||||
use super::mailbox::{self, LocalMailboxStorage, Mailbox, MailboxError, MailboxOps};
|
||||
|
||||
/// FrameBuffer channel supported structure - use with mailbox::channel::FrameBuffer
|
||||
/// Must have the same alignment as the mailbox buffers.
|
||||
#[repr(C)]
|
||||
#[repr(align(16))]
|
||||
type FrameBufferData = LocalMailboxStorage<10>;
|
||||
|
||||
mod index {
|
||||
pub const WIDTH: usize = 0;
|
||||
pub const HEIGHT: usize = 1;
|
||||
pub const VIRTUAL_WIDTH: usize = 2;
|
||||
pub const VIRTUAL_HEIGHT: usize = 3;
|
||||
pub const PITCH: usize = 4;
|
||||
pub const DEPTH: usize = 5;
|
||||
pub const X_OFFSET: usize = 6;
|
||||
pub const Y_OFFSET: usize = 7;
|
||||
pub const POINTER: usize = 8; // FIXME: could be 4096 for the alignment restriction.
|
||||
pub const SIZE: usize = 9;
|
||||
}
|
||||
|
||||
pub struct FrameBuffer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub vwidth: u32,
|
||||
pub vheight: u32,
|
||||
pub pitch: u32,
|
||||
pub depth: u32,
|
||||
pub x_offset: u32,
|
||||
pub y_offset: u32,
|
||||
pub pointer: u32,
|
||||
pub size: u32,
|
||||
// Must be after HW-dictated fields to not break structure alignment.
|
||||
base_addr: usize,
|
||||
mailbox: Mailbox<10, FrameBufferData>,
|
||||
}
|
||||
|
||||
// @todo rewrite in terms of using the Mailbox
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.STATUS.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*FrameBuffer::ptr()).STATUS.read() }
|
||||
/// ```
|
||||
impl Deref for FrameBuffer {
|
||||
type Target = RegisterBlock; // mailbox RegisterBlock reused here
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for FrameBuffer {
|
||||
impl core::fmt::Debug for FrameBufferData {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"\n\n\n#### FrameBuffer({}x{}, {}x{}, d{}, --{}--, +{}x{}, {}@{:x})\n\n\n",
|
||||
self.width,
|
||||
self.height,
|
||||
self.vwidth,
|
||||
self.vheight,
|
||||
self.depth,
|
||||
self.pitch,
|
||||
self.x_offset,
|
||||
self.y_offset,
|
||||
self.size,
|
||||
self.pointer,
|
||||
self.storage[index::WIDTH],
|
||||
self.storage[index::HEIGHT],
|
||||
self.storage[index::VIRTUAL_WIDTH],
|
||||
self.storage[index::VIRTUAL_HEIGHT],
|
||||
self.storage[index::HEIGHT],
|
||||
self.storage[index::PITCH],
|
||||
self.storage[index::X_OFFSET],
|
||||
self.storage[index::Y_OFFSET],
|
||||
self.storage[index::SIZE],
|
||||
self.storage[index::POINTER],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameBuffer {
|
||||
pub fn new(reg_base: usize, width: u32, height: u32, depth: u32) -> FrameBuffer {
|
||||
FrameBuffer {
|
||||
width,
|
||||
height,
|
||||
vwidth: width,
|
||||
vheight: height,
|
||||
pitch: 0,
|
||||
depth,
|
||||
x_offset: 0,
|
||||
y_offset: 0,
|
||||
pointer: 0, // could be 4096 for the alignment?
|
||||
size: 0,
|
||||
|
||||
base_addr: reg_base,
|
||||
}
|
||||
pub fn new(
|
||||
base_addr: usize,
|
||||
width: u32,
|
||||
height: u32,
|
||||
depth: u32,
|
||||
) -> Result<FrameBuffer, MailboxError> {
|
||||
let mut fb = FrameBuffer {
|
||||
mailbox: unsafe { Mailbox::<10, FrameBufferData>::new(base_addr)? },
|
||||
};
|
||||
fb.mailbox.buffer.storage[index::WIDTH] = width;
|
||||
fb.mailbox.buffer.storage[index::VIRTUAL_WIDTH] = width;
|
||||
fb.mailbox.buffer.storage[index::HEIGHT] = height;
|
||||
fb.mailbox.buffer.storage[index::VIRTUAL_HEIGHT] = height;
|
||||
fb.mailbox.buffer.storage[index::DEPTH] = depth;
|
||||
Ok(fb)
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxOps for FrameBuffer {
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.base_addr as *const _
|
||||
fn write(&self, _channel: u32) -> mailbox::Result<()> {
|
||||
self.mailbox.do_write(mailbox::channel::FrameBuffer)
|
||||
}
|
||||
|
||||
/// <https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes> says:
|
||||
/// **With the exception of the property tags mailbox channel,**
|
||||
/// when passing memory addresses as the data part of a mailbox message,
|
||||
/// the addresses should be **bus addresses as seen from the VC.**
|
||||
fn write(&self, _channel: u32) -> Result<()> {
|
||||
write(
|
||||
self,
|
||||
BcmHost::phys2bus(&self as *const _ as usize) as *const _,
|
||||
channel::FrameBuffer,
|
||||
)
|
||||
}
|
||||
|
||||
fn read(&self, _channel: u32) -> Result<()> {
|
||||
read(self, 0, channel::FrameBuffer)
|
||||
fn read(&self, _channel: u32) -> mailbox::Result<()> {
|
||||
unsafe { self.mailbox.do_read(mailbox::channel::FrameBuffer, 0) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,16 +13,16 @@
|
|||
|
||||
use {
|
||||
super::BcmHost,
|
||||
crate::println,
|
||||
crate::{platform::MMIODerefWrapper, println},
|
||||
core::{
|
||||
ops::Deref,
|
||||
ptr::NonNull,
|
||||
result::Result as CoreResult,
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
},
|
||||
cortex_a::asm::barrier,
|
||||
snafu::Snafu,
|
||||
tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_bitfields,
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, WriteOnly},
|
||||
},
|
||||
};
|
||||
|
@ -31,15 +31,16 @@ use {
|
|||
/// The address for the buffer needs to be 16-byte aligned
|
||||
/// so that the VideoCore can handle it properly.
|
||||
/// The reason is that lowest 4 bits of the address will contain the channel number.
|
||||
pub struct Mailbox {
|
||||
// pub buffer: &'a mut [u32],
|
||||
base_addr: usize,
|
||||
buffer: NonNull<[u32]>,
|
||||
pub struct Mailbox<const N_SLOTS: usize, Storage = LocalMailboxStorage<N_SLOTS>> {
|
||||
registers: Registers,
|
||||
pub buffer: Storage,
|
||||
}
|
||||
|
||||
/// Mailbox that is ready to be called.
|
||||
/// This prevents invalid use of the mailbox until it is fully prepared.
|
||||
pub struct PreparedMailbox(Mailbox);
|
||||
pub struct PreparedMailbox<const N_SLOTS: usize, Storage = LocalMailboxStorage<N_SLOTS>>(
|
||||
Mailbox<N_SLOTS, Storage>,
|
||||
);
|
||||
|
||||
const MAILBOX_ALIGNMENT: usize = 16;
|
||||
const MAILBOX_ITEMS_COUNT: usize = 36;
|
||||
|
@ -57,6 +58,9 @@ const CHANNEL_MASK: u32 = 0xf;
|
|||
// always for communication from VC to ARM and Mailbox 1 is for ARM to VC.
|
||||
//
|
||||
// The ARM should never write Mailbox 0 or read Mailbox 1.
|
||||
//
|
||||
// There are 32 mailboxes on the ARM, which could be used for in-processor or inter-processor comms,
|
||||
// TODO: allow using all of them.
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
|
@ -69,42 +73,35 @@ register_bitfields! {
|
|||
]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
READ: ReadOnly<u32>, // 0x00 This is Mailbox0 read for ARM, can't write
|
||||
__reserved_0: [u32; 5], // 0x04
|
||||
STATUS: ReadOnly<u32, STATUS::Register>, // 0x18
|
||||
__reserved_1: u32, // 0x1C
|
||||
WRITE: WriteOnly<u32>, // 0x20 This is Mailbox1 write for ARM, can't read
|
||||
}
|
||||
|
||||
pub enum MailboxError {
|
||||
Response,
|
||||
Unknown,
|
||||
Timeout,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for MailboxError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
MailboxError::Response => "ResponseError",
|
||||
MailboxError::Unknown => "UnknownError",
|
||||
MailboxError::Timeout => "Timeout",
|
||||
}
|
||||
)
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
pub RegisterBlock {
|
||||
(0x00 => READ: ReadOnly<u32>), // This is Mailbox0 read for ARM, can't write
|
||||
(0x04 => __reserved_1),
|
||||
(0x18 => STATUS: ReadOnly<u32, STATUS::Register>),
|
||||
(0x1c => __reserved_2),
|
||||
(0x20 => WRITE: WriteOnly<u32>), // This is Mailbox1 write for ARM, can't read
|
||||
(0x24 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = ::core::result::Result<T, MailboxError>;
|
||||
// Hide RegisterBlock from public api.
|
||||
type Registers = MMIODerefWrapper<RegisterBlock>;
|
||||
|
||||
#[derive(Snafu, Debug)]
|
||||
pub enum MailboxError {
|
||||
#[snafu(display("ResponseError"))]
|
||||
Response,
|
||||
#[snafu(display("UnknownError"))]
|
||||
Unknown,
|
||||
#[snafu(display("Timeout"))]
|
||||
Timeout,
|
||||
}
|
||||
|
||||
pub type Result<T> = CoreResult<T, MailboxError>;
|
||||
|
||||
/// Typical operations with a mailbox.
|
||||
pub trait MailboxOps {
|
||||
/// Deref from self to a mailbox RegisterBlock. Used by Deref implementations.
|
||||
fn ptr(&self) -> *const RegisterBlock;
|
||||
fn write(&self, channel: u32) -> Result<()>;
|
||||
fn read(&self, channel: u32) -> Result<()>;
|
||||
fn call(&self, channel: u32) -> Result<()> {
|
||||
|
@ -113,6 +110,50 @@ pub trait MailboxOps {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait MailboxStorage {
|
||||
fn new() -> Self;
|
||||
}
|
||||
|
||||
pub trait MailboxStorageRef {
|
||||
fn as_ref(&self) -> &[u32];
|
||||
fn as_mut(&mut self) -> &mut [u32];
|
||||
fn as_ptr(&self) -> *const u32;
|
||||
fn value_at(&self, index: usize) -> u32;
|
||||
}
|
||||
|
||||
// TODO: allow from 2 to 36 slots (2 because you need at least an End tag)
|
||||
#[repr(align(16))] // MAILBOX_ALIGNMENT
|
||||
pub struct LocalMailboxStorage<const N_SLOTS: usize> {
|
||||
pub storage: [u32; N_SLOTS],
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize> MailboxStorage for LocalMailboxStorage<N_SLOTS> {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
storage: [0u32; N_SLOTS],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize> MailboxStorageRef for LocalMailboxStorage<N_SLOTS> {
|
||||
fn as_ref(&self) -> &[u32] {
|
||||
&self.storage
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> &mut [u32] {
|
||||
&mut self.storage
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const u32 {
|
||||
self.storage.as_ptr()
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
fn value_at(&self, index: usize) -> u32 {
|
||||
self.storage[index]
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Source https://elinux.org/RPi_Framebuffer
|
||||
* Source for channels 8 and 9: https://github.com/raspberrypi/firmware/wiki/Mailboxes
|
||||
|
@ -243,144 +284,39 @@ pub mod alpha_mode {
|
|||
pub const IGNORED: u32 = 2;
|
||||
}
|
||||
|
||||
pub fn write(regs: &RegisterBlock, buf: *const u32, channel: u32) -> Result<()> {
|
||||
let mut count: u32 = 0;
|
||||
let buf_ptr: u32 = buf as u32;
|
||||
|
||||
// This address adjustment will be performed from the outside when necessary
|
||||
// (see FrameBuffer for example).
|
||||
// let buf_ptr = BcmHost::phys2bus(buf_ptr); not used for PropertyTags channel
|
||||
|
||||
println!("Mailbox::write {:#08x}/{:#x}", buf_ptr, channel);
|
||||
|
||||
// Insert a compiler fence that ensures that all stores to the
|
||||
// mailbox buffer are finished before the GPU is signaled (which is
|
||||
// done by a store operation as well).
|
||||
compiler_fence(Ordering::Release);
|
||||
|
||||
while regs.STATUS.is_set(STATUS::FULL) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
regs.WRITE
|
||||
.set((buf_ptr & !CHANNEL_MASK) | (channel & CHANNEL_MASK));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read(regs: &RegisterBlock, expected: u32, channel: u32) -> Result<()> {
|
||||
loop {
|
||||
let mut count: u32 = 0;
|
||||
while regs.STATUS.is_set(STATUS::EMPTY) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
println!("Timed out waiting for mailbox response");
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data
|
||||
* Data memory barriers as we've switched peripheral
|
||||
*/
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
let data: u32 = regs.READ.get();
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
|
||||
println!(
|
||||
"Received mailbox response {:#08x}, expecting {:#08x}",
|
||||
data, expected
|
||||
);
|
||||
|
||||
// is it a response to our message?
|
||||
if ((data & CHANNEL_MASK) == channel) && ((data & !CHANNEL_MASK) == expected) {
|
||||
// is it a valid successful response?
|
||||
return Ok(());
|
||||
} else {
|
||||
// ignore invalid responses and loop again.
|
||||
// will return Timeout above if no matching response is received.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deref to RegisterBlock
|
||||
///
|
||||
/// Allows writing
|
||||
/// ```
|
||||
/// self.STATUS.read()
|
||||
/// ```
|
||||
/// instead of something along the lines of
|
||||
/// ```
|
||||
/// unsafe { (*Mailbox::ptr()).STATUS.read() }
|
||||
/// ```
|
||||
impl Deref for PreparedMailbox {
|
||||
type Target = RegisterBlock;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for Mailbox {
|
||||
impl<const N_SLOTS: usize> core::fmt::Debug for Mailbox<N_SLOTS> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let count = unsafe { self.buffer.as_ref()[0] } / 4;
|
||||
assert_eq!(unsafe { self.buffer.as_ref()[0] }, count * 4);
|
||||
let count = self.buffer.as_ref()[0] / 4;
|
||||
assert_eq!(self.buffer.as_ref()[0], count * 4);
|
||||
assert!(count <= 36);
|
||||
for i in 0usize..count as usize {
|
||||
writeln!(f, "[{:02}] {:08x}", i, unsafe { self.buffer.as_ref()[i] })?;
|
||||
writeln!(f, "[{:02}] {:08x}", i, self.buffer.value_at(i))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for PreparedMailbox {
|
||||
impl<const N_SLOTS: usize> core::fmt::Debug for PreparedMailbox<N_SLOTS> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mailbox {
|
||||
impl<const N_SLOTS: usize> Default for Mailbox<N_SLOTS> {
|
||||
fn default() -> Self {
|
||||
Self::new(MAILBOX_BASE).expect("Couldn't allocate a default mailbox")
|
||||
unsafe { Self::new(MAILBOX_BASE) }.expect("Couldn't allocate a default mailbox")
|
||||
}
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
impl PreparedMailbox {
|
||||
pub fn value_at(&self, index: usize) -> u32 {
|
||||
unsafe { self.0.buffer.as_ref()[index] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
/// Create a new mailbox in the DMA-able memory area.
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn new(base_addr: usize) -> ::core::result::Result<Mailbox, ()> {
|
||||
use core::alloc::Allocator;
|
||||
crate::DMA_ALLOCATOR
|
||||
.lock(|dma| {
|
||||
dma.allocate_zeroed(
|
||||
core::alloc::Layout::from_size_align(
|
||||
MAILBOX_ITEMS_COUNT * core::mem::size_of::<u32>(),
|
||||
MAILBOX_ALIGNMENT,
|
||||
)
|
||||
.unwrap(), // .map_err(|_| ())?,
|
||||
)
|
||||
})
|
||||
.map(|ret| {
|
||||
Ok(Mailbox {
|
||||
base_addr,
|
||||
buffer: ret.cast::<[u32; MAILBOX_ITEMS_COUNT]>(),
|
||||
})
|
||||
})
|
||||
.map_err(|_| ())?
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> Mailbox<N_SLOTS, Storage> {
|
||||
/// Create a new mailbox locally in an aligned stack space.
|
||||
/// # Safety
|
||||
/// Caller is responsible for picking the correct MMIO register base address.
|
||||
pub unsafe fn new(base_addr: usize) -> Result<Mailbox<N_SLOTS, Storage>> {
|
||||
Ok(Mailbox {
|
||||
registers: Registers::new(base_addr),
|
||||
buffer: Storage::new(),
|
||||
})
|
||||
}
|
||||
|
||||
// Specific mailbox functions
|
||||
|
@ -390,19 +326,17 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn request(&mut self) -> usize {
|
||||
unsafe { self.buffer.as_mut()[1] = REQUEST };
|
||||
self.buffer.as_mut()[1] = REQUEST;
|
||||
2
|
||||
}
|
||||
|
||||
/// Mark mailbox payload as completed.
|
||||
/// Consumes the Mailbox and returns a Preparedmailbox that can be called.
|
||||
#[inline]
|
||||
pub fn end(mut self, index: usize) -> PreparedMailbox {
|
||||
pub fn end(mut self, index: usize) -> PreparedMailbox<N_SLOTS, Storage> {
|
||||
// @todo return Result
|
||||
unsafe {
|
||||
self.buffer.as_mut()[index] = tag::End;
|
||||
self.buffer.as_mut()[0] = (index as u32 + 1) * 4;
|
||||
}
|
||||
self.buffer.as_mut()[index] = tag::End;
|
||||
self.buffer.as_mut()[0] = (index as u32 + 1) * 4;
|
||||
PreparedMailbox(self)
|
||||
}
|
||||
|
||||
|
@ -410,7 +344,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_physical_wh(&mut self, index: usize, width: u32, height: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPhysicalWH;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Request size // val size
|
||||
|
@ -423,7 +357,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_virtual_wh(&mut self, index: usize, width: u32, height: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetVirtualWH;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Request size // val size
|
||||
|
@ -436,7 +370,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_depth(&mut self, index: usize, depth: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetDepth;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Request size // val size
|
||||
|
@ -448,7 +382,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn allocate_buffer_aligned(&mut self, index: usize, alignment: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::AllocateBuffer;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Request size // val size
|
||||
|
@ -461,7 +395,7 @@ impl Mailbox {
|
|||
/// @returns index of the next available slot.
|
||||
#[inline]
|
||||
pub fn set_led_on(&mut self, index: usize, enable: bool) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetGpioState;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 0; // Response size // val size
|
||||
|
@ -472,7 +406,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_clock_rate(&mut self, index: usize, channel: u32, rate: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetClockRate;
|
||||
buf[index + 1] = 12; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Response size // val size
|
||||
|
@ -488,7 +422,7 @@ impl Mailbox {
|
|||
/// and no tags will be returned.
|
||||
#[inline]
|
||||
pub fn set_pixel_order(&mut self, index: usize, order: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPixelOrder;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -502,7 +436,7 @@ impl Mailbox {
|
|||
/// and no tags will be returned.
|
||||
#[inline]
|
||||
pub fn test_pixel_order(&mut self, index: usize, order: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::TestPixelOrder;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -512,7 +446,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_alpha_mode(&mut self, index: usize, mode: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetAlphaMode;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -522,7 +456,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn get_pitch(&mut self, index: usize) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::GetPitch;
|
||||
buf[index + 1] = 4; // Buffer size // val buf size
|
||||
buf[index + 2] = 4; // Response size // val size
|
||||
|
@ -532,7 +466,7 @@ impl Mailbox {
|
|||
|
||||
#[inline]
|
||||
pub fn set_device_power(&mut self, index: usize, device_id: u32, power_flags: u32) -> usize {
|
||||
let buf = unsafe { self.buffer.as_mut() };
|
||||
let buf = self.buffer.as_mut();
|
||||
buf[index] = tag::SetPowerState;
|
||||
buf[index + 1] = 8; // Buffer size // val buf size
|
||||
buf[index + 2] = 8; // Response size // val size
|
||||
|
@ -540,42 +474,130 @@ impl Mailbox {
|
|||
buf[index + 4] = power_flags; // bit 0: off, bit 1: no wait
|
||||
index + 5
|
||||
}
|
||||
}
|
||||
|
||||
impl MailboxOps for PreparedMailbox {
|
||||
/// Returns a pointer to the register block
|
||||
fn ptr(&self) -> *const RegisterBlock {
|
||||
self.0.base_addr as *const _
|
||||
}
|
||||
// Actual work functions
|
||||
|
||||
fn write(&self, channel: u32) -> Result<()> {
|
||||
write(self, self.0.buffer.as_ptr() as *const _, channel)
|
||||
}
|
||||
/// <https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes> says:
|
||||
/// **With the exception of the property tags mailbox channel,**
|
||||
/// when passing memory addresses as the data part of a mailbox message,
|
||||
/// the addresses should be **bus addresses as seen from the VC.**
|
||||
pub fn do_write(&self, channel: u32) -> Result<()> {
|
||||
let buf_ptr = self.buffer.as_ptr() as *const u32 as u32;
|
||||
let buf_ptr = if channel != channel::PropertyTagsArmToVc {
|
||||
BcmHost::phys2bus(buf_ptr as usize) as u32
|
||||
} else {
|
||||
buf_ptr
|
||||
};
|
||||
|
||||
// @todo read() should probably consume PreparedMailbox completely ?
|
||||
fn read(&self, channel: u32) -> Result<()> {
|
||||
// SAFETY: buffer is HW-mutable in the read call below!
|
||||
read(
|
||||
self,
|
||||
self.0.buffer.as_ptr() as *const [u32] as *const u32 as u32,
|
||||
channel,
|
||||
)?;
|
||||
let mut count: u32 = 0;
|
||||
|
||||
match unsafe { self.0.buffer.as_ref()[1] } {
|
||||
response::SUCCESS => {
|
||||
println!("\n######\nMailbox::returning SUCCESS");
|
||||
Ok(())
|
||||
}
|
||||
response::ERROR => {
|
||||
println!("\n######\nMailbox::returning ResponseError");
|
||||
Err(MailboxError::Response)
|
||||
}
|
||||
_ => {
|
||||
println!("\n######\nMailbox::returning UnknownError");
|
||||
println!("{:x}\n######", unsafe { self.0.buffer.as_ref()[1] });
|
||||
Err(MailboxError::Unknown)
|
||||
println!("Mailbox::write {:#08x}/{:#x}", buf_ptr, channel);
|
||||
|
||||
// Insert a compiler fence that ensures that all stores to the
|
||||
// mailbox buffer are finished before the GPU is signaled (which is
|
||||
// done by a store operation as well).
|
||||
compiler_fence(Ordering::Release);
|
||||
|
||||
while self.registers.STATUS.is_set(STATUS::FULL) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
barrier::dmb(barrier::SY);
|
||||
}
|
||||
self.registers
|
||||
.WRITE
|
||||
.set((buf_ptr & !CHANNEL_MASK) | (channel & CHANNEL_MASK));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform the mailbox read.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Buffer will be mutated by the hardware before read operation is completed.
|
||||
pub unsafe fn do_read(&self, channel: u32, expected: u32) -> Result<()> {
|
||||
loop {
|
||||
let mut count: u32 = 0;
|
||||
while self.registers.STATUS.is_set(STATUS::EMPTY) {
|
||||
count += 1;
|
||||
if count > (1 << 25) {
|
||||
println!("Timed out waiting for mailbox response");
|
||||
return Err(MailboxError::Timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data
|
||||
* Data memory barriers as we've switched peripheral
|
||||
*/
|
||||
barrier::dmb(barrier::SY);
|
||||
let data: u32 = self.registers.READ.get();
|
||||
barrier::dmb(barrier::SY);
|
||||
|
||||
println!(
|
||||
"Received mailbox response {:#08x}, expecting {:#08x}",
|
||||
data, expected
|
||||
);
|
||||
|
||||
// is it a response to our message?
|
||||
if ((data & CHANNEL_MASK) == channel) && ((data & !CHANNEL_MASK) == expected) {
|
||||
// is it a valid successful response?
|
||||
return match self.buffer.value_at(1) {
|
||||
response::SUCCESS => {
|
||||
println!("\n######\nMailbox::returning SUCCESS");
|
||||
Ok(())
|
||||
}
|
||||
response::ERROR => {
|
||||
println!("\n######\nMailbox::returning ResponseError");
|
||||
Err(MailboxError::Response)
|
||||
}
|
||||
_ => {
|
||||
println!("\n######\nMailbox::returning UnknownError");
|
||||
println!("{:x}\n######", self.buffer.value_at(1));
|
||||
Err(MailboxError::Unknown)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// ignore invalid responses and loop again.
|
||||
// will return Timeout above if no matching response is received.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> MailboxOps
|
||||
for PreparedMailbox<N_SLOTS, Storage>
|
||||
{
|
||||
fn write(&self, channel: u32) -> Result<()> {
|
||||
self.0.do_write(channel)
|
||||
}
|
||||
|
||||
// @todo read() should probably consume PreparedMailbox completely - because request is overwritten with response
|
||||
fn read(&self, channel: u32) -> Result<()> {
|
||||
unsafe { self.0.do_read(channel, self.0.buffer.as_ptr() as u32) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N_SLOTS: usize, Storage: MailboxStorage + MailboxStorageRef> MailboxStorageRef
|
||||
for PreparedMailbox<N_SLOTS, Storage>
|
||||
{
|
||||
fn as_ref(&self) -> &[u32] {
|
||||
self.0.buffer.as_ref()
|
||||
}
|
||||
|
||||
fn as_mut(&mut self) -> &mut [u32] {
|
||||
self.0.buffer.as_mut()
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const u32 {
|
||||
self.0.buffer.as_ptr()
|
||||
}
|
||||
|
||||
// @todo Probably need a ResultMailbox for accessing data after call()?
|
||||
fn value_at(&self, index: usize) -> u32 {
|
||||
self.0.buffer.value_at(index)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ impl Power {
|
|||
pub fn off(&self, gpio: &gpio::GPIO) -> Result<()> {
|
||||
// power off devices one by one
|
||||
for dev_id in 0..16 {
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<8>::default();
|
||||
let index = mbox.request();
|
||||
let index =
|
||||
mbox.set_device_power(index, dev_id, POWER_STATE_OFF | POWER_STATE_DO_NOT_WAIT);
|
||||
|
|
|
@ -8,7 +8,7 @@ use {
|
|||
mailbox::{self, channel, response::VAL_LEN_FLAG, Mailbox, MailboxOps},
|
||||
BcmHost,
|
||||
},
|
||||
crate::println,
|
||||
crate::{platform::rpi3::mailbox::MailboxStorageRef, println},
|
||||
core::convert::TryInto,
|
||||
snafu::Snafu,
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ impl VC {
|
|||
* (if the base or size has changed) is implicitly freed.
|
||||
*/
|
||||
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<36>::default();
|
||||
let index = mbox.request();
|
||||
let index = mbox.set_physical_wh(index, w, h);
|
||||
let index = mbox.set_virtual_wh(index, w, h);
|
||||
|
@ -67,7 +67,7 @@ impl VC {
|
|||
|
||||
// SetPixelOrder doesn't work in QEMU, however TestPixelOrder does.
|
||||
// Apparently, QEMU doesn't care about intermixing Get/Set and Test tags either.
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<36>::default();
|
||||
let index = mbox.request();
|
||||
#[cfg(qemu)]
|
||||
let index = mbox.test_pixel_order(index, 1);
|
||||
|
|
|
@ -171,7 +171,7 @@ fn print_help() {
|
|||
}
|
||||
|
||||
fn set_led(enable: bool) {
|
||||
let mut mbox = Mailbox::default();
|
||||
let mut mbox = Mailbox::<8>::default();
|
||||
let index = mbox.request();
|
||||
let index = mbox.set_led_on(index, enable);
|
||||
let mbox = mbox.end(index);
|
||||
|
|
Loading…
Reference in New Issue