Simplify the endpoint interface.

This commit is contained in:
4lDO2
2020-02-11 23:07:20 +01:00
parent 127ef077c2
commit dd9124eb82
7 changed files with 331 additions and 318 deletions
+7 -4
View File
@@ -47,8 +47,11 @@ fn main() {
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(&inquiry) }, DeviceReqData::In(&mut buffer)).expect("Failed to send command");
let mut buffer = [0u8; 5];
let mut command_buffer = [0u8; 6];
{
let mut inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap();
*inquiry = scsi::cmds::Inquiry::new(false, 0, 5, 0);
}
protocol.send_command(&command_buffer, DeviceReqData::In(&mut buffer)).expect("Failed to send command");
}
+39 -38
View File
@@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::{io, slice};
use thiserror::Error;
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpStatusHandle, XhciEndpTransferHandle};
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle};
use super::{Protocol, ProtocolError};
@@ -77,10 +77,8 @@ impl CommandStatusWrapper {
pub struct BulkOnlyTransport<'a> {
handle: &'a XhciClientHandle,
bulk_in: XhciEndpTransferHandle,
bulk_out: XhciEndpTransferHandle,
bulk_in_status: XhciEndpStatusHandle,
bulk_out_status: XhciEndpStatusHandle,
bulk_in: XhciEndpHandle,
bulk_out: XhciEndpHandle,
bulk_in_num: u8,
bulk_out_num: u8,
max_lun: u8,
@@ -103,10 +101,8 @@ impl<'a> BulkOnlyTransport<'a> {
println!("BOT_MAX_LUN {}", max_lun);
Ok(Self {
bulk_in: handle.open_endpoint(bulk_in_num, PortReqDirection::DeviceToHost)?,
bulk_out: handle.open_endpoint(bulk_out_num, PortReqDirection::HostToDevice)?,
bulk_in_status: handle.open_endpoint_status(bulk_in_num)?,
bulk_out_status: handle.open_endpoint_status(bulk_out_num)?,
bulk_in: handle.open_endpoint(bulk_in_num)?,
bulk_out: handle.open_endpoint(bulk_out_num)?,
bulk_in_num,
bulk_out_num,
handle,
@@ -115,15 +111,20 @@ impl<'a> BulkOnlyTransport<'a> {
interface_num: if_desc.number,
})
}
fn clear_stall(&mut self, endp_num: u8) -> Result<(), XhciClientHandleError> {
self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(endp_num), FEATURE_ENDPOINT_HALT)
fn clear_stall_in(&mut self) -> Result<(), XhciClientHandleError> {
self.bulk_in.reset(false);
self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(self.bulk_in_num), FEATURE_ENDPOINT_HALT)
}
fn clear_stall_out(&mut self) -> Result<(), XhciClientHandleError> {
self.bulk_out.reset(false);
self.handle.clear_feature(PortReqRecipient::Endpoint, u16::from(self.bulk_out_num), FEATURE_ENDPOINT_HALT)
}
fn reset_recovery(&mut self) -> Result<(), ProtocolError> {
bulk_only_mass_storage_reset(self.handle, self.interface_num.into())?;
self.clear_stall(self.bulk_in_num.into())?;
self.clear_stall(self.bulk_out_num.into())?;
self.clear_stall_in()?;
self.clear_stall_out()?;
if self.bulk_in_status.current_status()? == EndpointStatus::Halted || self.bulk_out_status.current_status()? == EndpointStatus::Halted {
if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted {
return Err(ProtocolError::RecoveryFailed)
}
Ok(())
@@ -135,25 +136,23 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
self.current_tag += 1;
let tag = self.current_tag;
let mut command_block = [0u8; 16];
if cb.len() > 16 {
return Err(ProtocolError::TooLargeCommandBlock(cb.len()));
}
command_block[..cb.len()].copy_from_slice(&cb);
println!("{}", base64::encode(cb));
println!();
let cbw = CommandBlockWrapper {
signature: CBW_SIGNATURE,
tag,
data_transfer_len: data.len() as u32,
lun: 0, // TODO
flags: u8::from(data.direction() == PortReqDirection::DeviceToHost) << 7,
cb_len: cb.len().try_into().or(Err(ProtocolError::TooLargeCommandBlock(cb.len())))?,
command_block,
};
match self.bulk_out.transfer_write(unsafe { plain::as_bytes(&cbw) })? {
let mut cbw_bytes = [0u8; 31];
let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes).unwrap();
*cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?;
println!("{}", base64::encode(&cbw_bytes));
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
match self.bulk_out.transfer_write(&cbw_bytes)? {
PortTransferStatus::ShortPacket(31) => (),
PortTransferStatus::Stalled => {
panic!("bulk out endpoint stalled when sending CBW");
println!("bulk out endpoint stalled when sending CBW");
self.reset_recovery()?;
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
}
_ => panic!("invalid number of CBW bytes written; expected a short packed of length 31 (0x1F)"),
}
@@ -165,7 +164,7 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
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)?;
self.clear_stall_in()?;
}
PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))),
};
@@ -173,27 +172,29 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
}
DeviceReqData::Out(ref buffer) => todo!(),
DeviceReqData::NoData => todo!(),
};
}
let mut csw = CommandStatusWrapper::default();
let mut csw_buffer = [0u8; 13];
match self.bulk_in.transfer_read(unsafe { plain::as_mut_bytes(&mut csw) })? {
match self.bulk_in.transfer_read(&mut csw_buffer)? {
PortTransferStatus::ShortPacket(13) => (),
PortTransferStatus::Stalled => {
println!("bulk in endpoint stalled when reading CSW");
self.clear_stall(self.bulk_in_num)?;
self.clear_stall_in()?;
}
_ => panic!("invalid number of CSW bytes read; expected a short packet of length 13 (0xD)"),
};
println!("{}", base64::encode(&csw_buffer));
let csw = plain::from_bytes::<CommandStatusWrapper>(&csw_buffer).unwrap();
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 {
if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted {
println!("Trying to recover from stall");
dbg!(self.bulk_in_status.current_status()?, self.bulk_out_status.current_status()?);
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
}
Ok(())
@@ -204,7 +205,7 @@ pub fn bulk_only_mass_storage_reset(handle: &XhciClientHandle, if_num: u16) -> R
handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFF, 0, if_num, DeviceReqData::NoData)
}
pub fn get_max_lun(handle: &XhciClientHandle, if_num: u16) -> Result<u8, XhciClientHandleError> {
let mut lun = 0;
let mut lun = 0u8;
let buffer = slice::from_mut(&mut lun);
handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFE, 0, if_num, DeviceReqData::In(buffer))?;
Ok(lun)
+6
View File
@@ -13,6 +13,8 @@ pub struct ReportIdentInfo {
pub info_ty: u8,
pub control: u8,
}
unsafe impl plain::Plain for ReportIdentInfo {}
impl ReportIdentInfo {
pub fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self {
Self {
@@ -52,6 +54,8 @@ pub struct ReportSuppOpcodes {
pub _rsvd: u8,
pub control: u8,
}
unsafe impl plain::Plain for ReportSuppOpcodes {}
impl ReportSuppOpcodes {
pub const fn new(rep_opts: ReportSuppOpcodesOptions, rctd: bool, req_opcode: u8, req_serviceaction: u16, alloc_len: u32, control: u8) -> Self {
Self {
@@ -115,6 +119,7 @@ pub struct CommandDescriptor {
/// little endian
pub cdb_len: u16,
}
#[repr(packed)]
pub struct OneCommandParam {
pub _rsvd: u8,
@@ -133,6 +138,7 @@ pub struct Inquiry {
pub alloc_len: u16,
pub control: u8,
}
unsafe impl plain::Plain for Inquiry {}
impl Inquiry {
pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self {
+115 -88
View File
@@ -79,6 +79,16 @@ pub enum EndpBinaryDirection {
Out,
In,
}
impl From<PortReqDirection> for EndpBinaryDirection {
fn from(d: PortReqDirection) -> Self {
match d {
PortReqDirection::DeviceToHost => Self::In,
PortReqDirection::HostToDevice => Self::Out,
}
}
}
impl From<EndpBinaryDirection> for EndpDirection {
fn from(b: EndpBinaryDirection) -> Self {
match b {
@@ -305,7 +315,7 @@ impl str::FromStr for PortState {
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum EndpointStatus {
Disabled,
Enabled,
@@ -411,48 +421,28 @@ impl XhciClientHandle {
let string = std::fs::read_to_string(path)?;
Ok(string.parse()?)
}
pub fn endpoint_onetime_status(
pub fn open_endpoint_ctl(
&self,
num: u8,
) -> result::Result<EndpointStatus, XhciClientHandleError> {
let path = format!("{}:port{}/endpoints/{}/status", self.scheme, self.port, num);
let string = std::fs::read_to_string(path)?;
Ok(string.parse()?)
) -> result::Result<File, XhciClientHandleError> {
let path = format!("{}:port{}/endpoints/{}/ctl", self.scheme, self.port, num);
Ok(File::open(path)?)
}
pub fn open_endpoint_status(
pub fn open_endpoint_data(
&self,
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)?,
))
}
pub fn open_endpoint(
&self,
num: u8,
direction: PortReqDirection,
) -> result::Result<XhciEndpTransferHandle, XhciClientHandleError> {
) -> result::Result<File, XhciClientHandleError> {
let path = format!(
"{}:port{}/endpoints/{}/transfer",
"{}:port{}/endpoints/{}/data",
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)?,
}))
Ok(File::open(path)?)
}
pub fn open_endpoint(&self, num: u8) -> result::Result<XhciEndpHandle, XhciClientHandleError> {
Ok(XhciEndpHandle {
ctl: self.open_endpoint_ctl(num)?,
data: self.open_endpoint_data(num)?,
})
}
pub fn device_request<'a>(
&self,
@@ -546,68 +536,105 @@ impl XhciClientHandle {
}
#[derive(Debug)]
pub struct XhciEndpStatusHandle(File);
impl XhciEndpStatusHandle {
pub fn current_status(&mut self) -> result::Result<EndpointStatus, XhciClientHandleError> {
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("non-utf8 endpoint state")),
))?;
Ok(status
.parse::<EndpointStatus>()
.or(Err(XhciClientHandleError::InvalidResponse(Invalid(
"malformed endpoint state",
))))?)
}
pub fn into_inner(self) -> File {
self.0
}
pub struct XhciEndpHandle {
data: File,
ctl: File,
}
#[derive(Debug)]
pub struct XhciEndpTransferHandle(File);
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum XhciEndpCtlDirection {
Out,
In,
NoData,
}
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)?;
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum XhciEndpCtlReq {
// TODO: Reduce the number of direction enums from 5 to perhaps 2.
// TODO: Allow to send multiple buffers in one transfer.
/// Tells xhcid that a buffer is about to be sent from the Data interface file, to the
/// endpoint.
Transfer(XhciEndpCtlDirection),
// TODO: Allow clients to specify what to reset.
let status = serde_json::from_slice(&status_buf[..status_bytes_read])?;
/// Tells xhcid that the endpoint is going to be reset.
Reset {
/// Only issue the Reset Endpoint and Set TR Dequeue Pointer commands, and let the client
/// itself send a potential ClearFeature(ENDPOINT_HALT).
no_clear_feature: bool,
},
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")));
}
/// Tells xhcid that the endpoint status is going to be retrieved from the Ctl interface file.
Status,
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum XhciEndpCtlRes {
/// Xhcid responded with the current state of an endpoint.
Status(EndpointStatus),
/// Xhci sent the result of a transfer.
TransferResult(PortTransferStatus),
/// Xhcid is waiting for data to be sent or received on the Data interface file.
Pending,
/// No Ctl request is currently being processed by xhcid.
Idle,
}
impl XhciEndpHandle {
fn ctl_req(&mut self, ctl_req: &XhciEndpCtlReq) -> result::Result<(), XhciClientHandleError> {
let ctl_buffer = serde_json::to_vec(ctl_req)?;
let ctl_bytes_written = self.ctl.write(&ctl_buffer)?;
if ctl_bytes_written != ctl_buffer.len() {
return Err(Invalid("xhcid didn't process all of the ctl bytes").into());
}
Ok(status)
Ok(())
}
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)
fn ctl_res(&mut self) -> result::Result<XhciEndpCtlRes, XhciClientHandleError> {
// a response must never exceed 256 bytes
let mut ctl_buffer = [0u8; 256];
let ctl_bytes_read = self.ctl.read(&mut ctl_buffer)?;
let ctl_res = serde_json::from_slice(&ctl_buffer[..ctl_bytes_read as usize])?;
Ok(ctl_res)
}
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 reset(&mut self, no_clear_feature: bool) -> result::Result<(), XhciClientHandleError> {
self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature })
}
pub fn into_inner(self) -> File {
self.0
pub fn status(&mut self) -> result::Result<EndpointStatus, XhciClientHandleError> {
self.ctl_req(&XhciEndpCtlReq::Status)?;
match self.ctl_res()? {
XhciEndpCtlRes::Status(s) => Ok(s),
_ => Err(Invalid("expected status response").into()),
}
}
fn generic_transfer<F: FnOnce(&mut File) -> io::Result<usize>>(&mut self, direction: XhciEndpCtlDirection, f: F, expected_len: usize) -> result::Result<PortTransferStatus, XhciClientHandleError> {
let req = XhciEndpCtlReq::Transfer(XhciEndpCtlDirection::Out);
self.ctl_req(&req)?;
let bytes_read = f(&mut self.data)?;
let res = self.ctl_res()?;
match res {
XhciEndpCtlRes::TransferResult(PortTransferStatus::Success) if bytes_read != expected_len => Err(Invalid("no short packet, but fewer bytes were read/written").into()),
XhciEndpCtlRes::TransferResult(r) => Ok(r),
_ => Err(Invalid("expected transfer result").into()),
}
}
pub fn transfer_write(&mut self, buf: &[u8]) -> result::Result<PortTransferStatus, XhciClientHandleError> {
self.generic_transfer(XhciEndpCtlDirection::Out, |data| data.write(buf), buf.len())
}
pub fn transfer_read(&mut self, buf: &mut [u8]) -> result::Result<PortTransferStatus, XhciClientHandleError> {
let len = buf.len();
self.generic_transfer(XhciEndpCtlDirection::In, |data| data.read(buf), len)
}
pub fn transfer_nodata(&mut self, buf: &[u8]) -> result::Result<PortTransferStatus, XhciClientHandleError> {
self.generic_transfer(XhciEndpCtlDirection::NoData, |_| Ok(0), buf.len())
}
}
+11 -10
View File
@@ -35,6 +35,8 @@ use self::ring::Ring;
use self::runtime::{Interrupter, RuntimeRegs};
use self::trb::{TransferKind, TrbCompletionCode, TrbType};
use self::scheme::EndpIfState;
use crate::driver_interface::*;
struct Device<'a> {
@@ -151,14 +153,14 @@ pub(crate) enum RingOrStreams {
Streams(StreamContextArray),
}
pub(crate) enum EndpointState {
Ready(RingOrStreams),
Init,
pub(crate) struct EndpointState {
pub transfer: RingOrStreams,
pub driver_if_state: EndpIfState,
}
impl EndpointState {
fn ring(&mut self) -> Option<&mut Ring> {
match self {
Self::Ready(RingOrStreams::Ring(ring)) => Some(ring),
match self.transfer {
RingOrStreams::Ring(ref mut ring) => Some(ring),
_ => None,
}
}
@@ -294,10 +296,6 @@ impl Xhci {
self.dbs[0].write(0);
println!(" - XHCI initialized");
for (pointer, capability) in self.capabilities_iter() {
dbg!(pointer, capability);
}
}
pub fn enable_port_slot(cmd: &mut CommandRing, dbs: &mut [Doorbell]) -> u8 {
@@ -365,7 +363,10 @@ impl Xhci {
dev_desc,
endpoint_states: std::iter::once((
0,
EndpointState::Ready(RingOrStreams::Ring(ring)),
EndpointState {
transfer: RingOrStreams::Ring(ring),
driver_if_state: EndpIfState::Init,
}
))
.collect::<BTreeMap<_, _>>(),
};
+150 -176
View File
@@ -30,14 +30,21 @@ use super::usb::endpoint::{EndpointTy, ENDP_ATTR_TY_MASK};
use crate::driver_interface::*;
#[derive(Clone, Copy, Debug)]
pub enum EndpIfState {
Init,
WaitingForDataPipe(XhciEndpCtlDirection),
WaitingForStatus,
WaitingForTransferResult(PortTransferStatus),
}
/// Subdirs of an endpoint
pub enum EndpointHandleTy {
/// portX/endpoints/Y/transfer. Write calls transfer data to the device, and read calls
/// transfer data from the device.
Transfer(PortTransferState),
/// portX/endpoints/Y/data. Allows clients to read and write data associated with ctl requests.
Data,
/// portX/endpoints/Y/status
Status(usize), // offset
Ctl,
/// portX/endpoints/Y/
Root(usize, Vec<u8>), // offset, content
@@ -469,7 +476,7 @@ impl Xhci {
);
port_state.endpoint_states.insert(
endp_num,
EndpointState::Ready(super::RingOrStreams::Streams(array)),
EndpointState { transfer: super::RingOrStreams::Streams(array), driver_if_state: EndpIfState::Init },
);
array_ptr
@@ -484,7 +491,7 @@ impl Xhci {
);
port_state.endpoint_states.insert(
endp_num,
EndpointState::Ready(super::RingOrStreams::Ring(ring)),
EndpointState { transfer: super::RingOrStreams::Ring(ring), driver_if_state: EndpIfState::Init },
);
ring_ptr
};
@@ -627,7 +634,6 @@ impl Xhci {
}
if EndpDirection::from(buf.direction()) != endp_desc.direction() {
dbg!(buf.direction(), endp_desc, endp_idx, endp_num);
return Err(Error::new(EBADF));
}
@@ -637,14 +643,13 @@ impl Xhci {
.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)) => {
EndpointState { transfer: super::RingOrStreams::Ring(ref mut ring), .. } => ring,
EndpointState { transfer: 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)))?;
@@ -671,6 +676,7 @@ impl Xhci {
len,
cycle,
estimated_td_size,
0,
false,
true,
false,
@@ -922,7 +928,7 @@ impl Xhci {
.get_mut(&0)
.ok_or(Error::new(EIO))?
{
EndpointState::Ready(super::RingOrStreams::Ring(ref mut ring)) => ring,
EndpointState { transfer: super::RingOrStreams::Ring(ref mut ring), .. } => ring,
// Control endpoints never use streams
_ => return Err(Error::new(EIO)),
@@ -1074,84 +1080,6 @@ 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 {
@@ -1268,16 +1196,10 @@ impl SchemeMut for Xhci {
/*if self.dev_ctx.contexts[port_state.slot as usize].endpoints.get(endpoint_num as usize).ok_or(Error::new(ENOENT))?.a.read() & 0b111 != 1 {
return Err(Error::new(ENXIO)); // TODO: Find a proper error code for "endpoint not initialized".
}*/
let contents = match port_state
.endpoint_states
.get(&endpoint_num)
.ok_or(Error::new(ENOENT))?
{
EndpointState::Ready { .. } if endpoint_num != 0 => "transfer\nstatus\n",
EndpointState::Init | EndpointState::Ready { .. } => "status\n",
if !port_state.endpoint_states.contains_key(&endpoint_num) {
return Err(Error::new(ENOENT));
}
.as_bytes()
.to_owned();
let contents = "ctl\ndata\n".as_bytes().to_owned();
Handle::Endpoint(port_num, endpoint_num, EndpointHandleTy::Root(0, contents))
}
@@ -1296,37 +1218,8 @@ impl SchemeMut for Xhci {
}
let st = match sub {
"status" => {
// status is read-only
if flags & O_RDWR != O_RDONLY && flags & O_STAT == 0 {
return Err(Error::new(EACCES));
}
EndpointHandleTy::Status(0)
}
"transfer" => {
if endpoint_num == 0 {
// Don't allow user programs to interface directly with the control
// 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))?;
if let EndpDirection::In = endp_desc.direction() {
if flags & O_RDWR != O_RDONLY && flags & O_STAT != 0 {
return Err(Error::new(EACCES));
}
}
EndpointHandleTy::Transfer(PortTransferState::Ready)
}
"ctl" => EndpointHandleTy::Ctl,
"data" => EndpointHandleTy::Data,
_ => return Err(Error::new(ENOENT)),
};
Handle::Endpoint(port_num, endpoint_num, st)
@@ -1385,9 +1278,7 @@ impl SchemeMut for Xhci {
Handle::PortState(_, _) | Handle::PortReq(_, _) => stat.st_mode = MODE_CHR,
Handle::Endpoint(_, _, st) => match st {
EndpointHandleTy::Status(_) | EndpointHandleTy::Transfer(_) => {
stat.st_mode = MODE_CHR
}
EndpointHandleTy::Ctl | EndpointHandleTy::Data => stat.st_mode = MODE_CHR,
EndpointHandleTy::Root(_, ref buf) => {
stat.st_mode = MODE_DIR;
stat.st_size = buf.len() as u64;
@@ -1421,8 +1312,8 @@ impl SchemeMut for Xhci {
endp_num,
match st {
EndpointHandleTy::Root(_, _) => "",
EndpointHandleTy::Status(_) => "status",
EndpointHandleTy::Transfer(_) => "transfer",
EndpointHandleTy::Ctl => "ctl",
EndpointHandleTy::Data => "data",
}
)
.unwrap(),
@@ -1451,13 +1342,11 @@ impl SchemeMut for Xhci {
};
Ok(*offset)
}
// Read-only unknown-length status strings
Handle::Endpoint(_, _, EndpointHandleTy::Status(ref mut offset))
| Handle::PortState(_, ref mut offset) => {
*offset = match whence {
SEEK_SET => cmp::max(0, pos),
SEEK_CUR => cmp::max(0, *offset + pos),
SEEK_END => return Err(Error::new(ESPIPE)),
Handle::PortState(_, ref mut offset) => {
match whence {
SEEK_SET => *offset = pos,
SEEK_CUR => *offset = pos,
SEEK_END => *offset = pos,
_ => return Err(Error::new(EINVAL)),
};
Ok(*offset)
@@ -1487,39 +1376,9 @@ impl SchemeMut for Xhci {
Handle::ConfigureEndpoints(_) => return Err(Error::new(EBADF)),
&mut Handle::Endpoint(port_num, endp_num, ref mut st) => match st {
&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))?;
let status = self
.dev_ctx
.contexts
.get(ps.slot as usize)
.ok_or(Error::new(EBADF))?
.endpoints
.get(endp_num as usize)
.ok_or(Error::new(EBADF))?
.a
.read()
& ENDPOINT_CONTEXT_STATUS_MASK;
let string = match status {
0 => Some(EndpointStatus::Disabled),
1 => Some(EndpointStatus::Enabled),
2 => Some(EndpointStatus::Halted),
3 => Some(EndpointStatus::Stopped),
4 => Some(EndpointStatus::Error),
_ => None,
}
.as_ref()
.map(EndpointStatus::as_str)
.unwrap_or("unknown")
.as_bytes();
Ok(Self::write_dyn_string(string, buf, offset))
}
EndpointHandleTy::Root(_, _) => unreachable!(),
EndpointHandleTy::Ctl => self.on_read_endp_ctl(port_num, endp_num, buf),
EndpointHandleTy::Data => self.on_read_endp_data(port_num, endp_num, buf),
EndpointHandleTy::Root(_, _) => return Err(Error::new(EBADF)),
},
&mut Handle::PortState(port_num, ref mut offset) => {
let ps = self.port_states.get(&port_num).ok_or(Error::new(EBADF))?;
@@ -1557,8 +1416,10 @@ impl SchemeMut for Xhci {
self.configure_endpoints(port_num, buf)?;
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::Endpoint(port_num, endp_num, ref ep_file_ty) => match ep_file_ty {
EndpointHandleTy::Ctl => self.on_write_endp_ctl(port_num, endp_num, buf),
EndpointHandleTy::Data => self.on_write_endp_data(port_num, endp_num, buf),
_ => return Err(Error::new(EBADF)),
}
&mut Handle::PortReq(port_num, ref mut st) => {
let state = std::mem::replace(st, PortReqState::Tmp);
@@ -1576,3 +1437,116 @@ impl SchemeMut for Xhci {
Ok(0)
}
}
impl Xhci {
pub fn get_endp_status(&mut self, port_num: usize, endp_num: u8) -> Result<EndpointStatus> {
let slot = self.port_states.get(&port_num).ok_or(Error::new(EBADFD))?.slot;
let raw = self.dev_ctx.contexts.get(slot as usize).ok_or(Error::new(EBADFD))?.endpoints.get(endp_num as usize).ok_or(Error::new(EBADFD))?.a.read() & super::context::ENDPOINT_CONTEXT_STATUS_MASK;
Ok(match raw {
0 => EndpointStatus::Disabled,
1 => EndpointStatus::Enabled,
2 => EndpointStatus::Halted,
3 => EndpointStatus::Stopped,
4 => EndpointStatus::Error,
_ => return Err(Error::new(EIO)),
})
}
pub fn on_req_reset_device(&mut self, port_num: usize, endp_num: u8, no_clear_feature: bool) {
}
pub fn on_write_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result<usize> {
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
let req = serde_json::from_slice::<XhciEndpCtlReq>(buf).or(Err(Error::new(EBADMSG)))?;
match req {
XhciEndpCtlReq::Status => match ep_if_state {
state @ EndpIfState::Init => *state = EndpIfState::WaitingForStatus,
_ => return Err(Error::new(EBADF)),
}
XhciEndpCtlReq::Reset { no_clear_feature } => match ep_if_state {
EndpIfState::Init => self.on_req_reset_device(port_num, endp_num, no_clear_feature),
_ => return Err(Error::new(EBADF)),
}
XhciEndpCtlReq::Transfer(direction) => match ep_if_state {
state @ EndpIfState::Init => if direction == XhciEndpCtlDirection::NoData {
// Yield the result directly because no bytes have to be sent or received
// beforehand.
let (completion_code, bytes_transferred) = self.transfer(port_num, endp_num - 1, DeviceReqData::NoData)?;
if bytes_transferred > 0 { return Err(Error::new(EIO)) }
let result = Self::transfer_result(completion_code, 0);
let new_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
*new_state = EndpIfState::WaitingForTransferResult(result)
} else {
*state = EndpIfState::WaitingForDataPipe(direction)
}
_ => return Err(Error::new(EBADF)),
}
_ => return Err(Error::new(ENOSYS)),
}
Ok(buf.len())
}
fn transfer_result(completion_code: u8, bytes_transferred: u32) -> 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
}
}
pub fn on_write_endp_data(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result<usize> {
{
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
match ep_if_state {
state @ EndpIfState::WaitingForDataPipe(XhciEndpCtlDirection::Out) => (),
_ => return Err(Error::new(EBADF)),
}
}
{
let (completion_code, bytes_transferred) = self.transfer_write(port_num, endp_num - 1, buf)?;
let result = Self::transfer_result(completion_code, bytes_transferred);
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
*ep_if_state = EndpIfState::WaitingForTransferResult(result);
Ok(bytes_transferred as usize)
}
}
pub fn on_read_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result<usize> {
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
let res: XhciEndpCtlRes = match ep_if_state {
&mut EndpIfState::Init => XhciEndpCtlRes::Idle,
state @ &mut EndpIfState::WaitingForStatus => {
*state = EndpIfState::Init;
XhciEndpCtlRes::Status(self.get_endp_status(port_num, endp_num)?)
}
&mut EndpIfState::WaitingForDataPipe(_) => XhciEndpCtlRes::Pending,
&mut EndpIfState::WaitingForTransferResult(status) => {
*ep_if_state = EndpIfState::Init;
XhciEndpCtlRes::TransferResult(status)
}
};
let mut cursor = io::Cursor::new(buf);
serde_json::to_writer(&mut cursor, &res).or(Err(Error::new(EIO)))?;
Ok(cursor.seek(io::SeekFrom::Current(0)).unwrap() as usize)
}
pub fn on_read_endp_data(&mut self, port_num: usize, endp_num: u8, buf: &mut [u8]) -> Result<usize> {
{
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
match ep_if_state {
EndpIfState::WaitingForDataPipe(XhciEndpCtlDirection::In) => (),
_ => return Err(Error::new(EBADF)),
}
}
{
let (completion_code, bytes_transferred) = self.transfer_read(port_num, endp_num, buf)?;
let result = Self::transfer_result(completion_code, bytes_transferred);
let ep_if_state = &mut self.port_states.get_mut(&port_num).ok_or(Error::new(EBADF))?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))?.driver_if_state;
*ep_if_state = EndpIfState::WaitingForTransferResult(result);
Ok(bytes_transferred as usize)
}
}
}
+3 -2
View File
@@ -297,6 +297,7 @@ impl Trb {
len: u16,
cycle: bool,
estimated_td_size: u8,
interrupter: u8,
ent: bool,
isp: bool,
chain: bool,
@@ -308,11 +309,11 @@ impl Trb {
// 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(interrupter) << 21),
u32::from(cycle)
| (u32::from(ent) << 1)
| (u32::from(isp) << 2)
| (u32::from(ent) << 4)
| (u32::from(chain) << 4)
| (u32::from(ioc) << 5)
| (u32::from(idt) << 6)
| (u32::from(bei) << 9)