Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions psp/src/dialog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,21 @@ fn make_message_buf(message: &str) -> [u8; 512] {
const MAX_DIALOG_ITERATIONS: u32 = 600;

/// Small display list for utility dialog GU frames (16KB, 16-byte aligned).
/// Only accessed from the main thread, never concurrently.
#[repr(C, align(16))]
pub(crate) struct Align16<T>(pub T);
/// Shared display list for all utility dialog loops. Only accessed from
/// the main thread, never concurrently.
pub(crate) static mut DIALOG_LIST: Align16<[u8; 0x4000]> = Align16([0u8; 0x4000]);
pub(crate) struct DialogListBuf(core::cell::UnsafeCell<[u8; 0x4000]>);

// SAFETY: Only accessed from the main thread during dialog polling loops.
unsafe impl Sync for DialogListBuf {}

impl DialogListBuf {
pub(crate) fn as_mut_ptr(&self) -> *mut core::ffi::c_void {
self.0.get() as *mut core::ffi::c_void
}
}

pub(crate) static DIALOG_LIST: DialogListBuf =
DialogListBuf(core::cell::UnsafeCell::new([0u8; 0x4000]));

/// Create a `UtilityDialogCommon` for the netconf dialog.
pub(crate) fn make_netconf_common(size: u32) -> UtilityDialogCommon {
Expand Down Expand Up @@ -120,10 +130,7 @@ fn run_dialog(params: &mut UtilityMsgDialogParams) -> Result<DialogResult, Dialo
// SAFETY: DIALOG_LIST is only used by utility dialog loops
// which run on the main thread and never overlap.
unsafe {
crate::sys::sceGuStart(
crate::sys::GuContextType::Direct,
&raw mut DIALOG_LIST as *mut core::ffi::c_void,
);
crate::sys::sceGuStart(crate::sys::GuContextType::Direct, DIALOG_LIST.as_mut_ptr());
crate::sys::sceGuClearColor(0xff00_0000); // opaque black
crate::sys::sceGuClear(crate::sys::ClearBuffer::COLOR_BUFFER_BIT);
crate::sys::sceGuFinish();
Expand Down
86 changes: 48 additions & 38 deletions psp/src/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! ```

use crate::sys::gpio as nids;
use core::sync::atomic::{AtomicBool, Ordering};
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

/// Error from a GPIO operation.
#[derive(Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -56,12 +56,33 @@ type PortClearFn = unsafe extern "C" fn(mask: u32) -> i32;
type SetPortModeFn = unsafe extern "C" fn(pin: u32, mode: u32) -> i32;
type GetCaptureFn = unsafe extern "C" fn() -> u32;

static mut PORT_READ: Option<PortReadFn> = None;
static mut PORT_SET: Option<PortSetFn> = None;
static mut PORT_CLEAR: Option<PortClearFn> = None;
static mut SET_PORT_MODE: Option<SetPortModeFn> = None;
static mut SET_PORT_MODE2: Option<SetPortModeFn> = None;
static mut GET_CAPTURE: Option<GetCaptureFn> = None;
/// Stores a resolved function pointer as an `AtomicUsize` (0 = not resolved).
struct AtomicFnPtr(AtomicUsize);

impl AtomicFnPtr {
const fn new() -> Self {
Self(AtomicUsize::new(0))
}

fn store(&self, addr: *mut u8) {
self.0.store(addr as usize, Ordering::Release);
}

fn load(&self) -> Option<usize> {
let v = self.0.load(Ordering::Acquire);
if v == 0 { None } else { Some(v) }
}
}

// SAFETY: Function pointers are resolved once in init() and then only read.
unsafe impl Sync for AtomicFnPtr {}

static PORT_READ: AtomicFnPtr = AtomicFnPtr::new();
static PORT_SET: AtomicFnPtr = AtomicFnPtr::new();
static PORT_CLEAR: AtomicFnPtr = AtomicFnPtr::new();
static SET_PORT_MODE: AtomicFnPtr = AtomicFnPtr::new();
static SET_PORT_MODE2: AtomicFnPtr = AtomicFnPtr::new();
static GET_CAPTURE: AtomicFnPtr = AtomicFnPtr::new();
static INITIALIZED: AtomicBool = AtomicBool::new(false);

/// Resolve GPIO driver NIDs. Call once before using other functions.
Expand All @@ -76,33 +97,22 @@ pub unsafe fn init() -> u32 {
let l = nids::GPIO_LIBRARY.as_ptr();
let mut count = 0u32;

unsafe {
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_PORT_READ) {
PORT_READ = Some(core::mem::transmute(addr));
count += 1;
}
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_PORT_SET) {
PORT_SET = Some(core::mem::transmute(addr));
count += 1;
}
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_PORT_CLEAR) {
PORT_CLEAR = Some(core::mem::transmute(addr));
count += 1;
}
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_SET_PORT_MODE) {
SET_PORT_MODE = Some(core::mem::transmute(addr));
count += 1;
}
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_SET_PORT_MODE2) {
SET_PORT_MODE2 = Some(core::mem::transmute(addr));
count += 1;
}
if let Some(addr) = crate::hook::find_function(m, l, nids::NID_GPIO_GET_CAPTURE_PORT) {
GET_CAPTURE = Some(core::mem::transmute(addr));
count += 1;
}
macro_rules! try_resolve {
($nid:expr, $slot:expr) => {
if let Some(addr) = unsafe { crate::hook::find_function(m, l, $nid) } {
$slot.store(addr);
count += 1;
}
};
}

try_resolve!(nids::NID_GPIO_PORT_READ, PORT_READ);
try_resolve!(nids::NID_GPIO_PORT_SET, PORT_SET);
try_resolve!(nids::NID_GPIO_PORT_CLEAR, PORT_CLEAR);
try_resolve!(nids::NID_GPIO_SET_PORT_MODE, SET_PORT_MODE);
try_resolve!(nids::NID_GPIO_SET_PORT_MODE2, SET_PORT_MODE2);
try_resolve!(nids::NID_GPIO_GET_CAPTURE_PORT, GET_CAPTURE);

INITIALIZED.store(true, Ordering::Release);
count
}
Expand All @@ -111,7 +121,7 @@ pub unsafe fn init() -> u32 {
///
/// Returns `None` if [`init()`] has not been called or NID was not resolved.
pub fn read_port() -> Option<u32> {
let f = unsafe { PORT_READ }?;
let f: PortReadFn = unsafe { core::mem::transmute(PORT_READ.load()?) };
Some(unsafe { f() })
}

Expand All @@ -128,7 +138,7 @@ pub fn read_pin(pin: u32) -> Option<bool> {

/// Read the GPIO interrupt/capture status.
pub fn capture_status() -> Option<u32> {
let f = unsafe { GET_CAPTURE }?;
let f: GetCaptureFn = unsafe { core::mem::transmute(GET_CAPTURE.load()?) };
Some(unsafe { f() })
}

Expand All @@ -138,7 +148,7 @@ pub fn capture_status() -> Option<u32> {
///
/// **Warning:** Actually drives pins. Crashes on pins 29-31+ on TA-090v2.
pub fn set_pin_mode(pin: u32, mode: i32) -> Option<i32> {
let f = unsafe { SET_PORT_MODE }?;
let f: SetPortModeFn = unsafe { core::mem::transmute(SET_PORT_MODE.load()?) };
Some(unsafe { f(pin, mode as u32) })
}

Expand All @@ -147,19 +157,19 @@ pub fn set_pin_mode(pin: u32, mode: i32) -> Option<i32> {
/// Uses `sceGpioSetPortMode2` (NID 0x317D9D2C). Mode: 0=disable, 2=enable.
/// Safe for probing — Output Enable register is silicon-locked on TA-090v2.
pub fn set_pin_mode2(pin: u32, mode: i32) -> Option<i32> {
let f = unsafe { SET_PORT_MODE2 }?;
let f: SetPortModeFn = unsafe { core::mem::transmute(SET_PORT_MODE2.load()?) };
Some(unsafe { f(pin, mode as u32) })
}

/// Drive GPIO pins high.
pub fn set_pins(mask: u32) -> Option<i32> {
let f = unsafe { PORT_SET }?;
let f: PortSetFn = unsafe { core::mem::transmute(PORT_SET.load()?) };
Some(unsafe { f(mask) })
}

/// Drive GPIO pins low.
pub fn clear_pins(mask: u32) -> Option<i32> {
let f = unsafe { PORT_CLEAR }?;
let f: PortClearFn = unsafe { core::mem::transmute(PORT_CLEAR.load()?) };
Some(unsafe { f(mask) })
}

Expand Down
2 changes: 1 addition & 1 deletion psp/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ pub fn connect_dialog() -> Result<(), NetError> {
unsafe {
sys::sceGuStart(
sys::GuContextType::Direct,
&raw mut crate::dialog::DIALOG_LIST as *mut core::ffi::c_void,
crate::dialog::DIALOG_LIST.as_mut_ptr(),
);
sys::sceGuClearColor(0xff00_0000);
sys::sceGuClear(sys::ClearBuffer::COLOR_BUFFER_BIT);
Expand Down
16 changes: 6 additions & 10 deletions psp/src/osk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ const SOUND_THREAD: i32 = 0x10;
/// Maximum iterations for OSK polling (~30 seconds at 60 fps).
const MAX_OSK_ITERATIONS: u32 = 1800;

/// Small display list for utility dialog GU frames (16KB, 16-byte aligned).
#[repr(C, align(16))]
struct Align16<T>(T);
static mut DIALOG_LIST: Align16<[u8; 0x4000]> = Align16([0u8; 0x4000]);
// Reuse the shared display list buffer from dialog.rs (16KB, 16-byte aligned).
// All utility dialogs are mutually exclusive on the main thread, so sharing is safe.
use crate::dialog::DIALOG_LIST;

fn make_common(size: u32) -> UtilityDialogCommon {
UtilityDialogCommon {
Expand Down Expand Up @@ -177,13 +176,10 @@ impl OskBuilder {
// background, then close the frame before updating the
// utility dialog. PSPSDK convention: the dialog update
// must be called **outside** any open GU display list.
// SAFETY: DIALOG_LIST is only used by utility dialog loops
// which run on the main thread and never overlap.
// SAFETY: DIALOG_LIST is shared across dialog/osk/net but all run
// on the main thread and never overlap.
unsafe {
crate::sys::sceGuStart(
crate::sys::GuContextType::Direct,
&raw mut DIALOG_LIST as *mut core::ffi::c_void,
);
crate::sys::sceGuStart(crate::sys::GuContextType::Direct, DIALOG_LIST.as_mut_ptr());
crate::sys::sceGuClearColor(0xff00_0000); // opaque black
crate::sys::sceGuClear(crate::sys::ClearBuffer::COLOR_BUFFER_BIT);
crate::sys::sceGuFinish();
Expand Down
Loading
Loading