Make the usb scsi scheme able to do block I/O.
This commit is contained in:
+50
-20
@@ -20,15 +20,26 @@ fn main() {
|
||||
const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";
|
||||
|
||||
let scheme = args.next().expect(USAGE);
|
||||
let port = args.next().expect(USAGE).parse::<usize>().expect("port has to be a number");
|
||||
let protocol = args.next().expect(USAGE).parse::<u8>().expect("protocol has to be a number 0-255");
|
||||
let port = args
|
||||
.next()
|
||||
.expect(USAGE)
|
||||
.parse::<usize>()
|
||||
.expect("port has to be a number");
|
||||
let protocol = args
|
||||
.next()
|
||||
.expect(USAGE)
|
||||
.parse::<u8>()
|
||||
.expect("protocol has to be a number 0-255");
|
||||
|
||||
println!("USB SCSI driver spawned with scheme `{}`, port {}, protocol {}", scheme, port, protocol);
|
||||
println!(
|
||||
"USB SCSI driver spawned with scheme `{}`, port {}, protocol {}",
|
||||
scheme, port, protocol
|
||||
);
|
||||
|
||||
// Daemonize so that xhcid can continue to do other useful work (until proper IRQs,
|
||||
// async-await, and multithreading :D)
|
||||
if unsafe { syscall::clone(CloneFlags::empty()).unwrap() } != 0 {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
let disk_scheme_name = format!(":disk/{}-{}_scsi", scheme, port);
|
||||
@@ -36,30 +47,46 @@ fn main() {
|
||||
// TODO: Use eventfds.
|
||||
let handle = XhciClientHandle::new(scheme, port);
|
||||
|
||||
let desc = handle.get_standard_descs().expect("Failed to get standard descriptors");
|
||||
let desc = handle
|
||||
.get_standard_descs()
|
||||
.expect("Failed to get standard descriptors");
|
||||
|
||||
// TODO: Perhaps the drivers should just be given the config, interface, and alternate setting
|
||||
// from xhcid.
|
||||
let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc.config_descs.iter().find_map(|config_desc| {
|
||||
let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 {
|
||||
Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting))
|
||||
} else {
|
||||
None
|
||||
})?;
|
||||
Some((config_desc.clone(), config_desc.configuration_value, interface_desc))
|
||||
}).expect("Failed to find suitable configuration");
|
||||
let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc
|
||||
.config_descs
|
||||
.iter()
|
||||
.find_map(|config_desc| {
|
||||
let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| {
|
||||
if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 {
|
||||
Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})?;
|
||||
Some((
|
||||
config_desc.clone(),
|
||||
config_desc.configuration_value,
|
||||
interface_desc,
|
||||
))
|
||||
})
|
||||
.expect("Failed to find suitable configuration");
|
||||
|
||||
handle.configure_endpoints(&ConfigureEndpointsReq {
|
||||
config_desc: configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(alternate_setting),
|
||||
}).expect("Failed to configure endpoints");
|
||||
handle
|
||||
.configure_endpoints(&ConfigureEndpointsReq {
|
||||
config_desc: configuration_value,
|
||||
interface_desc: Some(interface_num),
|
||||
alternate_setting: Some(alternate_setting),
|
||||
})
|
||||
.expect("Failed to configure endpoints");
|
||||
|
||||
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol");
|
||||
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
|
||||
.expect("Failed to setup protocol");
|
||||
|
||||
// TODO: Let all of the USB drivers syscall clone(2), and xhcid won't have to keep track of all
|
||||
// the drivers.
|
||||
let socket_fd = syscall::open(disk_scheme_name, syscall::O_RDWR | syscall::O_CREAT).expect("usbscsid: failed to create disk scheme");
|
||||
let socket_fd = syscall::open(disk_scheme_name, syscall::O_RDWR | syscall::O_CREAT)
|
||||
.expect("usbscsid: failed to create disk scheme");
|
||||
let mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) };
|
||||
|
||||
//syscall::setrens(0, 0).expect("scsid: failed to enter null namespace");
|
||||
@@ -78,5 +105,8 @@ fn main() {
|
||||
Err(err) => panic!("scsid: failed to read disk scheme: {}", err),
|
||||
}
|
||||
scsi_scheme.handle(&mut packet);
|
||||
socket_file
|
||||
.write(&packet)
|
||||
.expect("scsid: failed to write packet");
|
||||
}
|
||||
}
|
||||
|
||||
+101
-30
@@ -1,7 +1,11 @@
|
||||
use std::num::NonZeroU32;
|
||||
use std::slice;
|
||||
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle};
|
||||
use xhcid_interface::{
|
||||
ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid,
|
||||
PortReqRecipient, PortReqTy, PortTransferStatus, XhciClientHandle, XhciClientHandleError,
|
||||
XhciEndpHandle,
|
||||
};
|
||||
|
||||
use super::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
||||
|
||||
@@ -18,12 +22,18 @@ pub struct CommandBlockWrapper {
|
||||
pub tag: u32,
|
||||
pub data_transfer_len: u32,
|
||||
pub flags: u8, // upper nibble reserved
|
||||
pub lun: u8, // bits 7:5 reserved
|
||||
pub lun: u8, // bits 7:5 reserved
|
||||
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> {
|
||||
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()));
|
||||
@@ -86,11 +96,23 @@ pub struct BulkOnlyTransport<'a> {
|
||||
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> {
|
||||
pub fn init(
|
||||
handle: &'a XhciClientHandle,
|
||||
config_desc: &ConfDesc,
|
||||
if_desc: &IfDesc,
|
||||
) -> Result<Self, ProtocolError> {
|
||||
let endpoints = &if_desc.endpoints;
|
||||
|
||||
let bulk_in_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::In).unwrap() + 1) as u8;
|
||||
let bulk_out_num = (endpoints.iter().position(|endpoint| endpoint.direction() == EndpDirection::Out).unwrap() + 1) as u8;
|
||||
let bulk_in_num = (endpoints
|
||||
.iter()
|
||||
.position(|endpoint| endpoint.direction() == EndpDirection::In)
|
||||
.unwrap()
|
||||
+ 1) as u8;
|
||||
let bulk_out_num = (endpoints
|
||||
.iter()
|
||||
.position(|endpoint| endpoint.direction() == EndpDirection::Out)
|
||||
.unwrap()
|
||||
+ 1) as u8;
|
||||
|
||||
let max_lun = get_max_lun(handle, 0)?;
|
||||
println!("BOT_MAX_LUN {}", max_lun);
|
||||
@@ -108,26 +130,40 @@ impl<'a> BulkOnlyTransport<'a> {
|
||||
}
|
||||
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)
|
||||
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)
|
||||
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_in()?;
|
||||
self.clear_stall_out()?;
|
||||
|
||||
if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.status()? == EndpointStatus::Halted {
|
||||
return Err(ProtocolError::RecoveryFailed)
|
||||
if self.bulk_in.status()? == EndpointStatus::Halted
|
||||
|| self.bulk_out.status()? == EndpointStatus::Halted
|
||||
{
|
||||
return Err(ProtocolError::RecoveryFailed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<SendCommandStatus, ProtocolError> {
|
||||
fn send_command(
|
||||
&mut self,
|
||||
cb: &[u8],
|
||||
data: DeviceReqData,
|
||||
) -> Result<SendCommandStatus, ProtocolError> {
|
||||
self.current_tag += 1;
|
||||
let tag = self.current_tag;
|
||||
|
||||
@@ -160,26 +196,38 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
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::ShortPacket(len) => {
|
||||
panic!("received short packed (len {}) when transferring data", len)
|
||||
}
|
||||
PortTransferStatus::Stalled => {
|
||||
println!("bulk in endpoint stalled when reading data");
|
||||
self.clear_stall_in()?;
|
||||
}
|
||||
PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))),
|
||||
PortTransferStatus::Unknown => {
|
||||
return Err(ProtocolError::XhciError(
|
||||
XhciClientHandleError::InvalidResponse(Invalid(
|
||||
"unknown transfer status",
|
||||
)),
|
||||
))
|
||||
}
|
||||
};
|
||||
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),
|
||||
PortTransferStatus::Stalled => {
|
||||
println!("bulk out endpoint stalled when reading data");
|
||||
self.clear_stall_out()?;
|
||||
}
|
||||
PortTransferStatus::Unknown => return Err(ProtocolError::XhciError(XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")))),
|
||||
DeviceReqData::Out(buffer) => match self.bulk_out.transfer_write(buffer)? {
|
||||
PortTransferStatus::Success => (),
|
||||
PortTransferStatus::ShortPacket(len) => {
|
||||
panic!("received short packed (len {}) when transferring data", len)
|
||||
}
|
||||
}
|
||||
PortTransferStatus::Stalled => {
|
||||
println!("bulk out endpoint stalled when reading data");
|
||||
self.clear_stall_out()?;
|
||||
}
|
||||
PortTransferStatus::Unknown => {
|
||||
return Err(ProtocolError::XhciError(
|
||||
XhciClientHandleError::InvalidResponse(Invalid("unknown transfer status")),
|
||||
))
|
||||
}
|
||||
},
|
||||
DeviceReqData::NoData => (),
|
||||
}
|
||||
|
||||
@@ -190,18 +238,22 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
println!("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),
|
||||
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()?;
|
||||
}
|
||||
dbg!(csw);
|
||||
|
||||
if self.bulk_in.status()? == EndpointStatus::Halted || self.bulk_out.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()?);
|
||||
}
|
||||
@@ -212,19 +264,38 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
} else if csw.status == CswStatus::Failed as u8 {
|
||||
SendCommandStatusKind::Failed
|
||||
} else {
|
||||
return Err(ProtocolError::ProtocolError("bulk-only transport phase error, or other"));
|
||||
return Err(ProtocolError::ProtocolError(
|
||||
"bulk-only transport phase error, or other",
|
||||
));
|
||||
},
|
||||
residue: NonZeroU32::new(csw.data_residue),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bulk_only_mass_storage_reset(handle: &XhciClientHandle, if_num: u16) -> Result<(), XhciClientHandleError> {
|
||||
handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFF, 0, if_num, DeviceReqData::NoData)
|
||||
pub fn bulk_only_mass_storage_reset(
|
||||
handle: &XhciClientHandle,
|
||||
if_num: u16,
|
||||
) -> Result<(), XhciClientHandleError> {
|
||||
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 = 0u8;
|
||||
let buffer = slice::from_mut(&mut lun);
|
||||
handle.device_request(PortReqTy::Class, PortReqRecipient::Interface, 0xFE, 0, if_num, DeviceReqData::In(buffer))?;
|
||||
handle.device_request(
|
||||
PortReqTy::Class,
|
||||
PortReqRecipient::Interface,
|
||||
0xFE,
|
||||
0,
|
||||
if_num,
|
||||
DeviceReqData::In(buffer),
|
||||
)?;
|
||||
Ok(lun)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ use std::io;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError};
|
||||
use xhcid_interface::{
|
||||
ConfDesc, DevDesc, DeviceReqData, IfDesc, XhciClientHandle, XhciClientHandleError,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ProtocolError {
|
||||
@@ -40,7 +42,6 @@ pub enum SendCommandStatusKind {
|
||||
Failed,
|
||||
}
|
||||
|
||||
|
||||
impl Default for SendCommandStatusKind {
|
||||
fn default() -> Self {
|
||||
Self::Success
|
||||
@@ -48,7 +49,11 @@ impl Default for SendCommandStatusKind {
|
||||
}
|
||||
|
||||
pub trait Protocol {
|
||||
fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<SendCommandStatus, ProtocolError>;
|
||||
fn send_command(
|
||||
&mut self,
|
||||
command: &[u8],
|
||||
data: DeviceReqData,
|
||||
) -> Result<SendCommandStatus, ProtocolError>;
|
||||
}
|
||||
|
||||
/// Bulk-only transport
|
||||
@@ -60,9 +65,17 @@ mod uas {
|
||||
|
||||
use bot::BulkOnlyTransport;
|
||||
|
||||
pub fn setup<'a>(handle: &'a XhciClientHandle, protocol: u8, dev_desc: &DevDesc, conf_desc: &ConfDesc, if_desc: &IfDesc) -> Option<Box<dyn Protocol + 'a>> {
|
||||
pub fn setup<'a>(
|
||||
handle: &'a XhciClientHandle,
|
||||
protocol: u8,
|
||||
dev_desc: &DevDesc,
|
||||
conf_desc: &ConfDesc,
|
||||
if_desc: &IfDesc,
|
||||
) -> Option<Box<dyn Protocol + 'a>> {
|
||||
match protocol {
|
||||
0x50 => Some(Box::new(BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap())),
|
||||
0x50 => Some(Box::new(
|
||||
BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
+43
-10
@@ -4,12 +4,12 @@ use std::{cmp, str};
|
||||
use crate::protocol::Protocol;
|
||||
use crate::scsi::Scsi;
|
||||
|
||||
use syscall::SchemeMut;
|
||||
use syscall::error::{Error, Result};
|
||||
use syscall::error::{EACCES, EBADF, EINVAL, EIO, ENOENT, ENOSYS};
|
||||
use syscall::flag::{O_DIRECTORY, O_STAT};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR};
|
||||
use syscall::flag::{O_DIRECTORY, O_STAT};
|
||||
use syscall::flag::{SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
use syscall::SchemeMut;
|
||||
|
||||
// TODO: Only one disk, right?
|
||||
const LIST_CONTENTS: &'static [u8] = b"0\n";
|
||||
@@ -43,7 +43,9 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
if uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_start_matches('/');
|
||||
let path_str = str::from_utf8(path)
|
||||
.or(Err(Error::new(ENOENT)))?
|
||||
.trim_start_matches('/');
|
||||
let handle = if path_str.is_empty() {
|
||||
// List
|
||||
Handle::List(0)
|
||||
@@ -55,7 +57,7 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
};
|
||||
self.next_fd += 1;
|
||||
self.handles.insert(self.next_fd, handle);
|
||||
Err(Error::new(ENOSYS))
|
||||
Ok(self.next_fd)
|
||||
}
|
||||
fn fstat(&mut self, fd: usize, stat: &mut syscall::Stat) -> Result<usize> {
|
||||
match self.handles.get(&fd).ok_or(Error::new(EBADF))? {
|
||||
@@ -70,10 +72,17 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
stat.st_size = LIST_CONTENTS.len() as u64;
|
||||
}
|
||||
}
|
||||
Err(Error::new(ENOSYS))
|
||||
Ok(0)
|
||||
}
|
||||
fn fpath(&mut self, fd: usize, path: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let path = match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(_) => "0",
|
||||
Handle::List(_) => "",
|
||||
}
|
||||
.as_bytes();
|
||||
let min = std::cmp::min(path.len(), buf.len());
|
||||
buf[..min].copy_from_slice(&path[..min]);
|
||||
Ok(min)
|
||||
}
|
||||
fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
@@ -102,11 +111,18 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
if *offset as u64 % u64::from(self.scsi.block_size) != 0 || buf.len() as u64 % u64::from(self.scsi.block_size) != 0 {
|
||||
if *offset as u64 % u64::from(self.scsi.block_size) != 0
|
||||
|| buf.len() as u64 % u64::from(self.scsi.block_size) != 0
|
||||
{
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let lba = *offset as u64 / u64::from(self.scsi.block_size);
|
||||
let bytes_read = self.scsi.read(self.protocol, lba, buf).map_err(|err| dbg!(err)).or(Err(Error::new(EIO)))?;
|
||||
let bytes_read = self
|
||||
.scsi
|
||||
.read(self.protocol, lba, buf)
|
||||
.map_err(|err| dbg!(err))
|
||||
.or(Err(Error::new(EIO)))?;
|
||||
*offset += bytes_read as usize;
|
||||
Ok(bytes_read as usize)
|
||||
}
|
||||
Handle::List(ref mut offset) => {
|
||||
@@ -121,6 +137,23 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
}
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
if *offset as u64 % u64::from(self.scsi.block_size) != 0
|
||||
|| buf.len() as u64 % u64::from(self.scsi.block_size) != 0
|
||||
{
|
||||
return Err(Error::new(EINVAL));
|
||||
}
|
||||
let lba = *offset as u64 / u64::from(self.scsi.block_size);
|
||||
let bytes_written = self
|
||||
.scsi
|
||||
.write(self.protocol, lba, buf)
|
||||
.map_err(|err| dbg!(err))
|
||||
.or(Err(Error::new(EIO)))?;
|
||||
*offset += bytes_written as usize;
|
||||
Ok(bytes_written as usize)
|
||||
}
|
||||
Handle::List(_) => Err(Error::new(EBADF)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+64
-10
@@ -1,5 +1,5 @@
|
||||
use std::{fmt, mem, slice};
|
||||
use super::opcodes::Opcode;
|
||||
use std::{fmt, mem, slice};
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Inquiry {
|
||||
@@ -124,7 +124,10 @@ impl FixedFormatSenseData {
|
||||
self.add_sense_len as u16 + 7
|
||||
}
|
||||
pub unsafe fn add_sense_bytes(&self) -> &[u8] {
|
||||
slice::from_raw_parts(&self.add_sense_len as *const u8, self.add_sense_len as usize - 18)
|
||||
slice::from_raw_parts(
|
||||
&self.add_sense_len as *const u8,
|
||||
self.add_sense_len as usize - 18,
|
||||
)
|
||||
}
|
||||
pub fn sense_key(&self) -> SenseKey {
|
||||
let sense_key_raw = self.b & 0b1111;
|
||||
@@ -189,6 +192,32 @@ impl Read16 {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Write16 {
|
||||
pub opcode: u8,
|
||||
pub a: u8,
|
||||
pub lba: u64, // big endian
|
||||
pub transfer_len: u32,
|
||||
pub b: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
unsafe impl plain::Plain for Write16 {}
|
||||
|
||||
impl Write16 {
|
||||
pub const fn new(lba: u64, transfer_len: u32, control: u8) -> Self {
|
||||
Self {
|
||||
// TODO
|
||||
opcode: Opcode::Write16 as u8,
|
||||
a: 0,
|
||||
lba,
|
||||
transfer_len,
|
||||
b: 0,
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ModeSense6 {
|
||||
@@ -202,7 +231,14 @@ pub struct ModeSense6 {
|
||||
unsafe impl plain::Plain for ModeSense6 {}
|
||||
|
||||
impl ModeSense6 {
|
||||
pub const fn new(dbd: bool, page_code: u8, pc: u8, subpage_code: u8, alloc_len: u8, control: u8) -> Self {
|
||||
pub const fn new(
|
||||
dbd: bool,
|
||||
page_code: u8,
|
||||
pc: u8,
|
||||
subpage_code: u8,
|
||||
alloc_len: u8,
|
||||
control: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::ModeSense6 as u8,
|
||||
a: (dbd as u8) << 3,
|
||||
@@ -228,7 +264,15 @@ pub struct ModeSense10 {
|
||||
unsafe impl plain::Plain for ModeSense10 {}
|
||||
|
||||
impl ModeSense10 {
|
||||
pub const fn new(dbd: bool, llbaa: bool, page_code: u8, pc: ModePageControl, subpage_code: u8, alloc_len: u16, control: u8) -> Self {
|
||||
pub const fn new(
|
||||
dbd: bool,
|
||||
llbaa: bool,
|
||||
page_code: u8,
|
||||
pc: ModePageControl,
|
||||
subpage_code: u8,
|
||||
alloc_len: u16,
|
||||
control: u8,
|
||||
) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::ModeSense10 as u8,
|
||||
a: ((dbd as u8) << 3) | ((llbaa as u8) << 4),
|
||||
@@ -240,7 +284,15 @@ impl ModeSense10 {
|
||||
}
|
||||
}
|
||||
pub const fn get_block_desc(alloc_len: u16, control: u8) -> Self {
|
||||
Self::new(false, true, 0x3F, ModePageControl::CurrentValues, 0x00, alloc_len, control)
|
||||
Self::new(
|
||||
false,
|
||||
true,
|
||||
0x3F,
|
||||
ModePageControl::CurrentValues,
|
||||
0x00,
|
||||
alloc_len,
|
||||
control,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,9 +331,7 @@ impl fmt::Debug for ShortLbaModeParamBlkDesc {
|
||||
}
|
||||
|
||||
const fn u24_be_to_u32(u24: [u8; 3]) -> u32 {
|
||||
((u24[0] as u32) << 16)
|
||||
| ((u24[1] as u32) << 8)
|
||||
| (u24[2] as u32)
|
||||
((u24[0] as u32) << 16) | ((u24[1] as u32) << 8) | (u24[2] as u32)
|
||||
}
|
||||
|
||||
/// From SPC-3, when LONGLBA is not set, and the peripheral device type of the INQUIRY data indicates that the device is not a direct access device. Otherwise, `ShortLbaModeParamBlkDesc` is used instead.
|
||||
@@ -434,7 +484,9 @@ impl<'a> Iterator for ModePageIter<'a> {
|
||||
|
||||
if !spf {
|
||||
if page_code == 0x01 {
|
||||
Some(AnyModePage::RwErrorRecovery(plain::from_bytes(next_buf).ok()?))
|
||||
Some(AnyModePage::RwErrorRecovery(
|
||||
plain::from_bytes(next_buf).ok()?,
|
||||
))
|
||||
} else {
|
||||
println!("Unimplemented sub_page {}", base64::encode(next_buf));
|
||||
None
|
||||
@@ -447,5 +499,7 @@ impl<'a> Iterator for ModePageIter<'a> {
|
||||
}
|
||||
|
||||
pub fn mode_page_iter(buffer: &[u8]) -> impl Iterator<Item = AnyModePage> {
|
||||
ModePageIter { raw: ModePageIterRaw { buffer } }
|
||||
ModePageIter {
|
||||
raw: ModePageIterRaw { buffer },
|
||||
}
|
||||
}
|
||||
|
||||
+120
-18
@@ -1,5 +1,5 @@
|
||||
use std::{mem, ops};
|
||||
use std::convert::TryFrom;
|
||||
use std::{mem, ops};
|
||||
|
||||
pub mod cmds;
|
||||
pub mod opcodes;
|
||||
@@ -29,6 +29,9 @@ const MIN_REPORT_SUPP_OPCODES_ALLOC_LEN: u32 = 4;
|
||||
pub enum ScsiError {
|
||||
#[error("protocol error when sending command: {0}")]
|
||||
ProtocolError(#[from] ProtocolError),
|
||||
|
||||
#[error("overflow")]
|
||||
Overflow(&'static str),
|
||||
}
|
||||
|
||||
impl Scsi {
|
||||
@@ -70,31 +73,67 @@ impl Scsi {
|
||||
let inquiry = self.cmd_inquiry();
|
||||
*inquiry = cmds::Inquiry::new(false, 0, max_inquiry_len, 0);
|
||||
|
||||
protocol.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");
|
||||
protocol
|
||||
.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");
|
||||
}
|
||||
pub fn get_ff_sense(&mut self, protocol: &mut dyn Protocol, alloc_len: u8) {
|
||||
let request_sense = self.cmd_request_sense();
|
||||
*request_sense = cmds::RequestSense::new(false, alloc_len, 0);
|
||||
self.data_buffer.resize(alloc_len.into(), 0);
|
||||
protocol.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");
|
||||
protocol
|
||||
.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, protocol: &mut dyn Protocol) -> Result<(&cmds::ModeParamHeader10, BlkDescSlice, impl Iterator<Item = cmds::AnyModePage>), ScsiError> {
|
||||
pub fn get_mode_sense10(
|
||||
&mut self,
|
||||
protocol: &mut dyn Protocol,
|
||||
) -> Result<
|
||||
(
|
||||
&cmds::ModeParamHeader10,
|
||||
BlkDescSlice,
|
||||
impl Iterator<Item = cmds::AnyModePage>,
|
||||
),
|
||||
ScsiError,
|
||||
> {
|
||||
let initial_alloc_len = 4; // covers both mode_data_len and blk_desc_len.
|
||||
let mode_sense10 = self.cmd_mode_sense10();
|
||||
*mode_sense10 = cmds::ModeSense10::get_block_desc(initial_alloc_len, 0);
|
||||
self.data_buffer.resize(mem::size_of::<cmds::ModeParamHeader10>(), 0);
|
||||
if let SendCommandStatus { kind: SendCommandStatusKind::Failed, .. } = protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]))? {
|
||||
self.data_buffer
|
||||
.resize(mem::size_of::<cmds::ModeParamHeader10>(), 0);
|
||||
if let SendCommandStatus {
|
||||
kind: SendCommandStatusKind::Failed,
|
||||
..
|
||||
} = protocol.send_command(
|
||||
&self.command_buffer[..10],
|
||||
DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]),
|
||||
)? {
|
||||
self.get_ff_sense(protocol, 252);
|
||||
panic!("{:?}", self.res_ff_sense_data());
|
||||
}
|
||||
|
||||
let optimal_alloc_len = self.res_mode_param_header10().block_desc_len() + self.res_mode_param_header10().mode_data_len() + mem::size_of::<cmds::ModeParamHeader10>() as u16;
|
||||
let optimal_alloc_len = self.res_mode_param_header10().block_desc_len()
|
||||
+ self.res_mode_param_header10().mode_data_len()
|
||||
+ mem::size_of::<cmds::ModeParamHeader10>() as u16;
|
||||
|
||||
let mode_sense10 = self.cmd_mode_sense10();
|
||||
*mode_sense10 = cmds::ModeSense10::get_block_desc(optimal_alloc_len, 0);
|
||||
self.data_buffer.resize(optimal_alloc_len as usize, 0);
|
||||
protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..optimal_alloc_len as usize]))?;
|
||||
Ok((self.res_mode_param_header10(), self.res_blkdesc_mode10(), self.res_mode_pages10()))
|
||||
protocol.send_command(
|
||||
&self.command_buffer[..10],
|
||||
DeviceReqData::In(&mut self.data_buffer[..optimal_alloc_len as usize]),
|
||||
)?;
|
||||
Ok((
|
||||
self.res_mode_param_header10(),
|
||||
self.res_blkdesc_mode10(),
|
||||
self.res_mode_pages10(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn cmd_inquiry(&mut self) -> &mut cmds::Inquiry {
|
||||
@@ -112,6 +151,9 @@ impl Scsi {
|
||||
pub fn cmd_read16(&mut self) -> &mut cmds::Read16 {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
pub fn cmd_write16(&mut self) -> &mut cmds::Write16 {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
pub fn res_standard_inquiry_data(&self) -> &StandardInquiryData {
|
||||
plain::from_bytes(&self.inquiry_buffer).unwrap()
|
||||
}
|
||||
@@ -127,17 +169,41 @@ impl Scsi {
|
||||
pub fn res_blkdesc_mode6(&self) -> &[cmds::ShortLbaModeParamBlkDesc] {
|
||||
let header = self.res_mode_param_header6();
|
||||
let descs_start = mem::size_of::<cmds::ModeParamHeader6>();
|
||||
plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)]).unwrap()
|
||||
plain::slice_from_bytes(
|
||||
&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len)],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
pub fn res_blkdesc_mode10(&self) -> BlkDescSlice {
|
||||
let header = self.res_mode_param_header10();
|
||||
let descs_start = mem::size_of::<cmds::ModeParamHeader10>();
|
||||
if header.longlba() {
|
||||
BlkDescSlice::Long(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len())]).unwrap())
|
||||
} else if self.res_standard_inquiry_data().periph_dev_ty() != cmds::PeriphDeviceType::DirectAccess as u8 && self.res_standard_inquiry_data().version() == cmds::InquiryVersion::Spc3 as u8 {
|
||||
BlkDescSlice::General(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len())]).unwrap())
|
||||
BlkDescSlice::Long(
|
||||
plain::slice_from_bytes(
|
||||
&self.data_buffer
|
||||
[descs_start..descs_start + usize::from(header.block_desc_len())],
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
} else if self.res_standard_inquiry_data().periph_dev_ty()
|
||||
!= cmds::PeriphDeviceType::DirectAccess as u8
|
||||
&& self.res_standard_inquiry_data().version() == cmds::InquiryVersion::Spc3 as u8
|
||||
{
|
||||
BlkDescSlice::General(
|
||||
plain::slice_from_bytes(
|
||||
&self.data_buffer
|
||||
[descs_start..descs_start + usize::from(header.block_desc_len())],
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
BlkDescSlice::Short(plain::slice_from_bytes(&self.data_buffer[descs_start..descs_start + usize::from(header.block_desc_len())]).unwrap())
|
||||
BlkDescSlice::Short(
|
||||
plain::slice_from_bytes(
|
||||
&self.data_buffer
|
||||
[descs_start..descs_start + usize::from(header.block_desc_len())],
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,15 +216,51 @@ impl Scsi {
|
||||
pub fn get_disk_size(&mut self) -> u64 {
|
||||
self.block_count * u64::from(self.block_size)
|
||||
}
|
||||
pub fn read(&mut self, protocol: &mut dyn Protocol, lba: u64, buffer: &mut [u8]) -> Result<u32, ScsiError> {
|
||||
pub fn read(
|
||||
&mut self,
|
||||
protocol: &mut dyn Protocol,
|
||||
lba: u64,
|
||||
buffer: &mut [u8],
|
||||
) -> Result<u32, ScsiError> {
|
||||
let blocks_to_read = buffer.len() as u64 / u64::from(self.block_size);
|
||||
let transfer_len = u32::try_from(blocks_to_read * u64::from(self.block_size)).ok().unwrap_or(std::u32::MAX);
|
||||
let bytes_to_read = blocks_to_read as usize * self.block_size as usize;
|
||||
let transfer_len = u32::try_from(blocks_to_read).or(Err(ScsiError::Overflow(
|
||||
"number of blocks to read couldn't fit inside a u32",
|
||||
)))?;
|
||||
{
|
||||
let read = self.cmd_read16();
|
||||
*read = cmds::Read16::new(lba, transfer_len, 0);
|
||||
}
|
||||
let status = protocol.send_command(&self.command_buffer[..16], DeviceReqData::In(&mut buffer[..transfer_len as usize]))?;
|
||||
Ok(status.bytes_transferred(transfer_len))
|
||||
// TODO: Use the to-be-written TransferReadStream instead of relying on everything being
|
||||
// able to fit within a single buffer.
|
||||
let status = protocol.send_command(
|
||||
&self.command_buffer[..16],
|
||||
DeviceReqData::In(&mut buffer[..bytes_to_read]),
|
||||
)?;
|
||||
Ok(status.bytes_transferred(bytes_to_read as u32))
|
||||
}
|
||||
pub fn write(
|
||||
&mut self,
|
||||
protocol: &mut dyn Protocol,
|
||||
lba: u64,
|
||||
buffer: &[u8],
|
||||
) -> Result<u32, ScsiError> {
|
||||
let blocks_to_write = buffer.len() as u64 / u64::from(self.block_size);
|
||||
let bytes_to_write = blocks_to_write as usize * self.block_size as usize;
|
||||
let transfer_len = u32::try_from(blocks_to_write).or(Err(ScsiError::Overflow(
|
||||
"number of blocks to write couldn't fit inside a u32",
|
||||
)))?;
|
||||
{
|
||||
let read = self.cmd_write16();
|
||||
*read = cmds::Write16::new(lba, transfer_len, 0);
|
||||
}
|
||||
// TODO: Use the to-be-written TransferReadStream instead of relying on everything being
|
||||
// able to fit within a single buffer.
|
||||
let status = protocol.send_command(
|
||||
&self.command_buffer[..16],
|
||||
DeviceReqData::Out(&buffer[..bytes_to_write]),
|
||||
)?;
|
||||
Ok(status.bytes_transferred(bytes_to_write as u32))
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
|
||||
+101
-33
@@ -190,7 +190,9 @@ impl EndpDesc {
|
||||
}
|
||||
pub fn isoch_mult(&self, lec: bool) -> u8 {
|
||||
if !lec && self.is_isoch() {
|
||||
if self.is_superspeedplus() { return 0 }
|
||||
if self.is_superspeedplus() {
|
||||
return 0;
|
||||
}
|
||||
self.ssc
|
||||
.as_ref()
|
||||
.map(|ssc| ssc.attributes & 0x3)
|
||||
@@ -203,7 +205,9 @@ impl EndpDesc {
|
||||
self.ssc.map(|ssc| ssc.max_burst).unwrap_or(0)
|
||||
}
|
||||
pub fn has_ssp_companion(&self) -> bool {
|
||||
self.ssc.map(|ssc| ssc.attributes & (1 << 7) != 0).unwrap_or(false)
|
||||
self.ssc
|
||||
.map(|ssc| ssc.attributes & (1 << 7) != 0)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
@@ -421,21 +425,12 @@ impl XhciClientHandle {
|
||||
let string = std::fs::read_to_string(path)?;
|
||||
Ok(string.parse()?)
|
||||
}
|
||||
pub fn open_endpoint_ctl(
|
||||
&self,
|
||||
num: u8,
|
||||
) -> result::Result<File, XhciClientHandleError> {
|
||||
pub fn open_endpoint_ctl(&self, num: u8) -> result::Result<File, XhciClientHandleError> {
|
||||
let path = format!("{}:port{}/endpoints/{}/ctl", self.scheme, self.port, num);
|
||||
Ok(File::open(path)?)
|
||||
}
|
||||
pub fn open_endpoint_data(
|
||||
&self,
|
||||
num: u8,
|
||||
) -> result::Result<File, XhciClientHandleError> {
|
||||
let path = format!(
|
||||
"{}:port{}/endpoints/{}/data",
|
||||
self.scheme, self.port, num
|
||||
);
|
||||
pub fn open_endpoint_data(&self, num: u8) -> result::Result<File, XhciClientHandleError> {
|
||||
let path = format!("{}:port{}/endpoints/{}/data", self.scheme, self.port, num);
|
||||
Ok(File::open(path)?)
|
||||
}
|
||||
pub fn open_endpoint(&self, num: u8) -> result::Result<XhciEndpHandle, XhciClientHandleError> {
|
||||
@@ -541,13 +536,18 @@ pub struct XhciEndpHandle {
|
||||
ctl: File,
|
||||
}
|
||||
|
||||
/// The direction of a transfer.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
pub enum XhciEndpCtlDirection {
|
||||
/// Host to device
|
||||
Out,
|
||||
/// Device to host
|
||||
In,
|
||||
/// No data, and hence no I/O on the Data interface file at all.
|
||||
NoData,
|
||||
}
|
||||
|
||||
/// A request to an endpoint Ctl interface file. Currently serialized with JSON.
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum XhciEndpCtlReq {
|
||||
@@ -555,9 +555,19 @@ pub enum XhciEndpCtlReq {
|
||||
// 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.
|
||||
Transfer {
|
||||
/// The direction of the transfer. If the direction is `XhciEndpCtlDirection::NoData`, no
|
||||
/// bytes will be transferred, and therefore no reads or writes shall be done to the Data
|
||||
/// driver interface file.
|
||||
direction: XhciEndpCtlDirection,
|
||||
|
||||
/// The number of bytes to be read or written. This field must be set to zero if the
|
||||
/// direction is `XhciEndpCtlDirection::NoData`. When all bytes have been read or written,
|
||||
/// the transfer will be considered complete by xhcid, and a non-pending status will be
|
||||
/// returned.
|
||||
count: u32,
|
||||
},
|
||||
// TODO: Allow clients to specify what to reset.
|
||||
/// 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
|
||||
@@ -568,6 +578,7 @@ pub enum XhciEndpCtlReq {
|
||||
/// Tells xhcid that the endpoint status is going to be retrieved from the Ctl interface file.
|
||||
Status,
|
||||
}
|
||||
/// A response from an endpoint Ctl interface file. Currently serialized with JSON.
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum XhciEndpCtlRes {
|
||||
@@ -586,9 +597,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)?;
|
||||
let ctl_buffer = serde_json::to_vec(ctl_req).expect("serde");
|
||||
|
||||
let ctl_bytes_written = self.ctl.write(&ctl_buffer)?;
|
||||
let ctl_bytes_written = self.ctl.write(&ctl_buffer).expect("ctlwrite");
|
||||
if ctl_bytes_written != ctl_buffer.len() {
|
||||
return Err(Invalid("xhcid didn't process all of the ctl bytes").into());
|
||||
}
|
||||
@@ -598,7 +609,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)?;
|
||||
let ctl_bytes_read = self.ctl.read(&mut ctl_buffer).expect("ctlread");
|
||||
|
||||
let ctl_res = serde_json::from_slice(&ctl_buffer[..ctl_bytes_read as usize])?;
|
||||
Ok(ctl_res)
|
||||
@@ -607,35 +618,89 @@ impl XhciEndpHandle {
|
||||
self.ctl_req(&XhciEndpCtlReq::Reset { no_clear_feature })
|
||||
}
|
||||
pub fn status(&mut self) -> result::Result<EndpointStatus, XhciClientHandleError> {
|
||||
self.ctl_req(&XhciEndpCtlReq::Status)?;
|
||||
match self.ctl_res()? {
|
||||
self.ctl_req(&XhciEndpCtlReq::Status)
|
||||
.expect("status ctlreq");
|
||||
match self.ctl_res().expect("status ctlres") {
|
||||
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(direction);
|
||||
self.ctl_req(&req)?;
|
||||
fn generic_transfer<F: FnOnce(&mut File) -> io::Result<usize>>(
|
||||
&mut self,
|
||||
direction: XhciEndpCtlDirection,
|
||||
f: F,
|
||||
expected_len: u32,
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let req = XhciEndpCtlReq::Transfer {
|
||||
direction,
|
||||
count: expected_len,
|
||||
};
|
||||
self.ctl_req(&req).expect("ctl_req");
|
||||
|
||||
let bytes_read = f(&mut self.data)?;
|
||||
let res = self.ctl_res()?;
|
||||
let bytes_read = f(&mut self.data).expect("f");
|
||||
let res = self.ctl_res().expect("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(PortTransferStatus::Success)
|
||||
if bytes_read != expected_len as usize =>
|
||||
{
|
||||
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_write(
|
||||
&mut self,
|
||||
buf: &[u8],
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
self.generic_transfer(
|
||||
XhciEndpCtlDirection::Out,
|
||||
|data| data.write(buf),
|
||||
buf.len() as u32,
|
||||
)
|
||||
}
|
||||
pub fn transfer_read(&mut self, buf: &mut [u8]) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let len = buf.len();
|
||||
pub fn transfer_read(
|
||||
&mut self,
|
||||
buf: &mut [u8],
|
||||
) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
let len = buf.len() as u32;
|
||||
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())
|
||||
pub fn transfer_nodata(&mut self) -> result::Result<PortTransferStatus, XhciClientHandleError> {
|
||||
self.generic_transfer(XhciEndpCtlDirection::NoData, |_| Ok(0), 0)
|
||||
}
|
||||
fn transfer_stream(&mut self, total_len: u32) -> TransferStream {
|
||||
TransferStream {
|
||||
bytes_to_transfer: total_len,
|
||||
bytes_transferred: 0,
|
||||
bytes_per_transfer: 32768, // TODO
|
||||
endp_handle: self,
|
||||
}
|
||||
}
|
||||
pub fn transfer_write_stream(&mut self, total_len: u32) -> TransferWriteStream {
|
||||
TransferWriteStream {
|
||||
inner: self.transfer_stream(total_len),
|
||||
}
|
||||
}
|
||||
pub fn transfer_read_stream(&mut self, total_len: u32) -> TransferReadStream {
|
||||
TransferReadStream {
|
||||
inner: self.transfer_stream(total_len),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TransferWriteStream<'a> {
|
||||
inner: TransferStream<'a>,
|
||||
}
|
||||
pub struct TransferReadStream<'a> {
|
||||
inner: TransferStream<'a>,
|
||||
}
|
||||
struct TransferStream<'a> {
|
||||
bytes_to_transfer: u32,
|
||||
bytes_transferred: u32,
|
||||
bytes_per_transfer: u32,
|
||||
endp_handle: &'a mut XhciEndpHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@@ -651,4 +716,7 @@ pub enum XhciClientHandleError {
|
||||
|
||||
#[error("transfer buffer too large ({0} > 65536)")]
|
||||
TransferBufTooLarge(usize),
|
||||
|
||||
#[error("unexpected short packet of size {0}")]
|
||||
UnexpectedShortPacket(usize),
|
||||
}
|
||||
|
||||
@@ -75,7 +75,12 @@ impl BosSuperSpeedPlusDesc {
|
||||
(self.attrs & 0x0000_000F) as u8
|
||||
}
|
||||
pub fn sublink_speed_attr(&self) -> &[u32] {
|
||||
unsafe { slice::from_raw_parts(&self.sublink_speed_attr as *const u32, self.ssac() as usize + 1) }
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
&self.sublink_speed_attr as *const u32,
|
||||
self.ssac() as usize + 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +160,9 @@ impl<'a> Iterator for BosAnyDevDescIter<'a> {
|
||||
} else if base.cap_ty == DeviceCapability::SuperSpeed as u8 {
|
||||
Some(BosAnyDevDesc::SuperSpeed(*plain::from_bytes(slice).ok()?))
|
||||
} else if base.cap_ty == DeviceCapability::SuperSpeedPlus as u8 {
|
||||
Some(BosAnyDevDesc::SuperSpeedPlus(*plain::from_bytes(slice).ok()?))
|
||||
Some(BosAnyDevDesc::SuperSpeedPlus(
|
||||
*plain::from_bytes(slice).ok()?,
|
||||
))
|
||||
} else if base.cap_ty == 0 {
|
||||
// TODO
|
||||
return None;
|
||||
|
||||
@@ -2,7 +2,8 @@ pub use self::bos::{bos_capability_descs, BosAnyDevDesc, BosDescriptor, BosSuper
|
||||
pub use self::config::ConfigDescriptor;
|
||||
pub use self::device::DeviceDescriptor;
|
||||
pub use self::endpoint::{
|
||||
EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK,
|
||||
EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor,
|
||||
SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK,
|
||||
};
|
||||
pub use self::interface::InterfaceDescriptor;
|
||||
pub use self::setup::Setup;
|
||||
|
||||
@@ -126,16 +126,28 @@ impl ProtocolSpeed {
|
||||
self.psim() == 5 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeed
|
||||
}
|
||||
pub fn is_superspeedplus_gen2x1(&self) -> bool {
|
||||
self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus
|
||||
self.psim() == 10
|
||||
&& self.psie() == Psie::Gbps
|
||||
&& self.pfd()
|
||||
&& self.lp() == Lp::SuperSpeedPlus
|
||||
}
|
||||
pub fn is_superspeedplus_gen1x2(&self) -> bool {
|
||||
self.psim() == 10 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus
|
||||
self.psim() == 10
|
||||
&& self.psie() == Psie::Gbps
|
||||
&& self.pfd()
|
||||
&& self.lp() == Lp::SuperSpeedPlus
|
||||
}
|
||||
pub fn is_superspeedplus_gen2x2(&self) -> bool {
|
||||
self.psim() == 20 && self.psie() == Psie::Gbps && self.pfd() && self.lp() == Lp::SuperSpeedPlus
|
||||
self.psim() == 20
|
||||
&& self.psie() == Psie::Gbps
|
||||
&& self.pfd()
|
||||
&& self.lp() == Lp::SuperSpeedPlus
|
||||
}
|
||||
pub fn is_superspeed_gen_x(&self) -> bool {
|
||||
self.is_superspeed_gen1x1() || self.is_superspeedplus_gen2x1() || self.is_superspeedplus_gen1x2() || self.is_superspeedplus_gen2x2()
|
||||
self.is_superspeed_gen1x1()
|
||||
|| self.is_superspeedplus_gen2x1()
|
||||
|| self.is_superspeedplus_gen1x2()
|
||||
|| self.is_superspeedplus_gen2x2()
|
||||
}
|
||||
/// Protocol speed ID value
|
||||
pub fn psiv(&self) -> u8 {
|
||||
|
||||
+33
-15
@@ -301,7 +301,8 @@ impl Xhci {
|
||||
pub fn enable_port_slot(&mut self, slot_ty: u8) -> Result<u8> {
|
||||
assert_eq!(slot_ty & 0x1F, slot_ty);
|
||||
|
||||
let cloned_event_trb = self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?;
|
||||
let cloned_event_trb =
|
||||
self.execute_command("ENABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?;
|
||||
Ok(cloned_event_trb.event_slot())
|
||||
}
|
||||
pub fn disable_port_slot(&mut self, slot: u8) -> Result<()> {
|
||||
@@ -329,7 +330,10 @@ impl Xhci {
|
||||
|
||||
println!(" - Enable slot");
|
||||
|
||||
let slot_ty = self.supported_protocol(i as u8).expect("Failed to find supported protocol information for port").proto_slot_ty();
|
||||
let slot_ty = self
|
||||
.supported_protocol(i as u8)
|
||||
.expect("Failed to find supported protocol information for port")
|
||||
.proto_slot_ty();
|
||||
let slot = self.enable_port_slot(slot_ty)?;
|
||||
|
||||
println!(" - Slot {}", slot);
|
||||
@@ -358,7 +362,7 @@ impl Xhci {
|
||||
EndpointState {
|
||||
transfer: RingOrStreams::Ring(ring),
|
||||
driver_if_state: EndpIfState::Init,
|
||||
}
|
||||
},
|
||||
))
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
};
|
||||
@@ -379,7 +383,12 @@ impl Xhci {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_default_control_pipe(&mut self, input_context: &mut Dma<InputContext>, slot_id: u8, dev_desc: &DevDesc) -> Result<()> {
|
||||
pub fn update_default_control_pipe(
|
||||
&mut self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
slot_id: u8,
|
||||
dev_desc: &DevDesc,
|
||||
) -> Result<()> {
|
||||
input_context.add_context.write(1 << 1);
|
||||
input_context.drop_context.write(0);
|
||||
|
||||
@@ -393,14 +402,21 @@ impl Xhci {
|
||||
b &= 0x0000_FFFF;
|
||||
b |= (new_max_packet_size) << 16;
|
||||
endp_ctx.b.write(b);
|
||||
|
||||
|
||||
self.execute_command("EVALUATE_CONTEXT", |trb, cycle| {
|
||||
trb.evaluate_context(slot_id, input_context.physical(), false, cycle)
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn address_device(&mut self, input_context: &mut Dma<InputContext>, i: usize, slot_ty: u8, slot: u8, speed: u8) -> Result<Ring> {
|
||||
pub fn address_device(
|
||||
&mut self,
|
||||
input_context: &mut Dma<InputContext>,
|
||||
i: usize,
|
||||
slot_ty: u8,
|
||||
slot: u8,
|
||||
speed: u8,
|
||||
) -> Result<Ring> {
|
||||
let mut ring = Ring::new(true)?;
|
||||
|
||||
{
|
||||
@@ -418,7 +434,7 @@ impl Xhci {
|
||||
route_string
|
||||
| (u32::from(mtt) << 25)
|
||||
| (u32::from(hub) << 26)
|
||||
| (u32::from(context_entries) << 27)
|
||||
| (u32::from(context_entries) << 27),
|
||||
);
|
||||
|
||||
let max_exit_latency = 0u16;
|
||||
@@ -427,7 +443,7 @@ impl Xhci {
|
||||
slot_ctx.b.write(
|
||||
u32::from(max_exit_latency)
|
||||
| (u32::from(root_hub_port_num) << 16)
|
||||
| (u32::from(number_of_ports) << 24)
|
||||
| (u32::from(number_of_ports) << 24),
|
||||
);
|
||||
|
||||
// TODO
|
||||
@@ -441,12 +457,14 @@ impl Xhci {
|
||||
u32::from(parent_hud_slot_id)
|
||||
| (u32::from(parent_port_num) << 8)
|
||||
| (u32::from(ttt) << 16)
|
||||
| (u32::from(interrupter) << 22)
|
||||
| (u32::from(interrupter) << 22),
|
||||
);
|
||||
|
||||
let endp_ctx = &mut input_context.device.endpoints[0];
|
||||
|
||||
let speed_id = self.lookup_psiv(root_hub_port_num, speed).expect("Failed to retrieve speed ID");
|
||||
let speed_id = self
|
||||
.lookup_psiv(root_hub_port_num, speed)
|
||||
.expect("Failed to retrieve speed ID");
|
||||
|
||||
let max_error_count = 3u8; // recommended value according to the XHCI spec
|
||||
let ep_ty = 4u8; // control endpoint, bidirectional
|
||||
@@ -470,19 +488,20 @@ impl Xhci {
|
||||
| (u32::from(ep_ty) << 3)
|
||||
| (u32::from(host_initiate_disable) << 7)
|
||||
| (u32::from(max_burst_size) << 8)
|
||||
| (u32::from(max_packet_size) << 16)
|
||||
| (u32::from(max_packet_size) << 16),
|
||||
);
|
||||
|
||||
let tr = ring.register();
|
||||
endp_ctx.trh.write((tr >> 32) as u32);
|
||||
endp_ctx.trl.write(tr as u32);
|
||||
}
|
||||
|
||||
|
||||
let input_context_physical = input_context.physical();
|
||||
|
||||
self.execute_command("ADDRESS_DEVICE", |trb, cycle| {
|
||||
trb.address_device(slot, input_context_physical, false, cycle)
|
||||
}).expect("ADDRESS_DEVICE failed");
|
||||
})
|
||||
.expect("ADDRESS_DEVICE failed");
|
||||
Ok(ring)
|
||||
}
|
||||
|
||||
@@ -584,8 +603,7 @@ impl Xhci {
|
||||
})
|
||||
}
|
||||
pub fn supported_protocol(&self, port: u8) -> Option<&'static SupportedProtoCap> {
|
||||
self
|
||||
.supported_protocols_iter()
|
||||
self.supported_protocols_iter()
|
||||
.find(|supp_proto| supp_proto.compat_port_range().contains(&port))
|
||||
}
|
||||
pub fn supported_protocol_speeds(
|
||||
|
||||
+527
-169
File diff suppressed because it is too large
Load Diff
+20
-6
@@ -213,7 +213,10 @@ impl Trb {
|
||||
self.set(
|
||||
input_ctx_ptr as u64,
|
||||
0,
|
||||
(u32::from(slot_id) << 24) | ((TrbType::AddressDevice as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle),
|
||||
(u32::from(slot_id) << 24)
|
||||
| ((TrbType::AddressDevice as u32) << 10)
|
||||
| (u32::from(bsr) << 9)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
// Synchronizes the input context endpoints with the device context endpoints, I think.
|
||||
@@ -241,7 +244,10 @@ impl Trb {
|
||||
self.set(
|
||||
input_ctx_ptr as u64,
|
||||
0,
|
||||
(u32::from(slot_id) << 24) | ((TrbType::EvaluateContext as u32) << 10) | (u32::from(bsr) << 9) | u32::from(cycle),
|
||||
(u32::from(slot_id) << 24)
|
||||
| ((TrbType::EvaluateContext as u32) << 10)
|
||||
| (u32::from(bsr) << 9)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
pub fn reset_endpoint(&mut self, slot_id: u8, endp_num_xhc: u8, tsp: bool, cycle: bool) {
|
||||
@@ -257,7 +263,15 @@ impl Trb {
|
||||
);
|
||||
}
|
||||
/// The deque_ptr has to contain the DCS bit (bit 0).
|
||||
pub fn set_tr_deque_ptr(&mut self, deque_ptr: u64, cycle: bool, sct: StreamContextType, stream_id: u16, endp_num_xhc: u8, slot: u8) {
|
||||
pub fn set_tr_deque_ptr(
|
||||
&mut self,
|
||||
deque_ptr: u64,
|
||||
cycle: bool,
|
||||
sct: StreamContextType,
|
||||
stream_id: u16,
|
||||
endp_num_xhc: u8,
|
||||
slot: u8,
|
||||
) {
|
||||
assert_eq!(deque_ptr & 0xFFFF_FFFF_FFFF_FFF1, deque_ptr);
|
||||
assert_eq!(endp_num_xhc & 0x1F, endp_num_xhc);
|
||||
|
||||
@@ -298,7 +312,7 @@ impl Trb {
|
||||
| (u32::from(ioc) << 5)
|
||||
| (u32::from(ch) << 4)
|
||||
| (u32::from(ent) << 1)
|
||||
| u32::from(cycle)
|
||||
| u32::from(cycle),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -334,7 +348,7 @@ impl Trb {
|
||||
pub fn normal(
|
||||
&mut self,
|
||||
buffer: u64,
|
||||
len: u16,
|
||||
len: u32,
|
||||
cycle: bool,
|
||||
estimated_td_size: u8,
|
||||
interrupter: u8,
|
||||
@@ -349,7 +363,7 @@ 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(interrupter) << 22),
|
||||
len | (u32::from(estimated_td_size) << 17) | (u32::from(interrupter) << 22),
|
||||
u32::from(cycle)
|
||||
| (u32::from(ent) << 1)
|
||||
| (u32::from(isp) << 2)
|
||||
|
||||
Reference in New Issue
Block a user