93 lines
2.2 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|