Add (untested) transfer support.
This commit is contained in:
Generated
+1
@@ -1337,6 +1337,7 @@ dependencies = [
|
||||
name = "usbscsid"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xhcid 0.1.0",
|
||||
]
|
||||
|
||||
@@ -8,5 +8,6 @@ license = "MIT"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
plain = "0.2"
|
||||
thiserror = "1"
|
||||
xhcid = { path = "../xhcid" }
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::env;
|
||||
use xhcid_interface::XhciClientHandle;
|
||||
|
||||
pub mod protocol;
|
||||
pub mod scsi;
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args().skip(1);
|
||||
@@ -17,4 +18,5 @@ fn main() {
|
||||
|
||||
let handle = XhciClientHandle::new(scheme, port);
|
||||
let protocol = protocol::setup(&handle, protocol);
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ pub struct BulkOnlyTransport<'a> {
|
||||
|
||||
impl<'a> BulkOnlyTransport<'a> {
|
||||
pub fn init(handle: &'a XhciClientHandle) -> Result<Self, ProtocolError> {
|
||||
let lun = get_max_lun(handle, 0)?;
|
||||
println!("BOT_MAX_LUN {}", lun);
|
||||
Ok(Self {
|
||||
handle,
|
||||
})
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
use super::opcodes::{Opcode, ServiceActionA3};
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct ReportIdentInfo {
|
||||
pub opcode: u8,
|
||||
/// bits 7:5 reserved
|
||||
pub serviceaction: u8,
|
||||
pub _rsvd: u16,
|
||||
pub restricted: u16,
|
||||
/// little endian
|
||||
pub alloc_len: u32,
|
||||
/// bit 0 reserved
|
||||
pub info_ty: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
|
||||
pub const REP_ID_INFO_INFO_TY_MASK: u8 = 0xFE;
|
||||
pub const REP_ID_INFO_INFO_TY_SHIFT: u8 = 1;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct ReportSuppOpcodes {
|
||||
pub opcode: u8,
|
||||
/// bits 7:5 reserved
|
||||
pub serviceaction: u8,
|
||||
/// bits 2:0 represent "REPORTING OPTIONS", bits 6:3 are reserved, and bit 7 is RCTD
|
||||
pub rep_opts: u8,
|
||||
pub req_opcode: u8,
|
||||
/// little endian
|
||||
pub req_serviceaction: u16,
|
||||
/// little endian
|
||||
pub alloc_len: u32,
|
||||
pub _rsvd: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
impl ReportSuppOpcodes {
|
||||
pub const fn new(rep_opts: ReportSuppOpcodesOptions, rctd: bool, req_opcode: u8, req_serviceaction: u16, alloc_len: u32, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::ServiceActionA3 as u8,
|
||||
serviceaction: ServiceActionA3::ReportSuppOpcodes as u8,
|
||||
rep_opts: ((rctd as u8) << REP_OPTS_RCTD_SHIFT) | rep_opts as u8,
|
||||
req_opcode,
|
||||
req_serviceaction: u16::to_le(req_serviceaction),
|
||||
alloc_len: u32::to_le(alloc_len),
|
||||
_rsvd: 0,
|
||||
control,
|
||||
}
|
||||
}
|
||||
pub const fn get_all(rctd: bool, alloc_len: u32, control: u8) -> Self {
|
||||
Self::new(ReportSuppOpcodesOptions::ListAll, rctd, 0, 0, alloc_len, control)
|
||||
}
|
||||
}
|
||||
|
||||
pub const REP_OPTS_MAIN_MASK: u8 = 0b0000_0111;
|
||||
pub const REP_OPTS_MAIN_SHIFT: u8 = 0;
|
||||
pub const REP_OPTS_RCTD_BIT: u8 = 1 << REP_OPTS_RCTD_SHIFT;
|
||||
pub const REP_OPTS_RCTD_SHIFT: u8 = 7;
|
||||
|
||||
/// Valid values of the `req_opts` field of `ReportSuppOpcodes`.
|
||||
#[repr(u8)]
|
||||
pub enum ReportSuppOpcodesOptions {
|
||||
/// Returns all commands, no matter what parameters are set.
|
||||
ListAll,
|
||||
|
||||
/// Returns one command with the requested opcode. If the command has service actions, this
|
||||
/// command fails.
|
||||
NoServicaction,
|
||||
|
||||
/// Returns one command with the requested opcode and service action. If the command doesn't
|
||||
/// support service actions, this command fails.
|
||||
ExplicitBoth,
|
||||
|
||||
/// Returns one command with the requested opcode and service action. The command may or may
|
||||
/// not implement service actions, but if it does, it has to be correct for the return value to
|
||||
/// indicate SUPPORTED.
|
||||
IndicateSupport,
|
||||
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct AllCommandsParam {
|
||||
/// Little endian
|
||||
pub data_len: u32,
|
||||
pub descs: [CommandDescriptor],
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct CommandDescriptor {
|
||||
pub opcode: u8,
|
||||
pub _rsvd1: u8,
|
||||
/// little endian
|
||||
pub serviceaction: u16,
|
||||
pub _rsvd2: u8,
|
||||
/// bit 0 is SERVACTV, bit 1 is CTDP, and bits 7:2 reserved
|
||||
pub a: u8,
|
||||
/// little endian
|
||||
pub cdb_len: u16,
|
||||
}
|
||||
#[repr(packed)]
|
||||
pub struct OneCommandParam {
|
||||
pub _rsvd: u8,
|
||||
/// bits 2:0 for SUPPOR, bits 6:3 reserved, and bit 7 for CTDP
|
||||
pub a: u8,
|
||||
// TODO
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod cmds;
|
||||
pub mod opcodes;
|
||||
|
||||
|
||||
pub struct Scsi {
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#[repr(u8)]
|
||||
pub enum Opcode {
|
||||
TestUnitReady = 0x00,
|
||||
/// obsolete
|
||||
RezeroUnit = 0x01,
|
||||
RequestSense = 0x03,
|
||||
FormatUnit = 0x04,
|
||||
ReassignBlocks = 0x07,
|
||||
/// obsolete
|
||||
Read6 = 0x08,
|
||||
/// obsolete
|
||||
Write6 = 0x0A,
|
||||
/// obsolete
|
||||
Seek = 0x0B,
|
||||
Inquiry = 0x12,
|
||||
ModeSelect6 = 0x15,
|
||||
/// obsolete
|
||||
Reserve6 = 0x16,
|
||||
/// obsolete
|
||||
Release6 = 0x17,
|
||||
ModeSense6 = 0x1A,
|
||||
StartStopUnit = 0x1B,
|
||||
RecvDiagnosticRes = 0x1C,
|
||||
SendDiagnostic = 0x1D,
|
||||
ReadCapacity10 = 0x25,
|
||||
Read10 = 0x28,
|
||||
Write10 = 0x2A,
|
||||
/// obsolete
|
||||
SeekExt = 0x2B,
|
||||
WriteAndVerify10 = 0x2E,
|
||||
Verify10 = 0x2F,
|
||||
SyncCache10 = 0x35,
|
||||
ReadDefectData10 = 0x37,
|
||||
WriteBuf10 = 0x3B,
|
||||
ReadBuf10 = 0x3C,
|
||||
/// obsolete
|
||||
ReadLong10 = 0x3E,
|
||||
WriteLong10 = 0x3F,
|
||||
/// obsolete
|
||||
ChangeDef = 0x40,
|
||||
WriteSame10 = 0x41,
|
||||
Unmap = 0x42,
|
||||
Sanitize = 0x48,
|
||||
LogSelect = 0x4C,
|
||||
LogSense = 0x4D,
|
||||
ModeSelect10 = 0x55,
|
||||
/// obsolete
|
||||
Reserve10 = 0x56,
|
||||
/// obsolete
|
||||
Release10 = 0x57,
|
||||
ModeSense10 = 0x5A,
|
||||
PersistentResvIn = 0x5E,
|
||||
PersistentResvOut = 0x5F,
|
||||
ServiceAction7F = 0x7F,
|
||||
Read16 = 0x88,
|
||||
Write16 = 0x8A,
|
||||
WriteAndVerify16 = 0x8E,
|
||||
Verify16 = 0x8F,
|
||||
SyncCache16 = 0x91,
|
||||
WriteSame16 = 0x93,
|
||||
WriteStream16 = 0x9A,
|
||||
ReadBuf16 = 0x9B,
|
||||
WriteAtomic16 = 0x9C,
|
||||
ServiceAction9E = 0x9E,
|
||||
ServiceAction9F,
|
||||
ReportLuns = 0xA0,
|
||||
SecurityProtoIn = 0xA2,
|
||||
ServiceActionA3 = 0xA3,
|
||||
ServiceActionA4 = 0xA4,
|
||||
Read12 = 0xA8,
|
||||
Write12 = 0xAA,
|
||||
WriteAndVerify12 = 0xAE,
|
||||
Verify12 = 0xAF,
|
||||
SecurityProtoOut = 0xB5,
|
||||
ReadDefectData12 = 0xB7,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ServiceAction7F {
|
||||
Read32 = 0x09,
|
||||
Verify32 = 0x0A,
|
||||
Write32 = 0x0B,
|
||||
WriteAndVerify32 = 0x0C,
|
||||
WriteSame32 = 0x0D,
|
||||
WriteAtomic32 = 0x18,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ServiceAction9E {
|
||||
ReadCapacity16 = 0x10,
|
||||
ReadLong16 = 0x11,
|
||||
GetLbaStatus = 0x12,
|
||||
StreamControl = 0x14,
|
||||
BackgroundControl = 0x15,
|
||||
GetStreamStatus = 0x16,
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum ServiceAction9F {
|
||||
WriteLong16 = 0x11,
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum ServiceActionA3 {
|
||||
ReportIdentInfo = 0x05,
|
||||
ReportSuppOpcodes = 0x0C,
|
||||
ReportSuppTaskManFuncs = 0x0D,
|
||||
ReportTimestamp = 0x0F,
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum ServiceActionA4 {
|
||||
SetIdentInfo = 0x06,
|
||||
SetTimestamp = 0x0F,
|
||||
}
|
||||
@@ -279,20 +279,27 @@ pub enum DeviceReqData<'a> {
|
||||
NoData,
|
||||
}
|
||||
impl DeviceReqData<'_> {
|
||||
fn len(&self) -> usize {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::In(buf) => buf.len(),
|
||||
Self::Out(buf) => buf.len(),
|
||||
Self::NoData => 0,
|
||||
}
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn map_buf<T, F: Fn(&[u8]) -> T>(&self, f: F) -> Option<T> {
|
||||
match self {
|
||||
Self::In(sbuf) => Some(f(sbuf)),
|
||||
Self::Out(dbuf) => Some(f(dbuf)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DeviceReqData<'a> {
|
||||
fn direction(&self) -> PortReqDirection {
|
||||
pub fn direction(&self) -> PortReqDirection {
|
||||
match self {
|
||||
DeviceReqData::Out(_) => PortReqDirection::HostToDevice,
|
||||
DeviceReqData::NoData => PortReqDirection::HostToDevice,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use syscall::error::Result;
|
||||
use syscall::io::{Dma, Io, Mmio};
|
||||
|
||||
use super::ring::Ring;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct SlotContext {
|
||||
pub a: Mmio<u32>,
|
||||
@@ -105,8 +107,23 @@ pub struct StreamContext {
|
||||
rsvd: Mmio<u32>,
|
||||
}
|
||||
|
||||
unsafe impl plain::Plain for StreamContext {}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum StreamContextType {
|
||||
SecondaryRing,
|
||||
PrimaryRing,
|
||||
PrimarySsa8,
|
||||
PrimarySsa16,
|
||||
PrimarySsa32,
|
||||
PrimarySsa64,
|
||||
PrimarySsa128,
|
||||
PrimarySsa256,
|
||||
}
|
||||
|
||||
pub struct StreamContextArray {
|
||||
pub contexts: Dma<[StreamContext]>,
|
||||
pub rings: Vec<Ring>,
|
||||
}
|
||||
|
||||
impl StreamContextArray {
|
||||
@@ -114,9 +131,15 @@ impl StreamContextArray {
|
||||
unsafe {
|
||||
Ok(Self {
|
||||
contexts: Dma::zeroed_unsized(count)?,
|
||||
rings: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
pub fn add_ring(&mut self, stream_id: u16, link: bool) -> Result<()> {
|
||||
// NOTE: stream_id is reserved
|
||||
self.rings.insert(stream_id as usize, Ring::new(link)?);
|
||||
Ok(())
|
||||
}
|
||||
pub fn register(&self) -> u64 {
|
||||
self.contexts.physical() as u64
|
||||
}
|
||||
|
||||
+105
-11
@@ -9,7 +9,7 @@ use syscall::io::{Dma, Io};
|
||||
use syscall::scheme::SchemeMut;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS,
|
||||
ENOTDIR, ENXIO, EOVERFLOW, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_CREAT, O_DIRECTORY,
|
||||
ENOTDIR, ENXIO, EOVERFLOW, EPERM, ESPIPE, MODE_CHR, MODE_DIR, MODE_FILE, O_CREAT, O_DIRECTORY,
|
||||
O_RDONLY, O_RDWR, O_STAT, O_WRONLY, SEEK_CUR, SEEK_END, SEEK_SET,
|
||||
};
|
||||
|
||||
@@ -310,7 +310,10 @@ impl Xhci {
|
||||
assert_ne!(ep_ty, 0); // 0 means invalid.
|
||||
|
||||
let ring_ptr = if max_streams != 0 {
|
||||
let array = StreamContextArray::new(1 << (max_streams + 1))?;
|
||||
let mut array = StreamContextArray::new(1 << (max_streams + 1))?;
|
||||
|
||||
// TODO: Use as many stream rings as needed.
|
||||
array.add_ring(1, true)?;
|
||||
let array_ptr = array.register();
|
||||
|
||||
assert_eq!(
|
||||
@@ -398,11 +401,86 @@ impl Xhci {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
fn transfer_read(&mut self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result<()> {
|
||||
Err(Error::new(ENOSYS))
|
||||
fn transfer_read(&mut self, port_num: usize, endp_idx: u8, buf: &mut [u8]) -> Result<u32> {
|
||||
self.transfer(port_num, endp_idx, if !buf.is_empty() { DeviceReqData::In(buf) } else { DeviceReqData::NoData })
|
||||
}
|
||||
fn transfer_write(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result<()> {
|
||||
Err(Error::new(ENOSYS))
|
||||
fn transfer_write(&mut self, port_num: usize, endp_idx: u8, buf: &[u8]) -> Result<u32> {
|
||||
self.transfer(port_num, endp_idx, if !buf.is_empty() { DeviceReqData::Out(buf) } else { DeviceReqData::NoData })
|
||||
}
|
||||
// TODO: Rename DeviceReqData to something more general.
|
||||
fn transfer(&mut self, port_num: usize, endp_idx: u8, buf: DeviceReqData) -> Result<u32> {
|
||||
let endp_num = endp_idx + 1;
|
||||
// TODO: Check that buf has a nonzero size, otherwise (at least for Rust's GlobalAlloc),
|
||||
// UB.
|
||||
let mut dma_buffer = match buf {
|
||||
DeviceReqData::Out(sbuf) => {
|
||||
let dma_buffer = unsafe { Dma::<[u8]>::zeroed_unsized(sbuf.len()) }?;
|
||||
dma_buffer.copy_from_slice(sbuf);
|
||||
Some(dma_buffer)
|
||||
}
|
||||
DeviceReqData::In(dbuf) => {
|
||||
Some(unsafe { Dma::<[u8]>::zeroed_unsized(dbuf.len()) }?)
|
||||
}
|
||||
DeviceReqData::NoData => None,
|
||||
};
|
||||
|
||||
let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?;
|
||||
let endp_desc: &EndpDesc = port_state.dev_desc.config_descs.get(0).ok_or(Error::new(EIO))?.interface_descs.get(0).ok_or(Error::new(EIO))?.endpoints.get(endp_idx as usize).ok_or(Error::new(EBADF))?;
|
||||
let endp_state = port_state.endpoint_states.get_mut(&endp_idx).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let ring: &mut Ring = match endp_state {
|
||||
EndpointState::Ready(super::RingOrStreams::Ring(ref mut ring)) => ring,
|
||||
EndpointState::Ready(super::RingOrStreams::Streams(stream_ctx_array)) => stream_ctx_array.rings.get_mut(1).ok_or(Error::new(EBADF))?,
|
||||
EndpointState::Init => return Err(Error::new(EIO)),
|
||||
};
|
||||
// TODO: Scatter-gather transfers, possibly allowing >64KiB sizes.
|
||||
let len = u16::try_from(buf.len()).or(Err(Error::new(ENOSYS)))?;
|
||||
let max_packet_size = endp_desc.max_packet_size;
|
||||
{
|
||||
let (trb, cycle) = ring.next();
|
||||
let (buffer, idt) = if len <= 8 && max_packet_size >= 8 {
|
||||
buf.map_buf(|buf| {
|
||||
let bytes = match <[u8; 8]>::try_from(buf) {
|
||||
Ok(b) => b,
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
// FIXME: little endian, right?
|
||||
(u64::from_le_bytes(bytes), true)
|
||||
}).unwrap_or((0, false))
|
||||
} else {
|
||||
(dma_buffer.map(|dma| dma.physical()).unwrap_or(0) as u64, false)
|
||||
};
|
||||
let estimated_td_size = mem::size_of_val(&trb) as u8; // one trb per td
|
||||
trb.normal(buffer, len, cycle, estimated_td_size, false, true, false, true, idt, false);
|
||||
}
|
||||
|
||||
self.dbs[port_state.slot as usize].write(endp_num.into());
|
||||
|
||||
let bytes_transferred = {
|
||||
let event = self.cmd.next_event();
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
// FIXME: EDTLA if event data was set
|
||||
if event.completion_code() != TrbCompletionCode::ShortPacket as u8 && event.transfer_length() != 0 {
|
||||
println!("Event trb yielded a short packet, but all bytes were still transferred");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8 || event.trb_type() != TrbType::Transfer as u8 {
|
||||
println!("Custom transfer event failed with {:#0x} {:#0x} {:#0x}", event.data.read(), event.status.read(), event.control.read());
|
||||
}
|
||||
// TODO: Handle event data
|
||||
println!("EVENT DATA: {:?}", event.event_data());
|
||||
|
||||
u32::from(len) - event.transfer_length()
|
||||
};
|
||||
|
||||
if let DeviceReqData::In(ref mut dbuf) = buf {
|
||||
dbuf.copy_from_slice(dma_buffer.unwrap());
|
||||
}
|
||||
|
||||
Ok(bytes_transferred)
|
||||
}
|
||||
pub(crate) fn get_dev_desc(&mut self, port_id: usize) -> Result<DevDesc> {
|
||||
let st = self
|
||||
@@ -814,10 +892,9 @@ impl SchemeMut for Xhci {
|
||||
return Err(Error::new(EISDIR));
|
||||
}
|
||||
|
||||
if self
|
||||
.port_states
|
||||
.get(&port_num)
|
||||
.ok_or(Error::new(ENOENT))?
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
if port_state
|
||||
.endpoint_states
|
||||
.get(&endpoint_num)
|
||||
.is_none()
|
||||
@@ -833,7 +910,24 @@ impl SchemeMut for Xhci {
|
||||
}
|
||||
EndpointHandleTy::Status(0)
|
||||
}
|
||||
"transfer" => EndpointHandleTy::Transfer,
|
||||
"transfer" => {
|
||||
if endpoint_num == 0 {
|
||||
// Don't allow user programs to interface directly with the control
|
||||
// endpoint.
|
||||
return Err(Error::new(EPERM));
|
||||
}
|
||||
let endp_desc = &port_state.dev_desc.config_descs.get(0).ok_or(Error::new(EIO))?.interface_descs.get(0).ok_or(Error::new(EIO))?.endpoints.get(endpoint_num as usize).ok_or(Error::new(ENOENT))?;
|
||||
match endp_desc.direction() {
|
||||
EndpDirection::Out => if flags & O_RDWR != O_WRONLY && flags & O_STAT != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
EndpDirection::In => if flags & O_RDWR != O_RDONLY && flags & O_STAT != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
EndpointHandleTy::Transfer
|
||||
}
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
Handle::Endpoint(port_num, endpoint_num, st)
|
||||
|
||||
@@ -117,9 +117,18 @@ pub const TRB_STATUS_COMPLETION_CODE_MASK: u32 = 0xFF00_0000;
|
||||
pub const TRB_STATUS_COMPLETION_PARAM_SHIFT: u8 = 0;
|
||||
pub const TRB_STATUS_COMPLETION_PARAM_MASK: u32 = 0x00FF_FFFF;
|
||||
|
||||
pub const TRB_STATUS_TRANSFER_LENGTH_SHIFT: u8 = 0;
|
||||
pub const TRB_STATUS_TRANSFER_LENGTH_MASK: u32 = 0x00FF_FFFF;
|
||||
|
||||
pub const TRB_CONTROL_TRB_TYPE_SHIFT: u8 = 10;
|
||||
pub const TRB_CONTROL_TRB_TYPE_MASK: u32 = 0x0000_FC00;
|
||||
|
||||
pub const TRB_CONTROL_EVENT_DATA_SHIFT: u8 = 2;
|
||||
pub const TRB_CONTROL_EVENT_DATA_BIT: u32 = 1 << TRB_CONTROL_EVENT_DATA_SHIFT;
|
||||
|
||||
pub const TRB_CONTROL_ENDPOINT_ID_MASK: u32 = 0x001F_0000;
|
||||
pub const TRB_CONTROL_ENDPOINT_ID_SHIFT: u8 = 16;
|
||||
|
||||
impl Trb {
|
||||
pub fn set(&mut self, data: u64, status: u32, control: u32) {
|
||||
self.data.write(data);
|
||||
@@ -137,6 +146,19 @@ impl Trb {
|
||||
pub fn completion_param(&self) -> u32 {
|
||||
self.status.read() & TRB_STATUS_COMPLETION_PARAM_MASK
|
||||
}
|
||||
/// Returns the number of bytes that should have been transmitten, but weren't.
|
||||
pub fn transfer_length(&self) -> u32 {
|
||||
self.status.read() & TRB_STATUS_TRANSFER_LENGTH_MASK
|
||||
}
|
||||
pub fn event_data_bit(&self) -> bool {
|
||||
self.control.readf(TRB_CONTROL_EVENT_DATA_BIT)
|
||||
}
|
||||
pub fn event_data(&self) -> Option<u64> {
|
||||
if self.event_data_bit() { Some(self.data.read()) } else { None }
|
||||
}
|
||||
pub fn endpoint_id(&self) -> u8 {
|
||||
((self.control.read() & TRB_CONTROL_ENDPOINT_ID_MASK) >> TRB_CONTROL_ENDPOINT_ID_SHIFT) as u8
|
||||
}
|
||||
pub fn trb_type(&self) -> u8 {
|
||||
((self.control.read() & TRB_CONTROL_TRB_TYPE_MASK) >> TRB_CONTROL_TRB_TYPE_SHIFT) as u8
|
||||
}
|
||||
@@ -216,6 +238,23 @@ impl Trb {
|
||||
| (cycle as u32),
|
||||
);
|
||||
}
|
||||
pub fn normal(&mut self, buffer: u64, len: u16, cycle: bool, estimated_td_size: u8, ent: bool, isp: bool, chain: bool, ioc: bool, idt: bool, bei: bool) {
|
||||
assert_eq!(estimated_td_size & 0x1F, estimated_td_size);
|
||||
// NOTE: The interrupter target and no snoop flags have been omitted.
|
||||
self.set(
|
||||
buffer,
|
||||
u32::from(len)
|
||||
| (u32::from(estimated_td_size) << 17),
|
||||
u32::from(cycle)
|
||||
| (u32::from(ent) << 1)
|
||||
| (u32::from(isp) << 2)
|
||||
| (u32::from(ent) << 4)
|
||||
| (u32::from(ioc) << 5)
|
||||
| (u32::from(idt) << 6)
|
||||
| (u32::from(bei) << 9)
|
||||
| ((TrbType::Normal as u32) << 10)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Trb {
|
||||
|
||||
Reference in New Issue
Block a user