Add periodic (driver interface-level) transfers.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use std::env;
|
||||
|
||||
use xhcid_interface::{ConfigureEndpointsReq, XhciClientHandle};
|
||||
use xhcid_interface::{ConfigureEndpointsReq, DeviceReqData, XhciClientHandle};
|
||||
|
||||
pub mod protocol;
|
||||
pub mod scsi;
|
||||
@@ -39,14 +39,16 @@ fn main() {
|
||||
|
||||
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol");
|
||||
|
||||
let get_info = {
|
||||
/*let get_info = {
|
||||
// Max number of bytes that can be recieved from a "REPORT IDENTIFYING INFORMATION"
|
||||
// command.
|
||||
let alloc_len = 256;
|
||||
let info_ty = scsi::cmds::ReportIdInfoInfoTy::IdentInfoSupp;
|
||||
let control = 0; // TODO: NACA?
|
||||
scsi::cmds::ReportIdentInfo::new(alloc_len, info_ty, control)
|
||||
};
|
||||
};*/
|
||||
let inquiry = scsi::cmds::Inquiry::new(false, 0, 36, 0);
|
||||
let mut buffer = [0u8; 36];
|
||||
use protocol::Protocol;
|
||||
protocol.send_command(unsafe { plain::as_bytes(&get_info) }).expect("Failed to send command");
|
||||
protocol.send_command(unsafe { plain::as_bytes(&inquiry) }, DeviceReqData::In(&mut buffer)).expect("Failed to send command");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::io::prelude::*;
|
||||
use std::{io, slice};
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpDirection, EndpointStatus, IfDesc, PortReqDirection, PortReqTy, PortReqRecipient, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle};
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle, XhciEndpTransferHandle};
|
||||
|
||||
use super::{Protocol, ProtocolError};
|
||||
|
||||
@@ -25,6 +25,28 @@ pub struct CommandBlockWrapper {
|
||||
pub cb_len: u8,
|
||||
pub command_block: [u8; 16],
|
||||
}
|
||||
impl CommandBlockWrapper {
|
||||
pub fn new(tag: u32, data_transfer_len: u32, direction: EndpBinaryDirection, lun: u8, cb: &[u8]) -> Result<Self, ProtocolError> {
|
||||
let mut command_block = [0u8; 16];
|
||||
if cb.len() > 16 {
|
||||
return Err(ProtocolError::TooLargeCommandBlock(cb.len()));
|
||||
}
|
||||
|
||||
command_block[..cb.len()].copy_from_slice(&cb);
|
||||
Ok(Self {
|
||||
signature: CBW_SIGNATURE,
|
||||
tag,
|
||||
data_transfer_len,
|
||||
flags: match direction {
|
||||
EndpBinaryDirection::Out => 0,
|
||||
EndpBinaryDirection::In => 1,
|
||||
} << CBW_FLAGS_DIRECTION_SHIFT,
|
||||
lun,
|
||||
cb_len: cb.len() as u8,
|
||||
command_block,
|
||||
})
|
||||
}
|
||||
}
|
||||
unsafe impl plain::Plain for CommandBlockWrapper {}
|
||||
|
||||
pub const CSW_SIGNATURE: u32 = 0x53425355;
|
||||
@@ -47,10 +69,16 @@ pub struct CommandStatusWrapper {
|
||||
}
|
||||
unsafe impl plain::Plain for CommandStatusWrapper {}
|
||||
|
||||
impl CommandStatusWrapper {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.signature == CSW_SIGNATURE
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BulkOnlyTransport<'a> {
|
||||
handle: &'a XhciClientHandle,
|
||||
bulk_in: File,
|
||||
bulk_out: File,
|
||||
bulk_in: XhciEndpTransferHandle,
|
||||
bulk_out: XhciEndpTransferHandle,
|
||||
bulk_in_status: XhciEndpStatusHandle,
|
||||
bulk_out_status: XhciEndpStatusHandle,
|
||||
bulk_in_num: u8,
|
||||
@@ -59,6 +87,8 @@ pub struct BulkOnlyTransport<'a> {
|
||||
current_tag: u32,
|
||||
}
|
||||
|
||||
pub const FEATURE_ENDPOINT_HALT: u16 = 0;
|
||||
|
||||
impl<'a> BulkOnlyTransport<'a> {
|
||||
pub fn init(handle: &'a XhciClientHandle, config_desc: &ConfDesc, if_desc: &IfDesc) -> Result<Self, ProtocolError> {
|
||||
let endpoints = &if_desc.endpoints;
|
||||
@@ -81,11 +111,13 @@ impl<'a> BulkOnlyTransport<'a> {
|
||||
current_tag: 0,
|
||||
})
|
||||
}
|
||||
fn recover_from_stall(&mut self) -> Result<(), ProtocolError> {
|
||||
fn clear_stall(&mut self, endp_num: u8) -> Result<(), XhciClientHandleError> {
|
||||
self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(endp_num), FEATURE_ENDPOINT_HALT)
|
||||
}
|
||||
fn reset_recovery(&mut self) -> Result<(), ProtocolError> {
|
||||
bulk_only_mass_storage_reset(self.handle, 0)?;
|
||||
const ENDPOINT_HALT: u16 = 0;
|
||||
self.handle.clear_feature(PortReqRecipient::Endpoint, self.bulk_in_num.into(), ENDPOINT_HALT)?;
|
||||
self.handle.clear_feature(PortReqRecipient::Endpoint, self.bulk_out_num.into(), ENDPOINT_HALT)?;
|
||||
self.clear_stall(self.bulk_in_num.into())?;
|
||||
self.clear_stall(self.bulk_out_num.into())?;
|
||||
|
||||
if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted {
|
||||
return Err(ProtocolError::RecoveryFailed)
|
||||
@@ -95,7 +127,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
fn send_command(&mut self, cb: &[u8]) -> Result<(), ProtocolError> {
|
||||
fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<(), ProtocolError> {
|
||||
dbg!(self.bulk_in_status.current_status()?);
|
||||
dbg!(self.bulk_out_status.current_status()?);
|
||||
self.current_tag += 1;
|
||||
@@ -110,34 +142,55 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
let cbw = CommandBlockWrapper {
|
||||
signature: CBW_SIGNATURE,
|
||||
tag,
|
||||
data_transfer_len: 256, // TODO
|
||||
data_transfer_len: data.len() as u32,
|
||||
lun: 0, // TODO
|
||||
flags: 1 << 7, // TODO
|
||||
flags: u8::from(data.direction() == PortReqDirection::DeviceToHost) << 7,
|
||||
cb_len: cb.len().try_into().or(Err(ProtocolError::TooLargeCommandBlock(cb.len())))?,
|
||||
command_block,
|
||||
};
|
||||
let bytes_written = self.bulk_out.write(unsafe { plain::as_bytes(&cbw) })?;
|
||||
if bytes_written != 31 {
|
||||
panic!("invalid number of cbw bytes written");
|
||||
match self.bulk_out.transfer_write(unsafe { plain::as_bytes(&cbw) })? {
|
||||
PortTransferStatus::ShortPacket(31) => (),
|
||||
PortTransferStatus::Stalled => {
|
||||
panic!("bulk out endpoint stalled when sending CBW");
|
||||
}
|
||||
_ => panic!("invalid number of CBW bytes written; expected a short packed of length 31 (0x1F)"),
|
||||
}
|
||||
let mut buffer = [0u8; 256];
|
||||
let bytes_read = self.bulk_in.read(&mut buffer)?;
|
||||
if bytes_read != 256 {
|
||||
panic!("invalid number of bytes read");
|
||||
}
|
||||
println!("{}", base64::encode(&buffer[..]));
|
||||
|
||||
match data {
|
||||
DeviceReqData::In(buffer) => {
|
||||
match self.bulk_in.transfer_read(buffer)? {
|
||||
PortTransferStatus::Success => (),
|
||||
PortTransferStatus::ShortPacket(len) => panic!("received short packed (len {}) when transferring data", len),
|
||||
PortTransferStatus::Stalled => {
|
||||
println!("bulk in endpoint stalled when reading data");
|
||||
self.clear_stall(self.bulk_in_num)?;
|
||||
}
|
||||
PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))),
|
||||
};
|
||||
println!("{}", base64::encode(&buffer[..]));
|
||||
}
|
||||
DeviceReqData::Out(ref buffer) => todo!(),
|
||||
DeviceReqData::NoData => todo!(),
|
||||
};
|
||||
|
||||
let mut csw = CommandStatusWrapper::default();
|
||||
let csw_bytes_read = self.bulk_in.read(unsafe { plain::as_mut_bytes(&mut csw) })?;
|
||||
|
||||
if csw_bytes_read != 13 {
|
||||
panic!("invalid number of csw bytes read");
|
||||
match self.bulk_in.transfer_read(unsafe { plain::as_mut_bytes(&mut csw) })? {
|
||||
PortTransferStatus::ShortPacket(13) => (),
|
||||
PortTransferStatus::Stalled => {
|
||||
println!("bulk in endpoint stalled when reading CSW");
|
||||
self.clear_stall(self.bulk_in_num)?;
|
||||
}
|
||||
_ => panic!("invalid number of CSW bytes read; expected a short packet of length 13 (0xD)"),
|
||||
};
|
||||
|
||||
if !csw.is_valid() {
|
||||
self.reset_recovery()?;
|
||||
}
|
||||
dbg!(csw);
|
||||
|
||||
if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted {
|
||||
println!("Trying to recover from stall");
|
||||
self.recover_from_stall()?;
|
||||
dbg!(self.bulk_in_status.current_status()?, self.bulk_out_status.current_status()?);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::io;
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::{DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError};
|
||||
use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProtocolError {
|
||||
@@ -19,7 +19,7 @@ pub enum ProtocolError {
|
||||
}
|
||||
|
||||
pub trait Protocol {
|
||||
fn send_command(&mut self, command: &[u8]) -> Result<(), ProtocolError>;
|
||||
fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<(), ProtocolError>;
|
||||
}
|
||||
|
||||
/// Bulk-only transport
|
||||
|
||||
@@ -122,3 +122,26 @@ pub struct OneCommandParam {
|
||||
pub a: u8,
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Inquiry {
|
||||
pub opcode: u8,
|
||||
/// bits 7:2 are reserved, bit 1 (CMDDT) is obsolete, bit 0 is EVPD
|
||||
pub evpd: u8,
|
||||
pub page_code: u8,
|
||||
/// little endian
|
||||
pub alloc_len: u16,
|
||||
pub control: u8,
|
||||
}
|
||||
|
||||
impl Inquiry {
|
||||
pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::Inquiry as u8,
|
||||
evpd: evpd as u8,
|
||||
page_code,
|
||||
alloc_len: u16::to_le(alloc_len),
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+154
-22
@@ -63,6 +63,21 @@ pub enum EndpDirection {
|
||||
In,
|
||||
Bidirectional,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum EndpBinaryDirection {
|
||||
Out,
|
||||
In,
|
||||
}
|
||||
impl From<EndpBinaryDirection> for EndpDirection {
|
||||
fn from(b: EndpBinaryDirection) -> Self {
|
||||
match b {
|
||||
EndpBinaryDirection::In => Self::In,
|
||||
EndpBinaryDirection::Out => Self::Out,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PortReqDirection> for EndpDirection {
|
||||
fn from(d: PortReqDirection) -> Self {
|
||||
match d {
|
||||
@@ -185,7 +200,7 @@ pub struct PortReq {
|
||||
pub length: u16,
|
||||
pub transfers_data: bool,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PortReqDirection {
|
||||
HostToDevice,
|
||||
DeviceToHost,
|
||||
@@ -230,7 +245,7 @@ impl PortState {
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
#[error("invalid input")]
|
||||
pub struct Invalid;
|
||||
pub struct Invalid(pub &'static str);
|
||||
|
||||
impl str::FromStr for PortState {
|
||||
type Err = Invalid;
|
||||
@@ -241,7 +256,7 @@ impl str::FromStr for PortState {
|
||||
"default" => Self::Default,
|
||||
"addressed" => Self::Addressed,
|
||||
"configured" => Self::Configured,
|
||||
_ => return Err(Invalid),
|
||||
_ => return Err(Invalid("read reserved port state")),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -256,6 +271,14 @@ pub enum EndpointStatus {
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum PortTransferStatus {
|
||||
Success,
|
||||
ShortPacket(u16),
|
||||
Stalled,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl EndpointStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
@@ -278,7 +301,7 @@ impl str::FromStr for EndpointStatus {
|
||||
"halted" => Self::Halted,
|
||||
"stopped" => Self::Stopped,
|
||||
"error" => Self::Error,
|
||||
_ => return Err(Invalid),
|
||||
_ => return Err(Invalid("read reserved endpoint state")),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -334,7 +357,9 @@ impl XhciClientHandle {
|
||||
let mut file = OpenOptions::new().read(false).write(true).open(path)?;
|
||||
let json_bytes_written = file.write(&json)?;
|
||||
if json_bytes_written != json.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"configure_endpoints didn't read as many bytes as were requested",
|
||||
)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -356,14 +381,35 @@ impl XhciClientHandle {
|
||||
num: u8,
|
||||
) -> result::Result<XhciEndpStatusHandle, XhciClientHandleError> {
|
||||
let path = format!("{}:port{}/endpoints/{}/status", self.scheme, self.port, num);
|
||||
Ok(XhciEndpStatusHandle(OpenOptions::new().read(true).write(false).create(false).open(path)?))
|
||||
Ok(XhciEndpStatusHandle(
|
||||
OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.create(false)
|
||||
.open(path)?,
|
||||
))
|
||||
}
|
||||
pub fn open_endpoint(&self, num: u8, direction: PortReqDirection) -> result::Result<File, XhciClientHandleError> {
|
||||
let path = format!("{}:port{}/endpoints/{}/transfer", self.scheme, self.port, num);
|
||||
Ok(match direction {
|
||||
PortReqDirection::HostToDevice => OpenOptions::new().read(false).write(true).create(false).open(path)?,
|
||||
PortReqDirection::DeviceToHost => OpenOptions::new().read(true).write(false).create(false).open(path)?,
|
||||
})
|
||||
pub fn open_endpoint(
|
||||
&self,
|
||||
num: u8,
|
||||
direction: PortReqDirection,
|
||||
) -> result::Result<XhciEndpTransferHandle, XhciClientHandleError> {
|
||||
let path = format!(
|
||||
"{}:port{}/endpoints/{}/transfer",
|
||||
self.scheme, self.port, num
|
||||
);
|
||||
Ok(XhciEndpTransferHandle(match direction {
|
||||
PortReqDirection::HostToDevice => OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(false)
|
||||
.open(path)?,
|
||||
PortReqDirection::DeviceToHost => OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.create(false)
|
||||
.open(path)?,
|
||||
}))
|
||||
}
|
||||
pub fn device_request<'a>(
|
||||
&self,
|
||||
@@ -374,7 +420,8 @@ impl XhciClientHandle {
|
||||
index: u16,
|
||||
data: DeviceReqData<'a>,
|
||||
) -> result::Result<(), XhciClientHandleError> {
|
||||
let length = u16::try_from(data.len()).or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?;
|
||||
let length = u16::try_from(data.len())
|
||||
.or(Err(XhciClientHandleError::TransferBufTooLarge(data.len())))?;
|
||||
|
||||
let req = PortReq {
|
||||
direction: data.direction(),
|
||||
@@ -393,7 +440,9 @@ impl XhciClientHandle {
|
||||
|
||||
let json_bytes_written = file.write(&json)?;
|
||||
if json_bytes_written != json.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"device_request didn't return the same number of bytes as were written",
|
||||
)));
|
||||
}
|
||||
|
||||
match data {
|
||||
@@ -401,25 +450,55 @@ impl XhciClientHandle {
|
||||
let bytes_read = file.read(buf)?;
|
||||
|
||||
if bytes_read != buf.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"device_request didn't transfer (host2dev) all bytes",
|
||||
)));
|
||||
}
|
||||
}
|
||||
DeviceReqData::Out(buf) => {
|
||||
let bytes_read = file.write(&buf)?;
|
||||
|
||||
if bytes_read != buf.len() {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid));
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"device_request didn't transfer (dev2host) all bytes",
|
||||
)));
|
||||
}
|
||||
}
|
||||
DeviceReqData::NoData => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn get_descriptor(&self, recipient: PortReqRecipient, ty: u8, idx: u8, windex: u16, buffer: &mut [u8]) -> result::Result<(), XhciClientHandleError> {
|
||||
self.device_request(PortReqTy::Standard, recipient, 0x06, (u16::from(ty) << 8) | u16::from(idx), windex, DeviceReqData::In(buffer))
|
||||
pub fn get_descriptor(
|
||||
&self,
|
||||
recipient: PortReqRecipient,
|
||||
ty: u8,
|
||||
idx: u8,
|
||||
windex: u16,
|
||||
buffer: &mut [u8],
|
||||
) -> result::Result<(), XhciClientHandleError> {
|
||||
self.device_request(
|
||||
PortReqTy::Standard,
|
||||
recipient,
|
||||
0x06,
|
||||
(u16::from(ty) << 8) | u16::from(idx),
|
||||
windex,
|
||||
DeviceReqData::In(buffer),
|
||||
)
|
||||
}
|
||||
pub fn clear_feature(&self, recipient: PortReqRecipient, index: u16, feature_sel: u16) -> result::Result<(), XhciClientHandleError> {
|
||||
self.device_request(PortReqTy::Standard, recipient, 0x01, feature_sel, index, DeviceReqData::NoData)
|
||||
pub fn clear_feature(
|
||||
&self,
|
||||
recipient: PortReqRecipient,
|
||||
index: u16,
|
||||
feature_sel: u16,
|
||||
) -> result::Result<(), XhciClientHandleError> {
|
||||
self.device_request(
|
||||
PortReqTy::Standard,
|
||||
recipient,
|
||||
0x01,
|
||||
feature_sel,
|
||||
index,
|
||||
DeviceReqData::NoData,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,8 +510,61 @@ impl XhciEndpStatusHandle {
|
||||
self.0.seek(io::SeekFrom::Start(0))?;
|
||||
let mut status_buf = [0u8; 16];
|
||||
let len = self.0.read(&mut status_buf)?;
|
||||
let status = std::str::from_utf8(&status_buf[..len]).or(Err(XhciClientHandleError::InvalidResponse(Invalid)))?;
|
||||
Ok(status.parse::<EndpointStatus>().or(Err(XhciClientHandleError::InvalidResponse(Invalid)))?)
|
||||
let status = std::str::from_utf8(&status_buf[..len]).or(Err(
|
||||
XhciClientHandleError::InvalidResponse(Invalid("non-utf8 endpoint state")),
|
||||
))?;
|
||||
Ok(status
|
||||
.parse::<EndpointStatus>()
|
||||
.or(Err(XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"malformed endpoint state",
|
||||
))))?)
|
||||
}
|
||||
pub fn into_inner(self) -> File {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct XhciEndpTransferHandle(File);
|
||||
|
||||
impl XhciEndpTransferHandle {
|
||||
fn get_status(
|
||||
&mut self,
|
||||
requested_len: usize,
|
||||
bytes_transferred: usize,
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let mut status_buf = [0u8; 32];
|
||||
let status_bytes_read = self.0.read(&mut status_buf)?;
|
||||
|
||||
let status = serde_json::from_slice(dbg!(&status_buf[..status_bytes_read]))?;
|
||||
|
||||
if let PortTransferStatus::ShortPacket(len) = status {
|
||||
if len as usize != bytes_transferred {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid("xhcid gave a short packet with a different length than the bytes transferred (which should have been the same)")));
|
||||
}
|
||||
} else if let PortTransferStatus::Success = status {
|
||||
if requested_len != bytes_transferred {
|
||||
return Err(XhciClientHandleError::InvalidResponse(Invalid("xhcid transferred fewer or more bytes than requested, but didn't return a short packed")));
|
||||
}
|
||||
}
|
||||
Ok(status)
|
||||
}
|
||||
pub fn transfer_write(
|
||||
&mut self,
|
||||
buffer: &[u8],
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let bytes_transferred = self.0.write(buffer)?;
|
||||
self.get_status(buffer.len(), bytes_transferred)
|
||||
}
|
||||
pub fn transfer_read(
|
||||
&mut self,
|
||||
buffer: &mut [u8],
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let bytes_transferred = self.0.read(buffer)?;
|
||||
self.get_status(buffer.len(), bytes_transferred)
|
||||
}
|
||||
pub fn into_inner(self) -> File {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ impl CapabilityRegs {
|
||||
as u8
|
||||
}
|
||||
pub fn max_ports(&self) -> u8 {
|
||||
((self.hcs_params1.read() & HCS_PARAMS1_MAX_PORTS_MASK) >> HCS_PARAMS1_MAX_PORTS_SHIFT) as u8
|
||||
((self.hcs_params1.read() & HCS_PARAMS1_MAX_PORTS_MASK) >> HCS_PARAMS1_MAX_PORTS_SHIFT)
|
||||
as u8
|
||||
}
|
||||
pub fn max_slots(&self) -> u8 {
|
||||
(self.hcs_params1.read() & HCS_PARAMS1_MAX_SLOTS_MASK) as u8
|
||||
|
||||
+24
-14
@@ -8,9 +8,7 @@ pub struct ExtendedCapabilitiesIter {
|
||||
}
|
||||
impl ExtendedCapabilitiesIter {
|
||||
pub unsafe fn new(base: *const u8) -> Self {
|
||||
Self {
|
||||
base,
|
||||
}
|
||||
Self { base }
|
||||
}
|
||||
}
|
||||
impl Iterator for ExtendedCapabilitiesIter {
|
||||
@@ -26,7 +24,11 @@ impl Iterator for ExtendedCapabilitiesIter {
|
||||
|
||||
let next_rel = u16::from(next_rel_in_dwords) << 2;
|
||||
|
||||
self.base = if next_rel != 0 { self.base.offset(next_rel as isize) } else { ptr::null() };
|
||||
self.base = if next_rel != 0 {
|
||||
self.base.offset(next_rel as isize)
|
||||
} else {
|
||||
ptr::null()
|
||||
};
|
||||
|
||||
Some((current, capability_id))
|
||||
}
|
||||
@@ -109,9 +111,7 @@ pub enum Lp {
|
||||
|
||||
impl ProtocolSpeed {
|
||||
pub const fn from_raw(raw: u32) -> Self {
|
||||
Self {
|
||||
a: Mmio::from(raw),
|
||||
}
|
||||
Self { a: Mmio::from(raw) }
|
||||
}
|
||||
pub fn is_lowspeed(&self) -> bool {
|
||||
self.psim() == 1500 && self.psie() == Psie::Kbps
|
||||
@@ -196,11 +196,17 @@ pub const SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT: u8 = 0;
|
||||
|
||||
impl SupportedProtoCap {
|
||||
pub unsafe fn protocol_speeds(&self) -> &[ProtocolSpeed] {
|
||||
slice::from_raw_parts(&self.protocol_speeds as *const u8 as *const _, self.psic() as usize)
|
||||
slice::from_raw_parts(
|
||||
&self.protocol_speeds as *const u8 as *const _,
|
||||
self.psic() as usize,
|
||||
)
|
||||
}
|
||||
pub unsafe fn protocol_speeds_mut(&mut self) -> &mut [ProtocolSpeed] {
|
||||
// XXX: Variance really is annoying sometimes.
|
||||
slice::from_raw_parts_mut(&self.protocol_speeds as *const u8 as *mut u8 as *mut _, self.psic() as usize)
|
||||
slice::from_raw_parts_mut(
|
||||
&self.protocol_speeds as *const u8 as *mut u8 as *mut _,
|
||||
self.psic() as usize,
|
||||
)
|
||||
}
|
||||
pub fn rev_minor(&self) -> u8 {
|
||||
((self.a.read() & SUPP_PROTO_CAP_REV_MIN_MASK) >> SUPP_PROTO_CAP_REV_MIN_SHIFT) as u8
|
||||
@@ -213,10 +219,12 @@ impl SupportedProtoCap {
|
||||
u32::to_le_bytes(self.b.read())
|
||||
}
|
||||
pub fn compat_port_offset(&self) -> u8 {
|
||||
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK) >> SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT) as u8
|
||||
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_OFF_MASK)
|
||||
>> SUPP_PROTO_CAP_COMPAT_PORT_OFF_SHIFT) as u8
|
||||
}
|
||||
pub fn compat_port_count(&self) -> u8 {
|
||||
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK) >> SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT) as u8
|
||||
((self.c.read() & SUPP_PROTO_CAP_COMPAT_PORT_CNT_MASK)
|
||||
>> SUPP_PROTO_CAP_COMPAT_PORT_CNT_SHIFT) as u8
|
||||
}
|
||||
pub fn compat_port_range(&self) -> Range<u8> {
|
||||
self.compat_port_offset()..self.compat_port_offset() + self.compat_port_count()
|
||||
@@ -229,12 +237,12 @@ impl SupportedProtoCap {
|
||||
((self.c.read() & SUPP_PROTO_CAP_PSIC_MASK) >> SUPP_PROTO_CAP_PSIC_SHIFT) as u8
|
||||
}
|
||||
pub fn proto_slot_ty(&self) -> u8 {
|
||||
((self.d.read() & SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK) >> SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT) as u8
|
||||
((self.d.read() & SUPP_PROTO_CAP_PORT_SLOT_TYPE_MASK)
|
||||
>> SUPP_PROTO_CAP_PORT_SLOT_TYPE_SHIFT) as u8
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for SupportedProtoCap {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
||||
f.debug_struct("SupportedProtoCap")
|
||||
.field("rev_minor", &self.rev_minor())
|
||||
.field("rev_major", &self.rev_major())
|
||||
@@ -244,7 +252,9 @@ impl fmt::Debug for SupportedProtoCap {
|
||||
.field("proto_defined", &self.proto_defined())
|
||||
.field("psic", &self.psic())
|
||||
.field("proto_slot_ty", &self.proto_slot_ty())
|
||||
.field("proto_speeds", unsafe { &self.protocol_speeds().to_owned() })
|
||||
.field("proto_speeds", unsafe {
|
||||
&self.protocol_speeds().to_owned()
|
||||
})
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
+34
-19
@@ -351,7 +351,11 @@ impl Xhci {
|
||||
{
|
||||
input.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit).
|
||||
|
||||
input.device.slot.a.write((1 << 27) | (u32::from(speed) << 20)); // FIXME: The speed field, bits 23:20, is deprecated.
|
||||
input
|
||||
.device
|
||||
.slot
|
||||
.a
|
||||
.write((1 << 27) | (u32::from(speed) << 20)); // FIXME: The speed field, bits 23:20, is deprecated.
|
||||
input.device.slot.b.write(((i as u32 + 1) & 0xFF) << 16);
|
||||
|
||||
// control endpoint?
|
||||
@@ -501,18 +505,26 @@ impl Xhci {
|
||||
Ok(())
|
||||
}
|
||||
pub fn capabilities_iter(&self) -> ExtendedCapabilitiesIter {
|
||||
unsafe { ExtendedCapabilitiesIter::new((self.base as *mut u8).offset((self.cap.ext_caps_ptr_in_dwords() << 2) as isize)) }
|
||||
unsafe {
|
||||
ExtendedCapabilitiesIter::new(
|
||||
(self.base as *mut u8).offset((self.cap.ext_caps_ptr_in_dwords() << 2) as isize),
|
||||
)
|
||||
}
|
||||
}
|
||||
pub fn supported_protocols_iter(&self) -> impl Iterator<Item = &'static SupportedProtoCap> {
|
||||
self.capabilities_iter().filter_map(|(pointer, cap_num)| unsafe {
|
||||
if cap_num == CapabilityId::SupportedProtocol as u8 {
|
||||
Some(&*pointer.cast::<SupportedProtoCap>().as_ptr())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
self.capabilities_iter()
|
||||
.filter_map(|(pointer, cap_num)| unsafe {
|
||||
if cap_num == CapabilityId::SupportedProtocol as u8 {
|
||||
Some(&*pointer.cast::<SupportedProtoCap>().as_ptr())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn supported_protocol_speeds(&self, port: u8) -> Option<impl Iterator<Item = &'static ProtocolSpeed>> {
|
||||
pub fn supported_protocol_speeds(
|
||||
&self,
|
||||
port: u8,
|
||||
) -> Option<impl Iterator<Item = &'static ProtocolSpeed>> {
|
||||
use extended::*;
|
||||
const DEFAULT_SUPP_PROTO_SPEEDS: [ProtocolSpeed; 7] = [
|
||||
// Full-speed
|
||||
@@ -521,7 +533,7 @@ impl Xhci {
|
||||
| (false as u32) << PROTO_SPEED_PFD_SHIFT
|
||||
| (Psie::Mbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 12 << PROTO_SPEED_PSIM_SHIFT
|
||||
| 1 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 1 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// Low-speed
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -529,7 +541,7 @@ impl Xhci {
|
||||
| (false as u32) << PROTO_SPEED_PFD_SHIFT
|
||||
| (Psie::Kbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 1500 << PROTO_SPEED_PSIM_SHIFT
|
||||
| 2 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 2 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// High-speed
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -537,7 +549,7 @@ impl Xhci {
|
||||
| (false as u32) << PROTO_SPEED_PFD_SHIFT
|
||||
| (Psie::Mbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 480 << PROTO_SPEED_PSIM_SHIFT
|
||||
| 3 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 3 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// SuperSpeed Gen1 x1
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -546,7 +558,7 @@ impl Xhci {
|
||||
| (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 5 << PROTO_SPEED_PSIM_SHIFT
|
||||
| (Lp::SuperSpeed as u32) << PROTO_SPEED_LP_SHIFT
|
||||
| 4 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 4 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// SuperSpeedPlus Gen2 x1
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -555,7 +567,7 @@ impl Xhci {
|
||||
| (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 10 << PROTO_SPEED_PSIM_SHIFT
|
||||
| (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT
|
||||
| 5 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 5 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// SuperSpeedPlus Gen1 x2
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -564,7 +576,7 @@ impl Xhci {
|
||||
| (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 10 << PROTO_SPEED_PSIM_SHIFT
|
||||
| (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT
|
||||
| 6 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 6 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
// SuperSpeedPlus Gen2 x2
|
||||
ProtocolSpeed::from_raw(
|
||||
@@ -573,10 +585,12 @@ impl Xhci {
|
||||
| (Psie::Gbps as u32) << PROTO_SPEED_PSIE_SHIFT
|
||||
| 20 << PROTO_SPEED_PSIM_SHIFT
|
||||
| (Lp::SuperSpeedPlus as u32) << PROTO_SPEED_LP_SHIFT
|
||||
| 7 << PROTO_SPEED_PSIV_SHIFT
|
||||
| 7 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
];
|
||||
let supp_proto = self.supported_protocols_iter().find(|supp_proto| supp_proto.compat_port_range().contains(&port))?;
|
||||
let supp_proto = self
|
||||
.supported_protocols_iter()
|
||||
.find(|supp_proto| supp_proto.compat_port_range().contains(&port))?;
|
||||
|
||||
Some(if supp_proto.psic() != 0 {
|
||||
unsafe { supp_proto.protocol_speeds().iter() }
|
||||
@@ -585,7 +599,8 @@ impl Xhci {
|
||||
})
|
||||
}
|
||||
pub fn lookup_psiv(&self, port: u8, psiv: u8) -> Option<&'static ProtocolSpeed> {
|
||||
self.supported_protocol_speeds(port)?.find(|speed| speed.psiv() == psiv)
|
||||
self.supported_protocol_speeds(port)?
|
||||
.find(|speed| speed.psiv() == psiv)
|
||||
}
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::slice;
|
||||
use std::num::NonZeroU8;
|
||||
use std::slice;
|
||||
use syscall::io::{Io, Mmio};
|
||||
|
||||
use super::CapabilityRegs;
|
||||
|
||||
+303
-72
@@ -1,6 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::io::prelude::*;
|
||||
use std::{cmp, mem, path, str};
|
||||
use std::{cmp, io, mem, path, str};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
@@ -8,9 +8,9 @@ use smallvec::{smallvec, SmallVec};
|
||||
use syscall::io::{Dma, Io};
|
||||
use syscall::scheme::SchemeMut;
|
||||
use syscall::{
|
||||
Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT, ENOSYS,
|
||||
ENOTDIR, ENXIO, EOPNOTSUPP, 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,
|
||||
Error, Result, Stat, EACCES, EBADF, EBADFD, EBADMSG, EEXIST, EINVAL, EIO, EISDIR, ENOENT,
|
||||
ENOSYS, ENOTDIR, ENXIO, EOPNOTSUPP, 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,
|
||||
};
|
||||
|
||||
use super::{port, usb};
|
||||
@@ -33,7 +33,7 @@ use crate::driver_interface::*;
|
||||
pub enum EndpointHandleTy {
|
||||
/// portX/endpoints/Y/transfer. Write calls transfer data to the device, and read calls
|
||||
/// transfer data from the device.
|
||||
Transfer,
|
||||
Transfer(PortTransferState),
|
||||
|
||||
/// portX/endpoints/Y/status
|
||||
Status(usize), // offset
|
||||
@@ -42,10 +42,19 @@ pub enum EndpointHandleTy {
|
||||
Root(usize, Vec<u8>), // offset, content
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum PortTransferState {
|
||||
/// Ready to read or write to do another transfer
|
||||
Ready,
|
||||
|
||||
/// Transfer has completed, and the status has to be read.
|
||||
WaitingForStatusReq(PortTransferStatus),
|
||||
}
|
||||
|
||||
pub enum PortReqState {
|
||||
Init,
|
||||
WaitingForDeviceBytes(Dma<[u8]>, usb::Setup), // buffer, setup params
|
||||
WaitingForHostBytes(Dma<[u8]>, usb::Setup), // buffer, setup params
|
||||
WaitingForHostBytes(Dma<[u8]>, usb::Setup), // buffer, setup params
|
||||
TmpSetup(usb::Setup),
|
||||
Tmp,
|
||||
}
|
||||
@@ -182,11 +191,7 @@ impl Xhci {
|
||||
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.setup(
|
||||
req,
|
||||
TransferKind::NoData,
|
||||
cycle,
|
||||
);
|
||||
cmd.setup(req, TransferKind::NoData, cycle);
|
||||
}
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
@@ -203,10 +208,7 @@ impl Xhci {
|
||||
let control = event.control.read();
|
||||
|
||||
if (status >> 24) != TrbCompletionCode::Success as u32 {
|
||||
println!(
|
||||
"DEVICE_REQ ERROR, COMPLETION CODE {:#0x}",
|
||||
(status >> 24)
|
||||
);
|
||||
println!("DEVICE_REQ ERROR, COMPLETION CODE {:#0x}", (status >> 24));
|
||||
}
|
||||
|
||||
println!(
|
||||
@@ -224,15 +226,56 @@ impl Xhci {
|
||||
fn set_configuration(&mut self, port: usize, config: u8) -> Result<()> {
|
||||
self.device_req_no_data(port, usb::Setup::set_configuration(config))
|
||||
}
|
||||
fn set_interface(&mut self, port: usize, interface_num: u8, alternate_setting: u8) -> Result<()> {
|
||||
self.device_req_no_data(port, usb::Setup::set_interface(interface_num, alternate_setting))
|
||||
fn set_interface(
|
||||
&mut self,
|
||||
port: usize,
|
||||
interface_num: u8,
|
||||
alternate_setting: u8,
|
||||
) -> Result<()> {
|
||||
self.device_req_no_data(
|
||||
port,
|
||||
usb::Setup::set_interface(interface_num, alternate_setting),
|
||||
)
|
||||
}
|
||||
|
||||
fn reset_endpoint(&mut self, port_num: usize, endp_num: u8, tsp: bool) -> Result<()> {
|
||||
let slot = self
|
||||
.port_states
|
||||
.get(&port_num)
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.slot;
|
||||
{
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
cmd.reset_endpoint(slot, endp_num + 1, tsp, cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("RESET_ENDPOINT failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> {
|
||||
let mut req: ConfigureEndpointsReq =
|
||||
serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?;
|
||||
|
||||
if (!self.cap.cic() || !self.op.cie()) && (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None) {
|
||||
if (!self.cap.cic() || !self.op.cie())
|
||||
&& (req.config_desc != 0 || req.interface_desc != None || req.alternate_setting != None)
|
||||
{
|
||||
//return Err(Error::new(EOPNOTSUPP));
|
||||
req.config_desc = 0;
|
||||
req.alternate_setting = None;
|
||||
@@ -259,8 +302,13 @@ impl Xhci {
|
||||
|
||||
let current_slot_a = input_context.device.slot.a.read();
|
||||
|
||||
let endpoints =
|
||||
&port_state.dev_desc.config_descs.get(req.config_desc as usize).ok_or(Error::new(EBADMSG))?.interface_descs[0].endpoints;
|
||||
let endpoints = &port_state
|
||||
.dev_desc
|
||||
.config_descs
|
||||
.get(req.config_desc as usize)
|
||||
.ok_or(Error::new(EBADMSG))?
|
||||
.interface_descs[0]
|
||||
.endpoints;
|
||||
|
||||
if endpoints.len() >= 31 {
|
||||
return Err(Error::new(EIO));
|
||||
@@ -273,7 +321,11 @@ impl Xhci {
|
||||
| ((u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT)
|
||||
& CONTEXT_ENTRIES_MASK),
|
||||
);
|
||||
input_context.control.write((u32::from(req.alternate_setting.unwrap_or(0)) << 16) | (u32::from(req.interface_desc.unwrap_or(0)) << 8) | u32::from(req.config_desc));
|
||||
input_context.control.write(
|
||||
(u32::from(req.alternate_setting.unwrap_or(0)) << 16)
|
||||
| (u32::from(req.interface_desc.unwrap_or(0)) << 8)
|
||||
| u32::from(req.config_desc),
|
||||
);
|
||||
|
||||
let lec = self.cap.lec();
|
||||
|
||||
@@ -425,20 +477,49 @@ impl Xhci {
|
||||
.configuration_value;
|
||||
self.set_configuration(port, configuration_value)?;
|
||||
|
||||
if let (Some(interface_num), Some(alternate_setting)) = (req.interface_desc, req.alternate_setting) {
|
||||
if let (Some(interface_num), Some(alternate_setting)) =
|
||||
(req.interface_desc, req.alternate_setting)
|
||||
{
|
||||
self.set_interface(port, interface_num, alternate_setting)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
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_read(
|
||||
&mut self,
|
||||
port_num: usize,
|
||||
endp_idx: u8,
|
||||
buf: &mut [u8],
|
||||
) -> Result<(u8, 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_idx: u8, buf: &[u8]) -> Result<u32> {
|
||||
self.transfer(port_num, endp_idx, if !buf.is_empty() { DeviceReqData::Out(buf) } else { DeviceReqData::NoData })
|
||||
fn transfer_write(&mut self, port_num: usize, endp_idx: u8, buf: &[u8]) -> Result<(u8, 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, mut buf: DeviceReqData) -> Result<u32> {
|
||||
fn transfer(
|
||||
&mut self,
|
||||
port_num: usize,
|
||||
endp_idx: u8,
|
||||
mut buf: DeviceReqData,
|
||||
) -> Result<(u8, u32)> {
|
||||
// TODO: Check that only readable enpoints are read, etc.
|
||||
let endp_num = endp_idx + 1;
|
||||
let xhc_endp_num = endp_num + 1;
|
||||
// TODO: Check that buf has a nonzero size, otherwise (at least for Rust's GlobalAlloc),
|
||||
@@ -455,8 +536,21 @@ impl Xhci {
|
||||
DeviceReqData::NoData => None,
|
||||
};
|
||||
|
||||
let port_state = self.port_states.get_mut(&port_num).ok_or(Error::new(EBADFD))?;
|
||||
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(EBADFD))?;
|
||||
let port_state = self
|
||||
.port_states
|
||||
.get_mut(&port_num)
|
||||
.ok_or(Error::new(EBADFD))?;
|
||||
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(EBADFD))?;
|
||||
|
||||
if endp_desc.is_isoch() {
|
||||
return Err(Error::new(ENOSYS));
|
||||
@@ -467,11 +561,19 @@ impl Xhci {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let endp_state = port_state.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?;
|
||||
let endp_state = port_state
|
||||
.endpoint_states
|
||||
.get_mut(&endp_num)
|
||||
.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::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.
|
||||
@@ -485,42 +587,72 @@ impl Xhci {
|
||||
bytes[..len as usize].copy_from_slice(&sbuf[..len as usize]);
|
||||
// FIXME: little endian, right?
|
||||
(u64::from_le_bytes(bytes), true)
|
||||
}).unwrap_or((0, false))
|
||||
})
|
||||
.unwrap_or((0, false))
|
||||
} else {
|
||||
(dma_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0) as u64, false)
|
||||
(
|
||||
dma_buffer.as_ref().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);
|
||||
trb.normal(
|
||||
buffer,
|
||||
len,
|
||||
cycle,
|
||||
estimated_td_size,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
idt,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
let stream_id = 1u16;
|
||||
self.dbs[port_state.slot as usize].write(u32::from(xhc_endp_num) | (u32::from(stream_id) << 16));
|
||||
self.dbs[port_state.slot as usize]
|
||||
.write(u32::from(xhc_endp_num) | (u32::from(stream_id) << 16));
|
||||
|
||||
let bytes_transferred = {
|
||||
let (completion_code, 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::ShortPacket as u8
|
||||
&& event.transfer_length() != 0
|
||||
{
|
||||
println!(
|
||||
"Event trb didn't yield a short packet, but some bytes were not 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());
|
||||
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()
|
||||
(
|
||||
event.completion_code(),
|
||||
u32::from(len) - event.transfer_length(),
|
||||
)
|
||||
};
|
||||
|
||||
if let DeviceReqData::In(dbuf) = &mut buf {
|
||||
dbuf.copy_from_slice(&*dma_buffer.as_ref().unwrap());
|
||||
}
|
||||
|
||||
Ok(bytes_transferred)
|
||||
Ok((completion_code, bytes_transferred as u32))
|
||||
}
|
||||
pub(crate) fn get_dev_desc(&mut self, port_id: usize) -> Result<DevDesc> {
|
||||
let st = self
|
||||
@@ -689,7 +821,13 @@ impl Xhci {
|
||||
|
||||
bytes_to_read
|
||||
}
|
||||
fn port_req_transfer(&mut self, port_num: usize, data_buffer: Option<&mut Dma<[u8]>>, setup: usb::Setup, transfer_kind: TransferKind) -> Result<()> {
|
||||
fn port_req_transfer(
|
||||
&mut self,
|
||||
port_num: usize,
|
||||
data_buffer: Option<&mut Dma<[u8]>>,
|
||||
setup: usb::Setup,
|
||||
transfer_kind: TransferKind,
|
||||
) -> Result<()> {
|
||||
// TODO: This json format might be too high level, but is useful for debugging,
|
||||
// but when actual device-specific drivers are written, a binary format would
|
||||
// be better. Maybe something simple like bincode could be used, if a custom binary struct
|
||||
@@ -786,7 +924,13 @@ impl Xhci {
|
||||
// FIXME: Make sure there aren't any other PortReq handles, perhaps by storing the state in
|
||||
// PortState?
|
||||
}
|
||||
fn handle_port_req_write(&mut self, fd: usize, port_num: usize, mut st: PortReqState, buf: &[u8]) -> Result<usize> {
|
||||
fn handle_port_req_write(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
port_num: usize,
|
||||
mut st: PortReqState,
|
||||
buf: &[u8],
|
||||
) -> Result<usize> {
|
||||
let bytes_written = match st {
|
||||
PortReqState::Init => {
|
||||
let req = serde_json::from_slice::<PortReq>(buf).or(Err(Error::new(EBADMSG)))?;
|
||||
@@ -821,7 +965,13 @@ impl Xhci {
|
||||
}
|
||||
Ok(bytes_written)
|
||||
}
|
||||
fn handle_port_req_read(&mut self, fd: usize, port_num: usize, mut st: PortReqState, buf: &mut [u8]) -> Result<usize> {
|
||||
fn handle_port_req_read(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
port_num: usize,
|
||||
mut st: PortReqState,
|
||||
buf: &mut [u8],
|
||||
) -> Result<usize> {
|
||||
let bytes_read = match st {
|
||||
PortReqState::WaitingForDeviceBytes(mut dma_buffer, setup) => {
|
||||
if buf.len() != dma_buffer.len() {
|
||||
@@ -834,7 +984,9 @@ impl Xhci {
|
||||
|
||||
buf.len()
|
||||
}
|
||||
PortReqState::Init | PortReqState::WaitingForHostBytes(_, _) => return Err(Error::new(EBADF)),
|
||||
PortReqState::Init | PortReqState::WaitingForHostBytes(_, _) => {
|
||||
return Err(Error::new(EBADF))
|
||||
}
|
||||
PortReqState::Tmp | PortReqState::TmpSetup(_) => unreachable!(),
|
||||
};
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
@@ -843,6 +995,84 @@ impl Xhci {
|
||||
}
|
||||
Ok(bytes_read)
|
||||
}
|
||||
fn completion_code_to_status(
|
||||
completion_code: u8,
|
||||
bytes_transferred: u16,
|
||||
) -> PortTransferStatus {
|
||||
if completion_code == TrbCompletionCode::Success as u8 {
|
||||
PortTransferStatus::Success
|
||||
} else if completion_code == TrbCompletionCode::ShortPacket as u8 {
|
||||
PortTransferStatus::ShortPacket(bytes_transferred as u16)
|
||||
} else if completion_code == TrbCompletionCode::Stall as u8 {
|
||||
PortTransferStatus::Stalled
|
||||
} else {
|
||||
PortTransferStatus::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_transfer_write(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
port_num: usize,
|
||||
endp_num: u8,
|
||||
mut st: PortTransferState,
|
||||
buf: &[u8],
|
||||
) -> Result<usize> {
|
||||
let bytes_written = match st {
|
||||
PortTransferState::Ready => {
|
||||
let (completion_code, bytes_written) =
|
||||
self.transfer_write(port_num, endp_num - 1, buf)?;
|
||||
st = PortTransferState::WaitingForStatusReq(Self::completion_code_to_status(
|
||||
completion_code,
|
||||
bytes_written as u16,
|
||||
));
|
||||
bytes_written
|
||||
}
|
||||
PortTransferState::WaitingForStatusReq(_) => return Err(Error::new(EBADF)),
|
||||
};
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Endpoint(_, _, EndpointHandleTy::Transfer(ref mut state)) => *state = st,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(bytes_written as usize)
|
||||
}
|
||||
fn handle_transfer_read(
|
||||
&mut self,
|
||||
fd: usize,
|
||||
port_num: usize,
|
||||
endp_num: u8,
|
||||
mut st: PortTransferState,
|
||||
mut buf: &mut [u8],
|
||||
) -> Result<usize> {
|
||||
let bytes_read = match st {
|
||||
PortTransferState::Ready => {
|
||||
let (completion_code, bytes_transferred) =
|
||||
self.transfer_read(port_num, endp_num - 1, buf)?;
|
||||
// TODO: Perhaps transfers with large buffers could be broken up to smaller
|
||||
// transfers.
|
||||
st = PortTransferState::WaitingForStatusReq(Self::completion_code_to_status(
|
||||
completion_code,
|
||||
bytes_transferred as u16,
|
||||
));
|
||||
bytes_transferred as usize
|
||||
}
|
||||
PortTransferState::WaitingForStatusReq(status) => {
|
||||
// Use a cursor to count the number of bytes written
|
||||
let mut cursor = io::Cursor::new(buf);
|
||||
serde_json::to_writer(&mut cursor, &status).or(Err(Error::new(EIO)))?;
|
||||
st = PortTransferState::Ready;
|
||||
|
||||
cursor
|
||||
.seek(io::SeekFrom::Current(0))
|
||||
.or(Err(Error::new(EIO)))? as usize
|
||||
}
|
||||
};
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Endpoint(_, _, EndpointHandleTy::Transfer(ref mut state)) => *state = st,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(bytes_read)
|
||||
}
|
||||
}
|
||||
|
||||
impl SchemeMut for Xhci {
|
||||
@@ -982,11 +1212,7 @@ impl SchemeMut for Xhci {
|
||||
|
||||
let port_state = self.port_states.get(&port_num).ok_or(Error::new(ENOENT))?;
|
||||
|
||||
if port_state
|
||||
.endpoint_states
|
||||
.get(&endpoint_num)
|
||||
.is_none()
|
||||
{
|
||||
if port_state.endpoint_states.get(&endpoint_num).is_none() {
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
|
||||
@@ -1004,17 +1230,23 @@ impl SchemeMut for Xhci {
|
||||
// endpoint.
|
||||
return Err(Error::new(ENOENT));
|
||||
}
|
||||
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 - 1).ok_or(Error::new(ENOENT))?;
|
||||
match endp_desc.direction() {
|
||||
EndpDirection::Out => if flags & O_RDWR != O_WRONLY && flags & O_STAT != 0 {
|
||||
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 - 1)
|
||||
.ok_or(Error::new(ENOENT))?;
|
||||
if let EndpDirection::In = endp_desc.direction() {
|
||||
if flags & O_RDWR != O_RDONLY && 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
|
||||
EndpointHandleTy::Transfer(PortTransferState::Ready)
|
||||
}
|
||||
_ => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
@@ -1064,15 +1296,19 @@ impl SchemeMut for Xhci {
|
||||
stat.st_mode = MODE_FILE;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _)) | Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => {
|
||||
Handle::PortReq(_, PortReqState::WaitingForDeviceBytes(ref buf, _))
|
||||
| Handle::PortReq(_, PortReqState::WaitingForHostBytes(ref buf, _)) => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = buf.len() as u64;
|
||||
}
|
||||
Handle::PortReq(_, PortReqState::Tmp) | Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(),
|
||||
Handle::PortReq(_, PortReqState::Tmp)
|
||||
| Handle::PortReq(_, PortReqState::TmpSetup(_)) => unreachable!(),
|
||||
|
||||
Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR,
|
||||
Handle::Endpoint(_, _, st) => match st {
|
||||
EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer => stat.st_mode = MODE_CHR,
|
||||
EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer(_) => {
|
||||
stat.st_mode = MODE_CHR
|
||||
}
|
||||
EndpointHandleTy::Root(_, ref buf) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = buf.len() as u64;
|
||||
@@ -1107,7 +1343,7 @@ impl SchemeMut for Xhci {
|
||||
match st {
|
||||
EndpointHandleTy::Root(_, _) => "",
|
||||
EndpointHandleTy::Status(_) => "status",
|
||||
EndpointHandleTy::Transfer => "transfer",
|
||||
EndpointHandleTy::Transfer(_) => "transfer",
|
||||
}
|
||||
)
|
||||
.unwrap(),
|
||||
@@ -1172,11 +1408,8 @@ impl SchemeMut for Xhci {
|
||||
Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)),
|
||||
|
||||
&mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st {
|
||||
EndpointHandleTy::Transfer => {
|
||||
self.transfer_read(port_num, endp_num - 1, buf)?;
|
||||
// TODO: Perhaps transfers with large buffers could be broken up a to smaller
|
||||
// transfers.
|
||||
Ok(buf.len())
|
||||
&mut EndpointHandleTy::Transfer(state) => {
|
||||
self.handle_transfer_read(fd, port_num, endp_num, state, buf)
|
||||
}
|
||||
EndpointHandleTy::Status(ref mut offset) => {
|
||||
let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?;
|
||||
@@ -1245,10 +1478,8 @@ impl SchemeMut for Xhci {
|
||||
self.configure_endpoints(port_num, buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
&mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer) => {
|
||||
self.transfer_write(port_num, endp_num - 1, buf)?;
|
||||
// TODO
|
||||
Ok(buf.len())
|
||||
&mut Handle::Endpoint(port_num, endp_num, EndpointHandleTy::Transfer(state)) => {
|
||||
self.handle_transfer_write(fd, port_num, endp_num, state, buf)
|
||||
}
|
||||
&mut Handle::PortReq(port_num, ref mut st) => {
|
||||
let state = std::mem::replace(st, PortReqState::Tmp);
|
||||
|
||||
+55
-8
@@ -154,10 +154,15 @@ impl Trb {
|
||||
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 }
|
||||
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
|
||||
((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
|
||||
@@ -205,8 +210,39 @@ impl Trb {
|
||||
0,
|
||||
(u32::from(slot_id) << 24)
|
||||
| ((TrbType::ConfigureEndpoint as u32) << 10)
|
||||
| (cycle as u32),
|
||||
)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) {
|
||||
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
|
||||
self.set(
|
||||
0,
|
||||
0,
|
||||
(u32::from(slot_id) << 24)
|
||||
| (u32::from(endp_num_xhc) << 16)
|
||||
| ((TrbType::ResetEndpoint as u32) << 10)
|
||||
| (u32::from(tsp) << 9)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
pub fn stop_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, suspend: bool, cycle: bool) {
|
||||
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
|
||||
self.set(
|
||||
0,
|
||||
0,
|
||||
(u32::from(slot_id) << 24)
|
||||
| (u32::from(suspend) << 23)
|
||||
| (u32::from(endp_num_xhc) << 16)
|
||||
| ((TrbType::StopEndpoint as u32) << 10)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
pub fn reset_device(&mut self, slot_id: u8, cycle: bool) {
|
||||
self.set(
|
||||
0,
|
||||
0,
|
||||
(u32::from(slot_id) << 24) | ((TrbType::ResetDevice as u32) << 10) | u32::from(cycle),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn setup(&mut self, setup: usb::Setup, transfer: TransferKind, cycle: bool) {
|
||||
@@ -238,13 +274,24 @@ 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) {
|
||||
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(len) | (u32::from(estimated_td_size) << 17),
|
||||
u32::from(cycle)
|
||||
| (u32::from(ent) << 1)
|
||||
| (u32::from(isp) << 2)
|
||||
@@ -252,7 +299,7 @@ impl Trb {
|
||||
| (u32::from(ioc) << 5)
|
||||
| (u32::from(idt) << 6)
|
||||
| (u32::from(bei) << 9)
|
||||
| ((TrbType::Normal as u32) << 10)
|
||||
| ((TrbType::Normal as u32) << 10),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user