Add basic SCSI error handling.

This commit is contained in:
4lDO2
2020-02-15 23:54:23 +01:00
parent 4d14310e2d
commit 3c9527206f
9 changed files with 98 additions and 99 deletions
+17 -23
View File
@@ -167,27 +167,23 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
self.current_tag += 1;
let tag = self.current_tag;
println!("{}", base64::encode(cb));
println!();
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)?;
let cbw = *cbw;
println!("{}", base64::encode(&cbw_bytes));
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
// TODO: Is this needed?
bulk_only_mass_storage_reset(&self.handle, self.interface_num.into())?;
match self.bulk_out.transfer_write(&cbw_bytes)? {
PortTransferStatus::Stalled => {
println!("bulk out endpoint stalled when sending CBW");
self.clear_stall_out()?;
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
// TODO: Error handling
panic!("bulk out endpoint stalled when sending CBW {:?}", cbw);
//self.clear_stall_out()?;
//dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
}
PortTransferStatus::ShortPacket(n) if n != 31 => {
panic!("received short packet when sending CBW ({} != 31)", n);
//panic!("received short packet when sending CBW ({} != 31)", n);
}
_ => (),
}
@@ -200,8 +196,8 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
panic!("received short packed (len {}) when transferring data", len)
}
PortTransferStatus::Stalled => {
println!("bulk in endpoint stalled when reading data");
self.clear_stall_in()?;
panic!("bulk in endpoint stalled when reading data");
//self.clear_stall_in()?;
}
PortTransferStatus::Unknown => {
return Err(ProtocolError::XhciError(
@@ -211,16 +207,15 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
))
}
};
println!("{}", base64::encode(&buffer[..]));
}
DeviceReqData::Out(buffer) => match self.bulk_out.transfer_write(buffer)? {
PortTransferStatus::Success => (),
PortTransferStatus::ShortPacket(len) => {
panic!("received short packed (len {}) when transferring data", len)
panic!("received short packet (len {}) when transferring data", len)
}
PortTransferStatus::Stalled => {
println!("bulk out endpoint stalled when reading data");
self.clear_stall_out()?;
panic!("bulk out endpoint stalled when reading data");
//self.clear_stall_out()?;
}
PortTransferStatus::Unknown => {
return Err(ProtocolError::XhciError(
@@ -235,28 +230,27 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
match self.bulk_in.transfer_read(&mut csw_buffer)? {
PortTransferStatus::Stalled => {
println!("bulk in endpoint stalled when reading CSW");
self.clear_stall_in()?;
panic!("bulk in endpoint stalled when reading CSW");
//self.clear_stall_in()?;
}
PortTransferStatus::ShortPacket(n) if n != 13 => {
panic!("received a short packet when reading CSW ({} != 13)", n)
}
_ => (),
};
println!("{}", base64::encode(&csw_buffer));
let csw = plain::from_bytes::<CommandStatusWrapper>(&csw_buffer).unwrap();
dbg!(csw);
if !csw.is_valid() || csw.tag != cbw.tag {
self.reset_recovery()?;
panic!("Invald CSW {:?} (for CBW {:?})", csw, cbw);
//self.reset_recovery()?;
}
if self.bulk_in.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()?, self.bulk_out.status()?);
}
}*/
Ok(SendCommandStatus {
kind: if csw.status == CswStatus::Passed as u8 {
+23 -15
View File
@@ -25,8 +25,12 @@ const REQUEST_SENSE_CMD_LEN: u8 = 6;
const MIN_INQUIRY_ALLOC_LEN: u16 = 5;
const MIN_REPORT_SUPP_OPCODES_ALLOC_LEN: u32 = 4;
type Result<T, E = ScsiError> = Result<T, E>;
#[derive(Debug, Error)]
pub enum ScsiError {
// TODO: Add some kind of context here, since it's very useful indeed to be able to see which
// command returned the protocol error.
#[error("protocol error when sending command: {0}")]
ProtocolError(#[from] ProtocolError),
@@ -35,10 +39,13 @@ pub enum ScsiError {
}
impl Scsi {
pub fn new(protocol: &mut dyn Protocol) -> Self {
pub fn new(protocol: &mut dyn Protocol) -> Result<Self> {
assert_eq!(std::mem::size_of::<StandardInquiryData>(), 96);
let mut this = Self {
command_buffer: [0u8; 16],
// separate buffer since the inquiry data is most likely going to be used in the
// future.
inquiry_buffer: [0u8; 259], // additional_len = 255 max
data_buffer: Vec::new(),
block_size: 0,
@@ -46,13 +53,15 @@ impl Scsi {
};
// Get the max length that the device supports, of the Standard Inquiry Data.
let max_inquiry_len = this.get_inquiry_alloc_len(protocol);
let max_inquiry_len = this.get_inquiry_alloc_len(protocol)?;
// Get the Standard Inquiry Data.
this.get_standard_inquiry_data(protocol, max_inquiry_len);
this.res_standard_inquiry_data();
this.get_standard_inquiry_data(protocol, max_inquiry_len)?;
let version = this.res_standard_inquiry_data().version();
println!("Inquiry version: {}", version);
let (block_size, block_count) = {
let (_, blkdescs, mode_page_iter) = this.get_mode_sense10(protocol).unwrap();
let (_, blkdescs, mode_page_iter) = this.get_mode_sense10(protocol)?;
// TODO: Can there be multiple disks at all?
let only_blkdesc = blkdescs.get(0).unwrap();
@@ -62,14 +71,14 @@ impl Scsi {
this.block_size = block_size;
this.block_count = block_count;
this
Ok(this)
}
pub fn get_inquiry_alloc_len(&mut self, protocol: &mut dyn Protocol) -> u16 {
self.get_standard_inquiry_data(protocol, MIN_INQUIRY_ALLOC_LEN);
pub fn get_inquiry_alloc_len(&mut self, protocol: &mut dyn Protocol) -> Result<u16> {
self.get_standard_inquiry_data(protocol, MIN_INQUIRY_ALLOC_LEN)?;
let standard_inquiry_data = self.res_standard_inquiry_data();
4 + u16::from(standard_inquiry_data.additional_len)
Ok(4 + u16::from(standard_inquiry_data.additional_len))
}
pub fn get_standard_inquiry_data(&mut self, protocol: &mut dyn Protocol, max_inquiry_len: u16) {
pub fn get_standard_inquiry_data(&mut self, protocol: &mut dyn Protocol, max_inquiry_len: u16) -> Result<()> {
let inquiry = self.cmd_inquiry();
*inquiry = cmds::Inquiry::new(false, 0, max_inquiry_len, 0);
@@ -77,10 +86,10 @@ impl Scsi {
.send_command(
&self.command_buffer[..INQUIRY_CMD_LEN as usize],
DeviceReqData::In(&mut self.inquiry_buffer[..max_inquiry_len as usize]),
)
.expect("Failed to send INQUIRY command");
)?;
Ok(())
}
pub fn get_ff_sense(&mut self, protocol: &mut dyn Protocol, alloc_len: u8) {
pub fn get_ff_sense(&mut self, protocol: &mut dyn Protocol, alloc_len: u8) -> Result<()> {
let request_sense = self.cmd_request_sense();
*request_sense = cmds::RequestSense::new(false, alloc_len, 0);
self.data_buffer.resize(alloc_len.into(), 0);
@@ -88,8 +97,7 @@ impl Scsi {
.send_command(
&self.command_buffer[..REQUEST_SENSE_CMD_LEN as usize],
DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize]),
)
.expect("Failed to send REQUEST_SENSE command");
)?;
}
pub fn get_mode_sense10(
&mut self,
+14 -14
View File
@@ -4,6 +4,7 @@ pub extern crate smallvec;
use std::convert::TryFrom;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::num::NonZeroU8;
use std::{io, result, str};
use serde::{Deserialize, Serialize};
@@ -176,17 +177,17 @@ impl EndpDesc {
self.is_isoch()
}
}
pub fn max_streams(&self) -> u8 {
pub fn log_max_streams(&self) -> Option<NonZeroU8> {
self.ssc
.as_ref()
.map(|ssc| {
if self.is_bulk() {
1 << (ssc.attributes & 0x1F)
let raw = ssc.attributes & 0x1F;
NonZeroU8::new(raw)
} else {
0
None
}
})
.unwrap_or(0)
}).flatten()
}
pub fn isoch_mult(&self, lec: bool) -> u8 {
if !lec && self.is_isoch() {
@@ -597,9 +598,9 @@ pub enum XhciEndpCtlRes {
impl XhciEndpHandle {
fn ctl_req(&mut self, ctl_req: &XhciEndpCtlReq) -> result::Result<(), XhciClientHandleError> {
let ctl_buffer = serde_json::to_vec(ctl_req).expect("serde");
let ctl_buffer = serde_json::to_vec(ctl_req)?;
let ctl_bytes_written = self.ctl.write(&ctl_buffer).expect("ctlwrite");
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());
}
@@ -609,7 +610,7 @@ impl XhciEndpHandle {
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).expect("ctlread");
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)
@@ -618,9 +619,8 @@ impl XhciEndpHandle {
self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature })
}
pub fn status(&mut self) -> result::Result<EndpointStatus, XhciClientHandleError> {
self.ctl_req(&XhciEndpCtlReq::Status)
.expect("status ctlreq");
match self.ctl_res().expect("status ctlres") {
self.ctl_req(&XhciEndpCtlReq::Status)?;
match self.ctl_res()? {
XhciEndpCtlRes::Status(s) => Ok(s),
_ => Err(Invalid("expected status response").into()),
}
@@ -635,10 +635,10 @@ impl XhciEndpHandle {
direction,
count: expected_len,
};
self.ctl_req(&req).expect("ctl_req");
self.ctl_req(&req)?;
let bytes_read = f(&mut self.data).expect("f");
let res = self.ctl_res().expect("ctl_res");
let bytes_read = f(&mut self.data)?;
let res = self.ctl_res()?;
match res {
XhciEndpCtlRes::TransferResult(PortTransferStatus::Success)
+1 -1
View File
@@ -12,7 +12,7 @@ pub struct CommandRing {
impl CommandRing {
pub fn new() -> Result<CommandRing> {
Ok(CommandRing {
ring: Ring::new(true)?,
ring: Ring::new(16, true)?,
events: EventRing::new()?,
})
}
+1 -1
View File
@@ -141,7 +141,7 @@ impl StreamContextArray {
// NOTE: stream_id 0 is reserved
assert_ne!(stream_id, 0);
let ring = Ring::new(link)?;
let ring = Ring::new(16, link)?;
let pointer = ring.register();
let sct = StreamContextType::PrimaryRing;
+5 -5
View File
@@ -13,19 +13,19 @@ pub struct EventRingSte {
}
pub struct EventRing {
pub ste: Dma<EventRingSte>,
pub ste: Dma<[EventRingSte]>,
pub ring: Ring,
}
impl EventRing {
pub fn new() -> Result<EventRing> {
let mut ring = EventRing {
ste: Dma::zeroed()?,
ring: Ring::new(false)?,
ste: unsafe { Dma::zeroed_unsized(1)? },
ring: Ring::new(32, false)?,
};
ring.ste.address.write(ring.ring.trbs.physical() as u64);
ring.ste.size.write(ring.ring.trbs.len() as u16);
ring.ste[0].address.write(ring.ring.trbs.physical() as u64);
ring.ste[0].size.write(ring.ring.trbs.len() as u16);
Ok(ring)
}
+1 -7
View File
@@ -417,7 +417,7 @@ impl Xhci {
slot: u8,
speed: u8,
) -> Result<Ring> {
let mut ring = Ring::new(true)?;
let mut ring = Ring::new(16, true)?;
{
input_context.add_context.write(1 << 1 | 1); // Enable the slot (zeroth bit) and the control endpoint (first bit).
@@ -505,12 +505,6 @@ impl Xhci {
Ok(ring)
}
pub fn ring_command_doorbell(&mut self) {
self.dbs[0].write(0);
}
pub fn ring_port_doorbell(&mut self, slot: u8, endpoint: u8, stream_id: u16) {
self.dbs[slot as usize].write(u32::from(endpoint) | (u32::from(stream_id) << 16));
}
pub fn trigger_irq(&mut self) -> bool {
// Read the Interrupter Pending bit.
+3 -3
View File
@@ -5,16 +5,16 @@ use super::trb::Trb;
pub struct Ring {
pub link: bool,
pub trbs: Dma<[Trb; 16]>,
pub trbs: Dma<[Trb]>,
pub i: usize,
pub cycle: bool,
}
impl Ring {
pub fn new(link: bool) -> Result<Ring> {
pub fn new(length: usize, link: bool) -> Result<Ring> {
Ok(Ring {
link: link,
trbs: Dma::zeroed()?,
trbs: unsafe { Dma::zeroed_unsized(length)? },
i: 0,
cycle: link,
})
+33 -30
View File
@@ -322,18 +322,18 @@ impl Xhci {
.get_mut(&endp_num)
.ok_or(Error::new(EBADF))?;
let ring: &mut Ring = match endp_state {
let (has_streams, ring) = match endp_state {
EndpointState {
transfer: super::RingOrStreams::Ring(ref mut ring),
..
} => ring,
} => (false, ring),
EndpointState {
transfer: super::RingOrStreams::Streams(stream_ctx_array),
..
} => stream_ctx_array
} => (true, stream_ctx_array
.rings
.get_mut(&1)
.ok_or(Error::new(EBADF))?,
.ok_or(Error::new(EBADF))?),
};
loop {
@@ -347,7 +347,7 @@ impl Xhci {
self.dbs[usize::from(slot)].write(Self::endp_doorbell(
endp_num,
self.endp_desc(port_num, endp_num)?,
stream_id,
if has_streams { stream_id } else { 0 },
));
let cloned_trb = {
@@ -586,7 +586,7 @@ impl Xhci {
)
};
let lec = self.cap.lec();
let max_psa_size = self.cap.max_psa_size();
let log_max_psa_size = self.cap.max_psa_size();
let port_speed_id = self.ports[port].speed();
let speed_id: &ProtocolSpeed = self
@@ -627,11 +627,16 @@ impl Xhci {
let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc);
let max_streams = endp_desc.max_streams();
let usb_log_max_streams = endp_desc.log_max_streams();
// TODO: Secondary streams.
let primary_streams = if max_streams != 0 {
cmp::min(max_streams, max_psa_size)
let primary_streams = if let Some(log_max_streams) = usb_log_max_streams {
// TODO: Can streams-capable be configured to not use streams?
if log_max_psa_size != 0 {
cmp::min(u8::from(log_max_streams), log_max_psa_size + 1) - 1
} else {
0
}
} else {
0
};
@@ -677,8 +682,8 @@ impl Xhci {
let port_state = self.port_state_mut(port)?;
let ring_ptr = if max_streams != 0 {
let mut array = StreamContextArray::new(1 << (max_streams + 1))?;
let ring_ptr = if usb_log_max_streams.is_some() {
let mut array = StreamContextArray::new(1 << (primary_streams + 1))?;
// TODO: Use as many stream rings as needed.
array.add_ring(1, true)?;
@@ -699,7 +704,7 @@ impl Xhci {
array_ptr
} else {
let ring = Ring::new(true)?;
let ring = Ring::new(16, true)?;
let ring_ptr = ring.register();
assert_eq!(
@@ -750,7 +755,7 @@ impl Xhci {
let input_context_physical = self.input_context(port)?.physical();
self.execute_command("CONFIGURE_ENDPOINT", |trb, cycle| {
trb.configure_endpoint(slot, input_context_physical, cycle)
});
})?;
// Tell the device about this configuration.
let configuration_value = self.config_desc(port, req.config_desc)?.configuration_value;
@@ -1663,7 +1668,6 @@ impl Xhci {
endp_num: u8,
clear_feature: bool,
) -> Result<()> {
dbg!();
if self.get_endp_status(port_num, endp_num)? != EndpointStatus::Halted {
return Err(Error::new(EPROTO));
}
@@ -1711,10 +1715,10 @@ impl Xhci {
.endpoint_states
.get_mut(&endp_num)
.ok_or(Error::new(EBADFD))?;
let ring = match &mut endpoint_state.transfer {
&mut super::RingOrStreams::Ring(ref mut ring) => ring,
let (has_streams, ring) = match &mut endpoint_state.transfer {
&mut super::RingOrStreams::Ring(ref mut ring) => (false, ring),
&mut super::RingOrStreams::Streams(ref mut arr) => {
arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?
(true, arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?)
}
};
@@ -1730,7 +1734,7 @@ impl Xhci {
self.dbs[slot as usize].write(Self::endp_doorbell(
endp_num,
self.endp_desc(port_num, endp_num)?,
stream_id,
if has_streams { stream_id } else { 0 },
));
Ok(())
}
@@ -1782,7 +1786,6 @@ impl Xhci {
endp_num: u8,
buf: &[u8],
) -> Result<usize> {
dbg!();
let ep_if_state = &mut self
.port_states
.get_mut(&port_num)
@@ -1791,14 +1794,11 @@ impl Xhci {
.get_mut(&endp_num)
.ok_or(Error::new(EBADF))?
.driver_if_state;
dbg!();
let req = serde_json::from_slice::<XhciEndpCtlReq>(buf).or(Err(Error::new(EBADMSG)))?;
dbg!();
match req {
XhciEndpCtlReq::Status => match ep_if_state {
state @ EndpIfState::Init => *state = EndpIfState::WaitingForStatus,
other => {
dbg!(other);
return Err(Error::new(EBADF));
}
},
@@ -1807,14 +1807,12 @@ impl Xhci {
self.on_req_reset_device(port_num, endp_num, !no_clear_feature)?
}
other => {
dbg!(other);
return Err(Error::new(EBADF));
}
},
XhciEndpCtlReq::Transfer { direction, count } => match ep_if_state {
state @ EndpIfState::Init => {
if direction == XhciEndpCtlDirection::NoData {
dbg!();
// Yield the result directly because no bytes have to be sent or received
// beforehand.
let (completion_code, bytes_transferred) =
@@ -1823,7 +1821,6 @@ impl Xhci {
return Err(Error::new(EIO));
}
let result = Self::transfer_result(completion_code, 0);
dbg!();
let new_state = &mut self
.port_states
.get_mut(&port_num)
@@ -1834,7 +1831,6 @@ impl Xhci {
.driver_if_state;
*new_state = EndpIfState::WaitingForTransferResult(result)
} else {
dbg!();
*state = EndpIfState::WaitingForDataPipe {
direction,
bytes_to_transfer: count,
@@ -1843,12 +1839,10 @@ impl Xhci {
}
}
other => {
dbg!(other);
return Err(Error::new(EBADF));
}
},
other => {
dbg!(other);
return Err(Error::new(EBADF));
}
}
@@ -1885,6 +1879,10 @@ impl Xhci {
self.transfer_write(port_num, endp_num - 1, buf)?;
let result = Self::transfer_result(completion_code, some_bytes_transferred);
// To avoid having to read from the Ctl interface file, the client should stop
// invoking further data transfer calls if any single transfer returns fewer bytes
// than requested.
let ep_if_state = &mut self.endpoint_state_mut(port_num, endp_num)?.driver_if_state;
if let &mut EndpIfState::WaitingForDataPipe {
@@ -1893,7 +1891,8 @@ impl Xhci {
ref mut bytes_transferred,
} = ep_if_state
{
if *bytes_transferred + some_bytes_transferred == bytes_to_transfer {
if *bytes_transferred + some_bytes_transferred == bytes_to_transfer || completion_code == TrbCompletionCode::ShortPacket as u8 {
// TODO: Add an error flag to WaitingForTransferResult.
*ep_if_state = EndpIfState::WaitingForTransferResult(result);
} else {
*bytes_transferred += some_bytes_transferred;
@@ -1965,6 +1964,10 @@ impl Xhci {
let (completion_code, some_bytes_transferred) =
self.transfer_read(port_num, endp_num - 1, buf)?;
// Just as with on_write_endp_data, a client issuing multiple reads must always
// stop reading if one read returns fewer bytes than expected.
let result = Self::transfer_result(completion_code, some_bytes_transferred);
let ep_if_state = &mut self
@@ -1981,7 +1984,7 @@ impl Xhci {
ref mut bytes_transferred,
} = ep_if_state
{
if *bytes_transferred + some_bytes_transferred == bytes_to_transfer {
if *bytes_transferred + some_bytes_transferred == bytes_to_transfer || completion_code == TrbCompletionCode::ShortPacket as u8 {
*ep_if_state = EndpIfState::WaitingForTransferResult(result);
} else {
*bytes_transferred += some_bytes_transferred;