yggdrasil/driver/bus/usb/src/communication.rs

93 lines
2.2 KiB
Rust

use core::{
future::poll_fn,
sync::atomic::{AtomicU32, Ordering},
task::{Context, Poll},
};
use alloc::{sync::Arc, vec::Vec};
use futures_util::task::AtomicWaker;
use libk_mm::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum UsbDirection {
Out,
In,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UsbTransferToken(pub u64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct UsbTransferResult(pub u32);
pub struct UsbTransferStatus {
pub result: AtomicU32,
pub notify: AtomicWaker,
}
pub struct UsbTransfer {
pub id: UsbTransferToken,
pub length: usize,
pub direction: UsbDirection,
pub elements: Vec<PhysicalAddress>,
pub status: Arc<UsbTransferStatus>,
}
// TODO this is xHCI-specific
impl UsbTransferResult {
pub fn is_success(&self) -> bool {
(self.0 >> 24) & 0xFF == 1
}
pub fn sub_length(&self) -> usize {
(self.0 & 0xFFFFFF) as _
}
}
impl UsbTransfer {
pub async fn wait(&self) -> Result<usize, Error> {
let sub_length = self.status.wait().await?;
Ok(self.length.saturating_sub(sub_length))
}
}
impl UsbTransferStatus {
pub fn new() -> Self {
Self {
result: AtomicU32::new(0),
notify: AtomicWaker::new(),
}
}
pub(crate) async fn wait(&self) -> Result<usize, Error> {
poll_fn(|cx| {
self.poll(cx).map(|v| {
if v.is_success() {
Ok(v.sub_length())
} else {
Err(Error::InvalidOperation)
}
})
})
.await
}
pub fn signal(&self, status: u32) {
self.result.store(status, Ordering::Release);
self.notify.wake();
}
pub fn poll(&self, cx: &mut Context<'_>) -> Poll<UsbTransferResult> {
self.notify.register(cx.waker());
let value = self.result.load(Ordering::Acquire);
if value != 0 {
Poll::Ready(UsbTransferResult(value))
} else {
Poll::Pending
}
}
}