Almost successfully read blocks using SCSI.
This commit is contained in:
@@ -64,7 +64,10 @@ fn main() {
|
||||
|
||||
//syscall::setrens(0, 0).expect("scsid: failed to enter null namespace");
|
||||
let mut scsi = Scsi::new(&mut *protocol);
|
||||
let mut scsi_scheme = ScsiScheme::new(&mut scsi);
|
||||
let mut buffer = [0u8; 512];
|
||||
scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
|
||||
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
|
||||
let mut scsi_scheme = ScsiScheme::new(&mut scsi, &mut *protocol);
|
||||
|
||||
// TODO: Use nonblocking and put all pending calls in a todo VecDeque. Use an eventfd as well.
|
||||
'scheme_loop: loop {
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::slice;
|
||||
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle};
|
||||
|
||||
use super::{Protocol, ProtocolError, SendCommandStatus};
|
||||
use super::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
||||
|
||||
pub const CBW_SIGNATURE: u32 = 0x43425355;
|
||||
|
||||
@@ -206,12 +206,15 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
|
||||
}
|
||||
|
||||
Ok(if csw.status == CswStatus::Passed as u8 {
|
||||
SendCommandStatus::Success
|
||||
} else if csw.status == CswStatus::Failed as u8 {
|
||||
SendCommandStatus::Failed { residue: NonZeroU32::new(csw.data_residue) }
|
||||
} else {
|
||||
return Err(ProtocolError::ProtocolError("bulk-only transport phase error, or other"));
|
||||
Ok(SendCommandStatus {
|
||||
kind: if csw.status == CswStatus::Passed as u8 {
|
||||
SendCommandStatusKind::Success
|
||||
} else if csw.status == CswStatus::Failed as u8 {
|
||||
SendCommandStatusKind::Failed
|
||||
} else {
|
||||
return Err(ProtocolError::ProtocolError("bulk-only transport phase error, or other"));
|
||||
},
|
||||
residue: NonZeroU32::new(csw.data_residue),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,26 @@ pub enum ProtocolError {
|
||||
ProtocolError(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SendCommandStatus {
|
||||
Success,
|
||||
Failed { residue: Option<NonZeroU32> },
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct SendCommandStatus {
|
||||
pub residue: Option<NonZeroU32>,
|
||||
pub kind: SendCommandStatusKind,
|
||||
}
|
||||
impl Default for SendCommandStatus {
|
||||
|
||||
impl SendCommandStatus {
|
||||
pub fn bytes_transferred(&self, transfer_len: u32) -> u32 {
|
||||
transfer_len - self.residue.map(u32::from).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SendCommandStatusKind {
|
||||
Success,
|
||||
Failed,
|
||||
}
|
||||
|
||||
|
||||
impl Default for SendCommandStatusKind {
|
||||
fn default() -> Self {
|
||||
Self::Success
|
||||
}
|
||||
|
||||
+51
-8
@@ -1,16 +1,18 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::str;
|
||||
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, ENOENT, ENOSYS};
|
||||
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::{SEEK_CUR, SEEK_END, SEEK_SET};
|
||||
|
||||
// TODO: Only one disk, right?
|
||||
const LIST_CONTENTS: &'static [u8] = b"disk\n";
|
||||
const LIST_CONTENTS: &'static [u8] = b"0\n";
|
||||
|
||||
enum Handle {
|
||||
List(usize),
|
||||
@@ -20,14 +22,16 @@ enum Handle {
|
||||
|
||||
pub struct ScsiScheme<'a> {
|
||||
scsi: &'a mut Scsi,
|
||||
protocol: &'a mut Protocol,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_fd: usize,
|
||||
}
|
||||
|
||||
impl<'a> ScsiScheme<'a> {
|
||||
pub fn new(scsi: &'a mut Scsi) -> Self {
|
||||
pub fn new(scsi: &'a mut Scsi, protocol: &'a mut dyn Protocol) -> Self {
|
||||
Self {
|
||||
scsi,
|
||||
protocol,
|
||||
handles: BTreeMap::new(),
|
||||
next_fd: 0,
|
||||
}
|
||||
@@ -58,12 +62,12 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
Handle::Disk(_) => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = self.scsi.get_disk_size();
|
||||
// TODO: stat.st_blocks
|
||||
stat.st_blksize = self.scsi.block_size;
|
||||
stat.st_blocks = self.scsi.block_count;
|
||||
}
|
||||
Handle::List(_) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = LIST_CONTENTS.len() as u64;
|
||||
// TODO: stat.st_blocks
|
||||
}
|
||||
}
|
||||
Err(Error::new(ENOSYS))
|
||||
@@ -72,10 +76,49 @@ impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(ref mut offset) => {
|
||||
let len = self.scsi.get_disk_size() as usize;
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::max(0, cmp::min(pos, len)),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(*offset + pos, len)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len + pos, len)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
Ok(*offset)
|
||||
}
|
||||
Handle::List(ref mut offset) => {
|
||||
let len = LIST_CONTENTS.len();
|
||||
*offset = match whence {
|
||||
SEEK_SET => cmp::max(0, cmp::min(pos, len)),
|
||||
SEEK_CUR => cmp::max(0, cmp::min(*offset + pos, len)),
|
||||
SEEK_END => cmp::max(0, cmp::min(len + pos, len)),
|
||||
_ => return Err(Error::new(EINVAL)),
|
||||
};
|
||||
Ok(*offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn read(&mut self, fd: usize, buf: &mut [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_read = self.scsi.read(self.protocol, lba, buf).map_err(|err| dbg!(err)).or(Err(Error::new(EIO)))?;
|
||||
Ok(bytes_read as usize)
|
||||
}
|
||||
Handle::List(ref mut offset) => {
|
||||
let max_bytes_to_read = cmp::min(LIST_CONTENTS.len(), buf.len());
|
||||
let bytes_to_read = cmp::max(max_bytes_to_read, *offset) - *offset;
|
||||
|
||||
buf[..bytes_to_read].copy_from_slice(&LIST_CONTENTS[..bytes_to_read]);
|
||||
*offset += bytes_to_read;
|
||||
|
||||
Ok(bytes_to_read)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
|
||||
+148
-201
@@ -1,187 +1,5 @@
|
||||
use std::{fmt, mem, slice};
|
||||
use super::opcodes::{Opcode, ServiceActionA3};
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct ReportIdentInfo {
|
||||
pub opcode: u8,
|
||||
/// bits 7:5 reserved
|
||||
pub serviceaction: u8,
|
||||
pub _rsvd: u16,
|
||||
pub restricted: u16,
|
||||
/// little endian
|
||||
pub alloc_len: u32,
|
||||
/// bit 0 reserved
|
||||
pub info_ty: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
unsafe impl plain::Plain for ReportIdentInfo {}
|
||||
|
||||
impl ReportIdentInfo {
|
||||
pub const fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::ServiceActionA3 as u8,
|
||||
serviceaction: ServiceActionA3::ReportIdentInfo as u8,
|
||||
_rsvd: 0,
|
||||
restricted: 0,
|
||||
alloc_len: u32::to_le(alloc_len),
|
||||
info_ty: (info_ty as u8) << REP_ID_INFO_INFO_TY_SHIFT,
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum ReportIdInfoInfoTy {
|
||||
PeripheralDevIdInfo = 0b000_0000,
|
||||
PeripheralDevTextIdInfo = 0b000_0010,
|
||||
IdentInfoSupp = 0b111_1111,
|
||||
// every other number ending with a 1 is restricted
|
||||
}
|
||||
|
||||
pub const REP_ID_INFO_INFO_TY_MASK: u8 = 0xFE;
|
||||
pub const REP_ID_INFO_INFO_TY_SHIFT: u8 = 1;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct ReportSuppOpcodes {
|
||||
pub opcode: u8,
|
||||
/// bits 7:5 reserved
|
||||
pub serviceaction: u8,
|
||||
/// bits 2:0 represent "REPORTING OPTIONS", bits 6:3 are reserved, and bit 7 is RCTD
|
||||
pub rep_opts: u8,
|
||||
pub req_opcode: u8,
|
||||
/// little endian
|
||||
pub req_serviceaction: u16,
|
||||
/// little endian
|
||||
pub alloc_len: u32,
|
||||
pub _rsvd: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
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 {
|
||||
opcode: Opcode::ServiceActionA3 as u8,
|
||||
serviceaction: ServiceActionA3::ReportSuppOpcodes as u8,
|
||||
rep_opts: ((rctd as u8) << REP_OPTS_RCTD_SHIFT) | rep_opts as u8,
|
||||
req_opcode,
|
||||
req_serviceaction: u16::to_le(req_serviceaction),
|
||||
alloc_len: u32::to_le(alloc_len),
|
||||
_rsvd: 0,
|
||||
control,
|
||||
}
|
||||
}
|
||||
pub const fn get_all(rctd: bool, alloc_len: u32, control: u8) -> Self {
|
||||
Self::new(ReportSuppOpcodesOptions::ListAll, rctd, 0, 0, alloc_len, control)
|
||||
}
|
||||
pub const fn get_supp_no_sa(rctd: bool, opcode: Opcode, alloc_len: u32, control: u8) -> Self {
|
||||
Self::new(ReportSuppOpcodesOptions::NoServicaction, rctd, opcode as u8, 0, alloc_len, control)
|
||||
}
|
||||
pub const fn get_supp(rctd: bool, opcode: Opcode, serviceaction: u16, alloc_len: u32, control: u8) -> Self {
|
||||
Self::new(ReportSuppOpcodesOptions::ExplicitBoth, rctd, opcode as u8, serviceaction, alloc_len, control)
|
||||
}
|
||||
}
|
||||
|
||||
pub const REP_OPTS_MAIN_MASK: u8 = 0b0000_0111;
|
||||
pub const REP_OPTS_MAIN_SHIFT: u8 = 0;
|
||||
pub const REP_OPTS_RCTD_BIT: u8 = 1 << REP_OPTS_RCTD_SHIFT;
|
||||
pub const REP_OPTS_RCTD_SHIFT: u8 = 7;
|
||||
|
||||
/// Valid values of the `req_opts` field of `ReportSuppOpcodes`.
|
||||
#[repr(u8)]
|
||||
pub enum ReportSuppOpcodesOptions {
|
||||
/// Returns all commands, no matter what parameters are set.
|
||||
ListAll,
|
||||
|
||||
/// Returns one command with the requested opcode. If the command has service actions, this
|
||||
/// command fails.
|
||||
NoServicaction,
|
||||
|
||||
/// Returns one command with the requested opcode and service action. If the command doesn't
|
||||
/// support service actions, this command fails.
|
||||
ExplicitBoth,
|
||||
|
||||
/// Returns one command with the requested opcode and service action. The command may or may
|
||||
/// not implement service actions, but if it does, it has to be correct for the return value to
|
||||
/// indicate SUPPORTED.
|
||||
///
|
||||
/// This option seems to be reserved for SPC-3 (inquiry version value of 5).
|
||||
IndicateSupport,
|
||||
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct AllCommandsParam {
|
||||
/// Little endian
|
||||
pub data_len: u32,
|
||||
pub descs: [CommandDescriptor; 0],
|
||||
}
|
||||
|
||||
impl AllCommandsParam {
|
||||
pub const fn alloc_len(&self) -> u32 {
|
||||
3 + u32::from_le(self.data_len)
|
||||
}
|
||||
pub unsafe fn descs(&self) -> &[CommandDescriptor] {
|
||||
assert_eq!(mem::size_of::<CommandDescriptor>(), 20);
|
||||
slice::from_raw_parts(&self.descs as *const CommandDescriptor, (self.alloc_len() as usize - 4) / mem::size_of::<CommandDescriptor>())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl plain::Plain for AllCommandsParam {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct CommandDescriptor {
|
||||
pub opcode: u8,
|
||||
pub _rsvd1: u8,
|
||||
/// little endian
|
||||
pub serviceaction: u16,
|
||||
pub _rsvd2: u8,
|
||||
/// bit 0 is SERVACTV, bit 1 is CTDP, and bits 7:2 reserved
|
||||
pub a: u8,
|
||||
/// little endian
|
||||
pub cdb_len: u16,
|
||||
pub cmd_timeouts_desc: [u8; 12],
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct OneCommandParam {
|
||||
pub _rsvd: u8,
|
||||
pub a: u8,
|
||||
pub cdb_size: u16,
|
||||
pub usage_data: [u8; 0],
|
||||
}
|
||||
unsafe impl plain::Plain for OneCommandParam {}
|
||||
|
||||
impl OneCommandParam {
|
||||
pub const fn ctdp(&self) -> bool {
|
||||
self.a & (1 << 7) != 0
|
||||
}
|
||||
pub fn support(&self) -> OneCommandParamSupport {
|
||||
let raw = self.a & 0b111;
|
||||
// Safe because all possible values are covered by the enum.
|
||||
unsafe { mem::transmute(raw) }
|
||||
}
|
||||
pub const fn total_len(&self) -> u16 {
|
||||
self.cdb_size as u16 + 3
|
||||
}
|
||||
/// Unsafe because the reference to self has to be valid for an additional (self.cdb_size - 1) bytes.
|
||||
pub unsafe fn cdb_usage_data(&self) -> &[u8] {
|
||||
slice::from_raw_parts(&self.usage_data as *const u8, self.total_len() as usize - 4)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum OneCommandParamSupport {
|
||||
NoDataAvail = 0b000,
|
||||
NotSupported = 0b001,
|
||||
Rsvd2 = 0b010,
|
||||
Supported = 0b011,
|
||||
Rsvd4 = 0b100,
|
||||
SupportedVendor = 0b101,
|
||||
Rsvd6 = 0b110,
|
||||
Rsvd7 = 0b111,
|
||||
}
|
||||
use super::opcodes::Opcode;
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct Inquiry {
|
||||
@@ -189,7 +7,7 @@ pub struct Inquiry {
|
||||
/// bits 7:2 are reserved, bit 1 (CMDDT) is obsolete, bit 0 is EVPD
|
||||
pub evpd: u8,
|
||||
pub page_code: u8,
|
||||
/// little endian
|
||||
/// big endian
|
||||
pub alloc_len: u16,
|
||||
pub control: u8,
|
||||
}
|
||||
@@ -201,7 +19,7 @@ impl Inquiry {
|
||||
opcode: Opcode::Inquiry as u8,
|
||||
evpd: evpd as u8,
|
||||
page_code,
|
||||
alloc_len: u16::to_le(alloc_len),
|
||||
alloc_len: u16::to_be(alloc_len),
|
||||
control,
|
||||
}
|
||||
}
|
||||
@@ -231,6 +49,33 @@ pub struct StandardInquiryData {
|
||||
_rsvd2: [u8; 22],
|
||||
}
|
||||
unsafe impl plain::Plain for StandardInquiryData {}
|
||||
impl StandardInquiryData {
|
||||
pub const fn periph_dev_ty(&self) -> u8 {
|
||||
self.a & 0x1F
|
||||
}
|
||||
pub const fn periph_dev_qual(&self) -> u8 {
|
||||
(self.a & 0xE0) >> 5
|
||||
}
|
||||
pub const fn version(&self) -> u8 {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum PeriphDeviceType {
|
||||
DirectAccess,
|
||||
SeqAccess,
|
||||
// there are more
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum InquiryVersion {
|
||||
NoConformance,
|
||||
Spc,
|
||||
Spc2,
|
||||
Spc3,
|
||||
Spc4,
|
||||
Spc5,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
@@ -269,7 +114,7 @@ pub struct FixedFormatSenseData {
|
||||
pub add_sense_code: u8,
|
||||
pub add_sense_code_qual: u8,
|
||||
pub field_replacable_unit_code: u8,
|
||||
pub sense_key_specific: [u8; 3], // little endian
|
||||
pub sense_key_specific: [u8; 3], // big endian
|
||||
pub add_sense_bytes: [u8; 0],
|
||||
}
|
||||
unsafe impl plain::Plain for FixedFormatSenseData {}
|
||||
@@ -326,6 +171,7 @@ pub struct Read16 {
|
||||
pub b: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
unsafe impl plain::Plain for Read16 {}
|
||||
|
||||
impl Read16 {
|
||||
pub const fn new(lba: u64, transfer_len: u32, control: u8) -> Self {
|
||||
@@ -335,8 +181,8 @@ impl Read16 {
|
||||
Self {
|
||||
opcode: Opcode::Read16 as u8,
|
||||
a: 0,
|
||||
lba: u64::to_le(lba),
|
||||
transfer_len: u32::to_le(transfer_len),
|
||||
lba: u64::to_be(lba),
|
||||
transfer_len: u32::to_be(transfer_len),
|
||||
b: 0,
|
||||
control,
|
||||
}
|
||||
@@ -389,7 +235,7 @@ impl ModeSense10 {
|
||||
b: page_code | ((pc as u8) << 6),
|
||||
subpage_code,
|
||||
_rsvd: [0u8; 3],
|
||||
alloc_len: u16::from_le(alloc_len),
|
||||
alloc_len: u16::from_be(alloc_len),
|
||||
control,
|
||||
}
|
||||
}
|
||||
@@ -407,7 +253,7 @@ pub enum ModePageControl {
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ShortLbaModeParamBlkDesc {
|
||||
pub block_count: u32,
|
||||
_rsvd: u8,
|
||||
@@ -417,21 +263,28 @@ unsafe impl plain::Plain for ShortLbaModeParamBlkDesc {}
|
||||
|
||||
impl ShortLbaModeParamBlkDesc {
|
||||
pub const fn block_count(&self) -> u32 {
|
||||
u32::from_le(self.block_count)
|
||||
u32::from_be(self.block_count)
|
||||
}
|
||||
pub const fn logical_block_len(&self) -> u32 {
|
||||
u24_le_to_u32(self.logical_block_len)
|
||||
u24_be_to_u32(self.logical_block_len)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for ShortLbaModeParamBlkDesc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("ShortLbaModeParamBlkDesc")
|
||||
.field("block_count", &self.block_count())
|
||||
.field("logical_block_len", &self.logical_block_len())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
const fn u24_le_to_u32(u24: [u8; 3]) -> u32 {
|
||||
const fn u24_be_to_u32(u24: [u8; 3]) -> u32 {
|
||||
((u24[0] as u32) << 16)
|
||||
| ((u24[1] as u32) << 8)
|
||||
| (u24[2] as u32)
|
||||
}
|
||||
|
||||
/// From SPC-3, when LONGLBA is set. For newer devices, `ShortLbaModeParamBlkDesc` is used instead (I
|
||||
/// think).
|
||||
/// 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.
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GeneralModeParamBlkDesc {
|
||||
@@ -442,12 +295,21 @@ pub struct GeneralModeParamBlkDesc {
|
||||
}
|
||||
unsafe impl plain::Plain for GeneralModeParamBlkDesc {}
|
||||
|
||||
impl GeneralModeParamBlkDesc {
|
||||
pub fn block_count(&self) -> u32 {
|
||||
u24_be_to_u32(self.block_count)
|
||||
}
|
||||
pub fn logical_block_len(&self) -> u32 {
|
||||
u24_be_to_u32(self.block_length)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GeneralModeParamBlkDesc {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("GeneralModeParamBlkDesc")
|
||||
.field("density_code", &self.density_code)
|
||||
.field("block_count", &u24_le_to_u32(self.block_count))
|
||||
.field("block_length", &u24_le_to_u32(self.block_length))
|
||||
.field("block_count", &u24_be_to_u32(self.block_count))
|
||||
.field("block_length", &u24_be_to_u32(self.block_length))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -463,10 +325,10 @@ unsafe impl plain::Plain for LongLbaModeParamBlkDesc {}
|
||||
|
||||
impl LongLbaModeParamBlkDesc {
|
||||
pub const fn block_count(&self) -> u64 {
|
||||
u64::from_le(self.block_count)
|
||||
u64::from_be(self.block_count)
|
||||
}
|
||||
pub const fn logical_block_len(&self) -> u32 {
|
||||
u32::from_le(self.logical_block_len)
|
||||
u32::from_be(self.logical_block_len)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,12 +355,97 @@ pub struct ModeParamHeader10 {
|
||||
unsafe impl plain::Plain for ModeParamHeader10 {}
|
||||
impl ModeParamHeader10 {
|
||||
pub const fn mode_data_len(&self) -> u16 {
|
||||
u16::from_le(self.mode_data_len)
|
||||
u16::from_be(self.mode_data_len)
|
||||
}
|
||||
pub const fn block_desc_len(&self) -> u16 {
|
||||
u16::from_le(self.block_desc_len)
|
||||
u16::from_be(self.block_desc_len)
|
||||
}
|
||||
pub const fn longlba(&self) -> bool {
|
||||
(self.b & 0x01) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RwErrorRecoveryPage {
|
||||
pub a: u8,
|
||||
pub page_length: u8,
|
||||
pub b: u8,
|
||||
pub read_retry_count: u8,
|
||||
_obsolete: [u8; 3],
|
||||
_rsvd: u8,
|
||||
pub recovery_time_limit: u16,
|
||||
}
|
||||
unsafe impl plain::Plain for RwErrorRecoveryPage {}
|
||||
|
||||
pub(crate) struct ModePageIterRaw<'a> {
|
||||
buffer: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ModePageIterRaw<'a> {
|
||||
type Item = &'a [u8];
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.buffer.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let a = self.buffer[0];
|
||||
let page_len = if a & (1 << 6) == 0 {
|
||||
// item is page_0 mode
|
||||
self.buffer[1] as usize + 1
|
||||
} else {
|
||||
// item is sub_page mode
|
||||
self.buffer[2] as usize + 3
|
||||
};
|
||||
if self.buffer.len() < page_len {
|
||||
return None;
|
||||
}
|
||||
let buffer = &self.buffer[..page_len];
|
||||
|
||||
self.buffer = if page_len == self.buffer.len() {
|
||||
&[]
|
||||
} else {
|
||||
&self.buffer[page_len..]
|
||||
};
|
||||
|
||||
Some(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum AnyModePage<'a> {
|
||||
RwErrorRecovery(&'a RwErrorRecoveryPage),
|
||||
}
|
||||
|
||||
struct ModePageIter<'a> {
|
||||
raw: ModePageIterRaw<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ModePageIter<'a> {
|
||||
type Item = AnyModePage<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next_buf = self.raw.next()?;
|
||||
let a = next_buf[0];
|
||||
|
||||
let page_code = a & 0x1F;
|
||||
let spf = a & (1 << 6) != 0;
|
||||
|
||||
if !spf {
|
||||
if page_code == 0x01 {
|
||||
Some(AnyModePage::RwErrorRecovery(plain::from_bytes(next_buf).ok()?))
|
||||
} else {
|
||||
println!("Unimplemented sub_page {}", base64::encode(next_buf));
|
||||
None
|
||||
}
|
||||
} else {
|
||||
println!("Unimplemented page_0 {}", base64::encode(next_buf));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mode_page_iter(buffer: &[u8]) -> impl Iterator<Item = AnyModePage> {
|
||||
ModePageIter { raw: ModePageIterRaw { buffer } }
|
||||
}
|
||||
|
||||
+79
-55
@@ -1,4 +1,5 @@
|
||||
use std::mem;
|
||||
use std::{mem, ops};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
pub mod cmds;
|
||||
pub mod opcodes;
|
||||
@@ -6,7 +7,7 @@ pub mod opcodes;
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::DeviceReqData;
|
||||
|
||||
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus};
|
||||
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
||||
use cmds::{SenseKey, StandardInquiryData};
|
||||
use opcodes::Opcode;
|
||||
|
||||
@@ -14,6 +15,8 @@ pub struct Scsi {
|
||||
command_buffer: [u8; 16],
|
||||
inquiry_buffer: [u8; 259],
|
||||
data_buffer: Vec<u8>,
|
||||
pub block_size: u32,
|
||||
pub block_count: u64,
|
||||
}
|
||||
|
||||
const INQUIRY_CMD_LEN: u8 = 6;
|
||||
@@ -35,6 +38,8 @@ impl Scsi {
|
||||
command_buffer: [0u8; 16],
|
||||
inquiry_buffer: [0u8; 259], // additional_len = 255 max
|
||||
data_buffer: Vec::new(),
|
||||
block_size: 0,
|
||||
block_count: 0,
|
||||
};
|
||||
|
||||
// Get the max length that the device supports, of the Standard Inquiry Data.
|
||||
@@ -43,7 +48,16 @@ impl Scsi {
|
||||
this.get_standard_inquiry_data(protocol, max_inquiry_len);
|
||||
this.res_standard_inquiry_data();
|
||||
|
||||
dbg!(this.get_mode_sense10(protocol).unwrap());
|
||||
let (block_size, block_count) = {
|
||||
let (_, blkdescs, mode_page_iter) = this.get_mode_sense10(protocol).unwrap();
|
||||
|
||||
// TODO: Can there be multiple disks at all?
|
||||
let only_blkdesc = blkdescs.get(0).unwrap();
|
||||
(only_blkdesc.block_size(), only_blkdesc.block_count())
|
||||
};
|
||||
|
||||
this.block_size = block_size;
|
||||
this.block_count = block_count;
|
||||
|
||||
this
|
||||
}
|
||||
@@ -58,51 +72,18 @@ impl Scsi {
|
||||
|
||||
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");
|
||||
}
|
||||
/*/// Similar to `check_supp_opcode_sized`, but simply checks whether the opcode is supported,
|
||||
/// without fetching any actual data.
|
||||
pub fn check_supp_opcode(&mut self, protocol: &mut dyn Protocol, opcode: Opcode, sa: Option<u16>) -> Result<bool, ScsiError> {
|
||||
self.check_supp_opcode_sized(protocol, opcode, sa, 2)
|
||||
}
|
||||
pub fn check_supp_opcode_sized(&mut self, protocol: &mut dyn Protocol, opcode: Opcode, sa: Option<u16>, alloc_len: u32) -> Result<bool, ScsiError> {
|
||||
let report_supp_opcodes = self.cmd_report_supp_opcodes();
|
||||
*report_supp_opcodes = if let Some(serviceaction) = sa {
|
||||
cmds::ReportSuppOpcodes::get_supp(false, opcode, serviceaction, alloc_len, 0)
|
||||
} else {
|
||||
cmds::ReportSuppOpcodes::get_supp_no_sa(false, opcode, alloc_len, 0)
|
||||
};
|
||||
self.data_buffer.resize(std::mem::size_of::<cmds::OneCommandParam>(), 0);
|
||||
protocol.send_command(&self.command_buffer[..REPORT_SUPP_OPCODES_CMD_LEN as usize], DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize]))?;
|
||||
Ok(self.res_one_command().support() == cmds::OneCommandParamSupport::Supported)
|
||||
}*/
|
||||
|
||||
/*pub fn get_supp_opcodes_alloc_len(&mut self, protocol: &mut dyn Protocol) -> u32 {
|
||||
self.get_supp_opcodes(protocol, MIN_REPORT_SUPP_OPCODES_ALLOC_LEN);
|
||||
self.res_all_commands().alloc_len()
|
||||
}*/
|
||||
/*pub fn get_supp_opcodes(&mut self, protocol: &mut dyn Protocol, alloc_len: u32) {
|
||||
let report_supp_opcodes = self.cmd_report_supp_opcodes();
|
||||
*report_supp_opcodes = cmds::ReportSuppOpcodes::get_all(false, alloc_len, 0);
|
||||
self.data_buffer.resize(alloc_len as usize, 0);
|
||||
let status = protocol.send_command(&self.command_buffer[..REPORT_SUPP_OPCODES_CMD_LEN as usize], DeviceReqData::In(&mut self.data_buffer[..alloc_len as usize])).expect("Failed to send REPORT_SUPP_OPCODES command");
|
||||
if status != SendCommandStatus::Success {
|
||||
self.get_ff_sense(protocol, cmds::RequestSense::MINIMAL_ALLOC_LEN);
|
||||
let data = self.res_ff_sense_data();
|
||||
if data.sense_key() == SenseKey::IllegalRequest && data.add_sense_code == cmds::ADD_SENSE_CODE05_INVAL_CDB_FIELD {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
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");
|
||||
}
|
||||
pub fn get_mode_sense10(&mut self, protocol: &mut dyn Protocol) -> Result<(&cmds::ModeParamHeader10, BlkDescSlice), 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::Failed { .. } = protocol.send_command(&self.command_buffer[..10], DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]))? {
|
||||
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());
|
||||
}
|
||||
@@ -113,7 +94,7 @@ impl Scsi {
|
||||
*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()))
|
||||
Ok((self.res_mode_param_header10(), self.res_blkdesc_mode10(), self.res_mode_pages10()))
|
||||
}
|
||||
|
||||
pub fn cmd_inquiry(&mut self) -> &mut cmds::Inquiry {
|
||||
@@ -125,22 +106,15 @@ impl Scsi {
|
||||
pub fn cmd_mode_sense10(&mut self) -> &mut cmds::ModeSense10 {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
/*pub fn cmd_report_supp_opcodes(&mut self) -> &mut cmds::ReportSuppOpcodes {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}*/
|
||||
pub fn cmd_request_sense(&mut self) -> &mut cmds::RequestSense {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
pub fn cmd_read16(&mut self) -> &mut cmds::Read16 {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
pub fn res_standard_inquiry_data(&self) -> &StandardInquiryData {
|
||||
plain::from_bytes(&self.inquiry_buffer).unwrap()
|
||||
}
|
||||
/*
|
||||
pub fn res_all_commands(&self) -> &cmds::AllCommandsParam {
|
||||
plain::from_bytes(&self.data_buffer).unwrap()
|
||||
}
|
||||
pub fn res_one_command(&self) -> &cmds::OneCommandParam {
|
||||
plain::from_bytes(&self.data_buffer).unwrap()
|
||||
}*/
|
||||
pub fn res_ff_sense_data(&self) -> &cmds::FixedFormatSenseData {
|
||||
plain::from_bytes(&self.data_buffer).unwrap()
|
||||
}
|
||||
@@ -158,21 +132,71 @@ impl Scsi {
|
||||
pub fn res_blkdesc_mode10(&self) -> BlkDescSlice {
|
||||
let header = self.res_mode_param_header10();
|
||||
let descs_start = mem::size_of::<cmds::ModeParamHeader10>();
|
||||
println!("MODE_SENSE PAGES: {}", base64::encode(&self.data_buffer[descs_start + header.block_desc_len() as usize..]));
|
||||
if header.longlba() {
|
||||
BlkDescSlice::Long(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::General(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())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn res_mode_pages10(&self) -> impl Iterator<Item = cmds::AnyModePage> {
|
||||
let header = self.res_mode_param_header10();
|
||||
let descs_start = mem::size_of::<cmds::ModeParamHeader10>();
|
||||
let buffer = &self.data_buffer[descs_start + header.block_desc_len() as usize..];
|
||||
cmds::mode_page_iter(buffer)
|
||||
}
|
||||
pub fn get_disk_size(&mut self) -> u64 {
|
||||
todo!()
|
||||
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> {
|
||||
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 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))
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum BlkDescSlice<'a> {
|
||||
//Short(&'a [cmds::ShortLbaModeParamBlkDesc]),
|
||||
Short(&'a [cmds::ShortLbaModeParamBlkDesc]),
|
||||
General(&'a [cmds::GeneralModeParamBlkDesc]),
|
||||
Long(&'a [cmds::LongLbaModeParamBlkDesc]),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlkDesc<'a> {
|
||||
Short(&'a cmds::ShortLbaModeParamBlkDesc),
|
||||
General(&'a cmds::GeneralModeParamBlkDesc),
|
||||
Long(&'a cmds::LongLbaModeParamBlkDesc),
|
||||
}
|
||||
impl<'a> BlkDesc<'a> {
|
||||
fn block_size(&self) -> u32 {
|
||||
match self {
|
||||
Self::Short(s) => s.logical_block_len(),
|
||||
Self::General(g) => g.logical_block_len(),
|
||||
Self::Long(l) => l.logical_block_len(),
|
||||
}
|
||||
}
|
||||
fn block_count(&self) -> u64 {
|
||||
match self {
|
||||
Self::Short(s) => s.block_count().into(),
|
||||
Self::General(g) => g.block_count().into(),
|
||||
Self::Long(l) => l.block_count(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BlkDescSlice<'a> {
|
||||
fn get(&self, idx: usize) -> Option<BlkDesc<'a>> {
|
||||
match self {
|
||||
Self::Short(s) => s.get(idx).map(BlkDesc::Short),
|
||||
Self::Long(l) => l.get(idx).map(BlkDesc::Long),
|
||||
Self::General(g) => g.get(idx).map(BlkDesc::General),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-64
@@ -298,22 +298,15 @@ impl Xhci {
|
||||
println!(" - XHCI initialized");
|
||||
}
|
||||
|
||||
pub fn enable_port_slot(cmd: &mut CommandRing, dbs: &mut [Doorbell]) -> u8 {
|
||||
let (cmd, cycle, event) = cmd.next();
|
||||
pub fn enable_port_slot(&mut self, slot_ty: u8) -> Result<u8> {
|
||||
assert_eq!(slot_ty & 0x1F, slot_ty);
|
||||
|
||||
cmd.enable_slot(0, cycle);
|
||||
|
||||
dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
let slot = (event.control.read() >> 24) as u8;
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
|
||||
slot
|
||||
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<()> {
|
||||
self.execute_command("DISABLE_SLOT", |cmd, cycle| cmd.enable_slot(0, cycle))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn slot_state(&self, slot: usize) -> u8 {
|
||||
@@ -336,14 +329,13 @@ impl Xhci {
|
||||
|
||||
println!(" - Enable slot");
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
|
||||
let slot = Self::enable_port_slot(&mut self.cmd, &mut self.dbs);
|
||||
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);
|
||||
|
||||
let mut input = Dma::<InputContext>::zeroed()?;
|
||||
let mut ring = self.address_device(&mut input, i, slot, speed)?;
|
||||
let mut ring = self.address_device(&mut input, i, slot_ty, slot, speed)?;
|
||||
|
||||
let dev_desc = Self::get_dev_desc_raw(
|
||||
&mut self.ports,
|
||||
@@ -401,31 +393,14 @@ impl Xhci {
|
||||
b &= 0x0000_FFFF;
|
||||
b |= (new_max_packet_size) << 16;
|
||||
endp_ctx.b.write(b);
|
||||
|
||||
{
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
|
||||
cmd.evaluate_context(slot_id, input_context.physical(), false, cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
panic!("EVALUATE_CONTEXT failed with {:#0x} {:#0x} {:#0x}", event.data.read(), event.status.read(), event.control.read());
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
}
|
||||
|
||||
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: 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)?;
|
||||
|
||||
{
|
||||
@@ -502,27 +477,12 @@ impl Xhci {
|
||||
endp_ctx.trh.write((tr >> 32) as u32);
|
||||
endp_ctx.trl.write(tr as u32);
|
||||
}
|
||||
|
||||
let input_context_physical = input_context.physical();
|
||||
|
||||
{
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
|
||||
cmd.address_device(slot, input_context.physical(), false, cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
panic!("ADDRESS DEVICE FAILED");
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
}
|
||||
self.execute_command("ADDRESS_DEVICE", |trb, cycle| {
|
||||
trb.address_device(slot, input_context_physical, false, cycle)
|
||||
}).expect("ADDRESS_DEVICE failed");
|
||||
Ok(ring)
|
||||
}
|
||||
|
||||
@@ -623,6 +583,11 @@ impl Xhci {
|
||||
}
|
||||
})
|
||||
}
|
||||
pub fn supported_protocol(&self, port: u8) -> Option<&'static SupportedProtoCap> {
|
||||
self
|
||||
.supported_protocols_iter()
|
||||
.find(|supp_proto| supp_proto.compat_port_range().contains(&port))
|
||||
}
|
||||
pub fn supported_protocol_speeds(
|
||||
&self,
|
||||
port: u8,
|
||||
@@ -690,9 +655,8 @@ impl Xhci {
|
||||
| 7 << PROTO_SPEED_PSIV_SHIFT,
|
||||
),
|
||||
];
|
||||
let supp_proto = self
|
||||
.supported_protocols_iter()
|
||||
.find(|supp_proto| supp_proto.compat_port_range().contains(&port))?;
|
||||
|
||||
let supp_proto = self.supported_protocol(port)?;
|
||||
|
||||
Some(if supp_proto.psic() != 0 {
|
||||
unsafe { supp_proto.protocol_speeds().iter() }
|
||||
|
||||
+234
-255
@@ -25,11 +25,16 @@ use super::extended::ProtocolSpeed;
|
||||
use super::operational::OperationalRegs;
|
||||
use super::ring::Ring;
|
||||
use super::runtime::RuntimeRegs;
|
||||
use super::trb::{TransferKind, TrbCompletionCode, TrbType};
|
||||
use super::trb::{TransferKind, Trb, TrbCompletionCode, TrbType};
|
||||
use super::usb::endpoint::{EndpointTy, ENDP_ATTR_TY_MASK};
|
||||
|
||||
use crate::driver_interface::*;
|
||||
|
||||
pub enum ControlFlow {
|
||||
Continue,
|
||||
Break,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum EndpIfState {
|
||||
Init,
|
||||
@@ -200,47 +205,149 @@ impl AnyDescriptor {
|
||||
}
|
||||
|
||||
impl Xhci {
|
||||
fn device_req_no_data(&mut self, port: usize, req: usb::Setup) -> Result<()> {
|
||||
let ps = self.port_states.get_mut(&port).ok_or(Error::new(EIO))?;
|
||||
let ring = ps
|
||||
.endpoint_states
|
||||
.get_mut(&0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.ring()
|
||||
.ok_or(Error::new(EIO))?;
|
||||
pub fn execute_command<F: FnOnce(&mut Trb, bool)>(&mut self, cmd_name: &str, f: F) -> Result<Trb> {
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
|
||||
f(cmd, cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - {} Waiting for event", cmd_name);
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("{} failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", cmd_name, event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
let ret = event.clone();
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
Ok(ret)
|
||||
}
|
||||
pub fn execute_control_transfer<D>(&mut self, port_num: usize, setup: usb::Setup, tk: TransferKind, name: &str, mut d: D) -> Result<Trb>
|
||||
where
|
||||
D: FnMut(&mut Trb, bool) -> ControlFlow,
|
||||
{
|
||||
let slot = self.port_state(port_num)?.slot;
|
||||
let ring = self.endpoint_state_mut(port_num, 0)?.ring().ok_or(Error::new(EIO))?;
|
||||
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.setup(req, TransferKind::NoData, cycle);
|
||||
cmd.setup(setup, tk, cycle);
|
||||
}
|
||||
if tk != TransferKind::NoData {
|
||||
loop {
|
||||
let (trb, cycle) = ring.next();
|
||||
match d(trb, cycle) {
|
||||
ControlFlow::Break => break,
|
||||
ControlFlow::Continue => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.status(false, cycle);
|
||||
cmd.status(tk == TransferKind::In, cycle);
|
||||
}
|
||||
self.dbs[ps.slot as usize].write(Self::def_control_endp_doorbell());
|
||||
self.dbs[usize::from(slot)].write(Self::def_control_endp_doorbell());
|
||||
|
||||
{
|
||||
let cloned_trb = {
|
||||
let event = self.cmd.next_event();
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
let status = event.status.read();
|
||||
let control = event.control.read();
|
||||
|
||||
if (status >> 24) != TrbCompletionCode::Success as u32 {
|
||||
println!("DEVICE_REQ ERROR, COMPLETION CODE {:#0x}", (status >> 24));
|
||||
println!(" - {} Waiting for event", name);
|
||||
}
|
||||
|
||||
println!(
|
||||
"DEVICE_REQ EVENT {:#0x} {:#0x} {:#0x}",
|
||||
event.data.read(),
|
||||
status,
|
||||
control
|
||||
);
|
||||
}
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8 || event.trb_type() != TrbType::Transfer as u8 {
|
||||
println!("{} CONTROL TRANSFER ERROR, EVENT TRB {:#0x} {:#0x} {:#0}»", name, event.data.read(), event.status.read(), event.control.read());
|
||||
}
|
||||
event.clone()
|
||||
};
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
|
||||
Ok(cloned_trb)
|
||||
}
|
||||
/// NOTE: There has to be AT LEAST one successful invocation of `d`, that actually updates the
|
||||
/// TRB (it could be a NO-OP in the worst case).
|
||||
pub fn execute_transfer<D>(&mut self, port_num: usize, endp_num: u8, stream_id: u16, name: &str, mut d: D) -> Result<Trb>
|
||||
where
|
||||
D: FnMut(&mut Trb, bool) -> ControlFlow,
|
||||
{
|
||||
let port_state = self.port_state_mut(port_num)?;
|
||||
let slot = port_state.slot;
|
||||
let endp_state = port_state
|
||||
.endpoint_states
|
||||
.get_mut(&endp_num)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
|
||||
let ring: &mut Ring = match endp_state {
|
||||
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))?
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let (trb, cycle) = ring.next();
|
||||
match d(trb, cycle) {
|
||||
ControlFlow::Break => break,
|
||||
ControlFlow::Continue => continue,
|
||||
}
|
||||
}
|
||||
|
||||
self.dbs[usize::from(slot)].write(Self::endp_doorbell(endp_num, self.endp_desc(port_num, endp_num)?, stream_id));
|
||||
|
||||
let cloned_trb = {
|
||||
let event = self.cmd.next_event();
|
||||
while event.data.read() == 0 {
|
||||
println!(" - {} Waiting for event", name);
|
||||
}
|
||||
|
||||
// FIXME: EDTLA if event data was set
|
||||
if event.completion_code() != TrbCompletionCode::ShortPacket as u8
|
||||
&& event.transfer_length() != 0
|
||||
{
|
||||
println!(
|
||||
"Event trb didn't yield a short packet, but some bytes were not transferred"
|
||||
);
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::Transfer as u8
|
||||
{
|
||||
println!(
|
||||
"Custom transfer event failed with {:#0x} {:#0x} {:#0x}",
|
||||
event.data.read(),
|
||||
event.status.read(),
|
||||
event.control.read()
|
||||
);
|
||||
}
|
||||
// TODO: Handle event data
|
||||
println!("EVENT DATA: {:?}", event.event_data());
|
||||
|
||||
let cloned_trb = event.clone();
|
||||
event.reserved(false);
|
||||
|
||||
cloned_trb
|
||||
};
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
|
||||
Ok(cloned_trb)
|
||||
}
|
||||
fn device_req_no_data(&mut self, port: usize, req: usb::Setup) -> Result<()> {
|
||||
self.execute_control_transfer(port, req, TransferKind::NoData, "DEVICE_REQ_NO_DATA", |_, _| ControlFlow::Break)?;
|
||||
Ok(())
|
||||
}
|
||||
fn set_configuration(&mut self, port: usize, config: u8) -> Result<()> {
|
||||
@@ -267,26 +374,7 @@ impl Xhci {
|
||||
.ok_or(Error::new(EBADF))?
|
||||
.slot;
|
||||
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
cmd.reset_endpoint(slot, endp_num_xhc, tsp, cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("RESET_ENDPOINT failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
self.execute_command("RESET_ENDPOINT", |trb, cycle| trb.reset_endpoint(slot, endp_num_xhc, tsp, cycle))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -349,6 +437,37 @@ impl Xhci {
|
||||
}
|
||||
}
|
||||
|
||||
fn port_state(&self, port: usize) -> Result<&super::PortState> {
|
||||
self.port_states.get(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn port_state_mut(&mut self, port: usize) -> Result<&mut super::PortState> {
|
||||
self.port_states.get_mut(&port).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn endpoint_state_mut(&mut self, port: usize, endp_num: u8) -> Result<&mut EndpointState> {
|
||||
self.port_state_mut(port)?.endpoint_states.get_mut(&endp_num).ok_or(Error::new(EBADF))
|
||||
}
|
||||
fn input_context(&mut self, port: usize) -> Result<&mut Dma<InputContext>> {
|
||||
Ok(&mut self.port_state_mut(port)?.input_context)
|
||||
}
|
||||
fn endp_ctx(&mut self, port: usize, endp_num_xhc: u8) -> Result<&mut super::context::EndpointContext> {
|
||||
Ok(self.input_context(port)?
|
||||
.device
|
||||
.endpoints
|
||||
.get_mut(endp_num_xhc as usize - 1)
|
||||
.ok_or(Error::new(EIO))?)
|
||||
}
|
||||
fn dev_desc(&self, port: usize) -> Result<&DevDesc> {
|
||||
Ok(&self.port_state(port)?.dev_desc)
|
||||
}
|
||||
fn config_descs(&self, port: usize) -> Result<&[ConfDesc]> {
|
||||
Ok(&self.dev_desc(port)?.config_descs)
|
||||
}
|
||||
fn config_desc(&self, port: usize, desc: u8) -> Result<&ConfDesc> {
|
||||
Ok(self.config_descs(port)?.get(usize::from(desc)).ok_or(Error::new(EBADF))?)
|
||||
}
|
||||
fn endp_descs(&self, port: usize, config_desc: u8, if_desc: u8) -> Result<&[EndpDesc]> {
|
||||
Ok(&self.port_state(port)?.dev_desc.config_descs.get(usize::from(config_desc)).ok_or(Error::new(EIO))?.interface_descs.get(usize::from(if_desc)).ok_or(Error::new(EIO))?.endpoints)
|
||||
}
|
||||
fn configure_endpoints(&mut self, port: usize, json_buf: &[u8]) -> Result<()> {
|
||||
let mut req: ConfigureEndpointsReq =
|
||||
serde_json::from_slice(json_buf).or(Err(Error::new(EBADMSG)))?;
|
||||
@@ -365,67 +484,59 @@ impl Xhci {
|
||||
return Err(Error::new(EBADMSG));
|
||||
}
|
||||
|
||||
let (endp_desc_count, new_context_entries) = {
|
||||
let endpoints = self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?;
|
||||
|
||||
if endpoints.len() >= 31 {
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
(endpoints.len(), (match endpoints.last() {
|
||||
Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l),
|
||||
None => 1,
|
||||
}) + 1)
|
||||
};
|
||||
let lec = self.cap.lec();
|
||||
let max_psa_size = self.cap.max_psa_size();
|
||||
|
||||
let port_speed_id = self.ports[port].speed();
|
||||
let speed_id: &ProtocolSpeed = self.lookup_psiv(port as u8, port_speed_id).ok_or(Error::new(EIO))?;
|
||||
|
||||
let port_state = self.port_states.get_mut(&port).ok_or(Error::new(ENOENT))?;
|
||||
let input_context: &mut Dma<InputContext> = &mut port_state.input_context;
|
||||
{
|
||||
let input_context = self.input_context(port)?;
|
||||
|
||||
// Configure the slot context as well, which holds the last index of the endp descs.
|
||||
input_context.add_context.write(1);
|
||||
input_context.drop_context.write(0);
|
||||
// Configure the slot context as well, which holds the last index of the endp descs.
|
||||
input_context.add_context.write(1);
|
||||
input_context.drop_context.write(0);
|
||||
|
||||
const CONTEXT_ENTRIES_MASK: u32 = 0xF800_0000;
|
||||
const CONTEXT_ENTRIES_SHIFT: u8 = 27;
|
||||
const CONTEXT_ENTRIES_MASK: u32 = 0xF800_0000;
|
||||
const CONTEXT_ENTRIES_SHIFT: u8 = 27;
|
||||
|
||||
let current_slot_a = input_context.device.slot.a.read();
|
||||
let current_slot_a = input_context.device.slot.a.read();
|
||||
|
||||
let dev_desc = &port_state.dev_desc;
|
||||
let endpoints = &dev_desc
|
||||
.config_descs
|
||||
.get(req.config_desc as usize)
|
||||
.ok_or(Error::new(EBADMSG))?
|
||||
.interface_descs.get(req.interface_desc.unwrap_or(0) as usize).ok_or(Error::new(EBADMSG))?
|
||||
.endpoints;
|
||||
|
||||
if endpoints.len() >= 31 {
|
||||
return Err(Error::new(EIO));
|
||||
input_context.device.slot.a.write(
|
||||
(current_slot_a & !CONTEXT_ENTRIES_MASK)
|
||||
| ((u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT)
|
||||
& CONTEXT_ENTRIES_MASK),
|
||||
);
|
||||
input_context.control.write(
|
||||
(u32::from(req.alternate_setting.unwrap_or(0)) << 16)
|
||||
| (u32::from(req.interface_desc.unwrap_or(0)) << 8)
|
||||
| u32::from(req.config_desc),
|
||||
);
|
||||
}
|
||||
|
||||
let new_context_entries = match endpoints.last() {
|
||||
Some(l) => Self::endp_num_to_dci(endpoints.len() as u8, l),
|
||||
None => 1,
|
||||
} + 1;
|
||||
|
||||
input_context.device.slot.a.write(
|
||||
(current_slot_a & !CONTEXT_ENTRIES_MASK)
|
||||
| ((u32::from(new_context_entries) << CONTEXT_ENTRIES_SHIFT)
|
||||
& CONTEXT_ENTRIES_MASK),
|
||||
);
|
||||
input_context.control.write(
|
||||
(u32::from(req.alternate_setting.unwrap_or(0)) << 16)
|
||||
| (u32::from(req.interface_desc.unwrap_or(0)) << 8)
|
||||
| u32::from(req.config_desc),
|
||||
);
|
||||
|
||||
let lec = self.cap.lec();
|
||||
|
||||
for endp_idx in 0..endpoints.len() as u8 {
|
||||
for endp_idx in 0..endp_desc_count as u8 {
|
||||
let endp_num = endp_idx + 1;
|
||||
|
||||
let endpoints = self.endp_descs(port, req.config_desc, req.interface_desc.unwrap_or(0))?;
|
||||
let dev_desc = self.dev_desc(port)?;
|
||||
let endp_desc = endpoints.get(endp_idx as usize).ok_or(Error::new(EIO))?;
|
||||
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, endp_desc);
|
||||
|
||||
input_context.add_context.writef(1 << endp_num_xhc, true);
|
||||
|
||||
let endp_ctx = input_context
|
||||
.device
|
||||
.endpoints
|
||||
.get_mut(endp_num_xhc as usize - 1)
|
||||
.ok_or(Error::new(EIO))?;
|
||||
|
||||
let max_streams = endp_desc.max_streams();
|
||||
let max_psa_size = self.cap.max_psa_size();
|
||||
|
||||
// TODO: Secondary streams.
|
||||
let primary_streams = if max_streams != 0 {
|
||||
@@ -467,6 +578,8 @@ impl Xhci {
|
||||
assert_eq!(max_error_count & 0x3, max_error_count);
|
||||
assert_ne!(ep_ty, 0); // 0 means invalid.
|
||||
|
||||
let port_state = self.port_state_mut(port)?;
|
||||
|
||||
let ring_ptr = if max_streams != 0 {
|
||||
let mut array = StreamContextArray::new(1 << (max_streams + 1))?;
|
||||
|
||||
@@ -502,6 +615,11 @@ impl Xhci {
|
||||
};
|
||||
assert_eq!(primary_streams & 0x1F, primary_streams);
|
||||
|
||||
let input_context = self.input_context(port)?;
|
||||
input_context.add_context.writef(1 << endp_num_xhc, true);
|
||||
|
||||
let endp_ctx = self.endp_ctx(port, endp_num_xhc)?;
|
||||
|
||||
endp_ctx.a.write(
|
||||
u32::from(mult) << 8
|
||||
| u32::from(primary_streams) << 10
|
||||
@@ -526,37 +644,12 @@ impl Xhci {
|
||||
);
|
||||
}
|
||||
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
|
||||
{
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
cmd.configure_endpoint(port_state.slot, input_context.physical(), cycle);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("CONFIGURE_ENDPOINT failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
}
|
||||
let slot = self.port_state(port)?.slot;
|
||||
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 = port_state
|
||||
.dev_desc
|
||||
.config_descs
|
||||
.get(req.config_desc as usize)
|
||||
.ok_or(Error::new(EIO))?
|
||||
.configuration_value;
|
||||
let configuration_value = self.config_desc(port, req.config_desc)?.configuration_value;
|
||||
self.set_configuration(port, configuration_value)?;
|
||||
|
||||
if let (Some(interface_num), Some(alternate_setting)) =
|
||||
@@ -609,7 +702,7 @@ impl Xhci {
|
||||
} else { unreachable!() }
|
||||
}
|
||||
fn endp_desc(&self, port_num: usize, endp_num: u8) -> Result<&EndpDesc> {
|
||||
self.port_states.get(&port_num).ok_or(Error::new(EIO))?.dev_desc.config_descs.first().ok_or(Error::new(EIO))?.interface_descs.first().ok_or(Error::new(EIO))?.endpoints.get(endp_num as usize - 1).ok_or(Error::new(EIO))
|
||||
Ok(self.endp_descs(port_num, 0, 0)?.get(usize::from(endp_num) - 1).ok_or(Error::new(EBADF))?)
|
||||
}
|
||||
fn endp_doorbell(endp_num: u8, desc: &EndpDesc, stream_id: u16) -> u32 {
|
||||
let db_target = Self::endp_num_to_dci(endp_num, desc);
|
||||
@@ -667,25 +760,11 @@ impl Xhci {
|
||||
return Err(Error::new(EBADF));
|
||||
}
|
||||
|
||||
let endp_state = port_state
|
||||
.endpoint_states
|
||||
.get_mut(&endp_num)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
let ring: &mut Ring = match endp_state {
|
||||
EndpointState { 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))?
|
||||
}
|
||||
};
|
||||
// TODO: Scatter-gather transfers, possibly allowing >64KiB sizes.
|
||||
let len = u16::try_from(buf.len()).or(Err(Error::new(ENOSYS)))?;
|
||||
let max_packet_size = endp_desc.max_packet_size;
|
||||
{
|
||||
let (trb, cycle) = ring.next();
|
||||
|
||||
let (buffer, idt, estimated_td_size) = {
|
||||
let (buffer, idt) = if len <= 8 && max_packet_size >= 8 && direction != EndpDirection::In {
|
||||
buf.map_buf(|sbuf| {
|
||||
let mut bytes = [0u8; 8];
|
||||
@@ -700,7 +779,12 @@ impl Xhci {
|
||||
false,
|
||||
)
|
||||
};
|
||||
let estimated_td_size = mem::size_of_val(&trb) as u8; // one trb per td
|
||||
let estimated_td_size = mem::size_of::<Trb>() as u8; // one trb per td
|
||||
(buffer, idt, estimated_td_size)
|
||||
};
|
||||
|
||||
let stream_id = 1u16;
|
||||
let event = self.execute_transfer(port_num, endp_num, stream_id, "CUSTOM_TRANSFER", |trb, cycle| {
|
||||
trb.normal(
|
||||
buffer,
|
||||
len,
|
||||
@@ -714,50 +798,16 @@ impl Xhci {
|
||||
idt,
|
||||
false,
|
||||
);
|
||||
}
|
||||
ControlFlow::Break
|
||||
})?;
|
||||
|
||||
let stream_id = 1u16;
|
||||
self.dbs[port_state.slot as usize].write(Self::endp_doorbell(endp_num, self.endp_desc(port_num, endp_num)?, stream_id));
|
||||
|
||||
let (completion_code, bytes_transferred) = {
|
||||
let event = self.cmd.next_event();
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
// FIXME: EDTLA if event data was set
|
||||
if event.completion_code() != TrbCompletionCode::ShortPacket as u8
|
||||
&& event.transfer_length() != 0
|
||||
{
|
||||
println!(
|
||||
"Event trb didn't yield a short packet, but some bytes were not transferred"
|
||||
);
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::Transfer as u8
|
||||
{
|
||||
println!(
|
||||
"Custom transfer event failed with {:#0x} {:#0x} {:#0x}",
|
||||
event.data.read(),
|
||||
event.status.read(),
|
||||
event.control.read()
|
||||
);
|
||||
}
|
||||
// TODO: Handle event data
|
||||
println!("EVENT DATA: {:?}", event.event_data());
|
||||
|
||||
(
|
||||
event.completion_code(),
|
||||
u32::from(len) - event.transfer_length(),
|
||||
)
|
||||
};
|
||||
let bytes_transferred = u32::from(len) - event.transfer_length();
|
||||
|
||||
if let DeviceReqData::In(dbuf) = &mut buf {
|
||||
dbuf.copy_from_slice(&*dma_buffer.as_ref().unwrap());
|
||||
}
|
||||
|
||||
Ok((completion_code, bytes_transferred as u32))
|
||||
Ok((event.completion_code(), bytes_transferred))
|
||||
}
|
||||
pub(crate) fn get_dev_desc(&mut self, port_id: usize) -> Result<DevDesc> {
|
||||
let st = self
|
||||
@@ -947,58 +997,10 @@ impl Xhci {
|
||||
// be better. Maybe something simple like bincode could be used, if a custom binary struct
|
||||
// is too much overkill.
|
||||
|
||||
let port_state = self
|
||||
.port_states
|
||||
.get_mut(&port_num)
|
||||
.ok_or(Error::new(EBADF))?;
|
||||
|
||||
let ring = match port_state
|
||||
.endpoint_states
|
||||
.get_mut(&0)
|
||||
.ok_or(Error::new(EIO))?
|
||||
{
|
||||
EndpointState { transfer: super::RingOrStreams::Ring(ref mut ring), .. } => ring,
|
||||
|
||||
// Control endpoints never use streams
|
||||
_ => return Err(Error::new(EIO)),
|
||||
};
|
||||
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.setup(setup, TransferKind::In, cycle);
|
||||
}
|
||||
if transfer_kind != TransferKind::NoData {
|
||||
let (cmd, cycle) = ring.next();
|
||||
|
||||
cmd.data(
|
||||
data_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0),
|
||||
setup.length,
|
||||
transfer_kind == TransferKind::In,
|
||||
cycle,
|
||||
);
|
||||
}
|
||||
{
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.status(transfer_kind == TransferKind::In, cycle);
|
||||
}
|
||||
self.dbs[port_state.slot as usize].write(Self::def_control_endp_doorbell());
|
||||
|
||||
{
|
||||
let event = self.cmd.next_event();
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::Transfer as u8
|
||||
{
|
||||
println!(
|
||||
"Custom device request failed with EVENT {:#0x} {:#0x} {:#0x}",
|
||||
event.data.read(),
|
||||
event.status.read(),
|
||||
event.control.read()
|
||||
);
|
||||
}
|
||||
}
|
||||
self.execute_control_transfer(port_num, setup, transfer_kind, "CUSTOM_DEVICE_REQ", |trb, cycle| {
|
||||
trb.data(data_buffer.as_ref().map(|dma| dma.physical()).unwrap_or(0), setup.length, transfer_kind == TransferKind::In, cycle);
|
||||
ControlFlow::Break
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
fn port_req_init_st(&mut self, port_num: usize, req: &PortReq) -> Result<PortReqState> {
|
||||
@@ -1517,8 +1519,10 @@ impl Xhci {
|
||||
&mut super::RingOrStreams::Ring(ref mut ring) => ring,
|
||||
&mut super::RingOrStreams::Streams(ref mut arr) => arr.rings.get_mut(&1).ok_or(Error::new(EBADFD))?,
|
||||
};
|
||||
|
||||
let (cmd, cycle) = ring.next();
|
||||
cmd.transfer_no_op(0, false, false, false, cycle);
|
||||
|
||||
let deque_ptr_and_cycle = ring.register();
|
||||
let slot = port_state.slot;
|
||||
|
||||
@@ -1538,33 +1542,8 @@ impl Xhci {
|
||||
let slot = self.slot(port_num)?;
|
||||
let endp_num_xhc = Self::endp_num_to_dci(endp_num, self.endp_desc(port_num, endp_num)?);
|
||||
|
||||
// TODO: Merge these command boilerplates into a single function.
|
||||
self.run.ints[0].erdp.write(self.cmd.erdp());
|
||||
self.execute_command("SET_TR_DEQUEUE_POINTER", |trb, cycle| trb.set_tr_deque_ptr(deque_ptr_and_cycle, cycle, StreamContextType::PrimaryRing, 1, endp_num_xhc, slot))?;
|
||||
|
||||
{
|
||||
let (cmd, cycle, event) = self.cmd.next();
|
||||
|
||||
|
||||
// TODO: I guess this very command is the one used to actually multiplex between
|
||||
// streams.
|
||||
cmd.set_tr_deque_ptr(deque_ptr_and_cycle, cycle, StreamContextType::PrimaryRing, 1, endp_num_xhc, slot);
|
||||
|
||||
self.dbs[0].write(0);
|
||||
|
||||
while event.data.read() == 0 {
|
||||
println!(" - Waiting for event");
|
||||
}
|
||||
|
||||
if event.completion_code() != TrbCompletionCode::Success as u8
|
||||
|| event.trb_type() != TrbType::CommandCompletion as u8
|
||||
{
|
||||
println!("SET_TR_DEQUEUE_POINTER failed with event TRB ({:#0x} {:#0x} {:#0x}) and command TRB ({:#0x} {:#0x} {:#0x})", event.data.read(), event.status.read(), event.control.read(), cmd.data.read(), cmd.status.read(), cmd.control.read());
|
||||
return Err(Error::new(EIO));
|
||||
}
|
||||
|
||||
cmd.reserved(false);
|
||||
event.reserved(false);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn on_write_endp_ctl(&mut self, port_num: usize, endp_num: u8, buf: &[u8]) -> Result<usize> {
|
||||
|
||||
@@ -112,6 +112,15 @@ pub struct Trb {
|
||||
pub status: Mmio<u32>,
|
||||
pub control: Mmio<u32>,
|
||||
}
|
||||
impl Clone for Trb {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
data: Mmio::from(self.data.read()),
|
||||
status: Mmio::from(self.status.read()),
|
||||
control: Mmio::from(self.control.read()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const TRB_STATUS_COMPLETION_CODE_SHIFT: u8 = 24;
|
||||
pub const TRB_STATUS_COMPLETION_CODE_MASK: u32 = 0xFF00_0000;
|
||||
@@ -148,6 +157,9 @@ impl Trb {
|
||||
pub fn completion_param(&self) -> u32 {
|
||||
self.status.read() & TRB_STATUS_COMPLETION_PARAM_MASK
|
||||
}
|
||||
pub fn event_slot(&self) -> u8 {
|
||||
(self.control.read() >> 24) as u8
|
||||
}
|
||||
/// Returns the number of bytes that should have been transmitten, but weren't.
|
||||
pub fn transfer_length(&self) -> u32 {
|
||||
self.status.read() & TRB_STATUS_TRANSFER_LENGTH_MASK
|
||||
|
||||
Reference in New Issue
Block a user