Set up a basic SCSI scheme, and get disk size.
Some commands are unused though, and probably won't be used, for example ReportSuppOpcodes. Also, there appears to be lots of different versions of the SCSI specification.
This commit is contained in:
Generated
+1
@@ -1402,6 +1402,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.56 (git+https://gitlab.redox-os.org/redox-os/syscall.git)",
|
||||
"thiserror 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xhcid 0.1.0",
|
||||
]
|
||||
|
||||
@@ -10,5 +10,6 @@ license = "MIT"
|
||||
[dependencies]
|
||||
base64 = "0.11" # Only for debugging
|
||||
plain = "0.2"
|
||||
redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git" }
|
||||
thiserror = "1"
|
||||
xhcid = { path = "../xhcid" }
|
||||
|
||||
+32
-20
@@ -1,11 +1,18 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::os::unix::io::{FromRawFd, RawFd};
|
||||
|
||||
use syscall::{CloneFlags, Packet, SchemeMut};
|
||||
use xhcid_interface::{ConfigureEndpointsReq, DeviceReqData, XhciClientHandle};
|
||||
|
||||
pub mod protocol;
|
||||
pub mod scsi;
|
||||
|
||||
use scsi::cmds::StandardInquiryData;
|
||||
mod scheme;
|
||||
|
||||
use scheme::ScsiScheme;
|
||||
use scsi::Scsi;
|
||||
|
||||
fn main() {
|
||||
let mut args = env::args().skip(1);
|
||||
@@ -18,6 +25,15 @@ fn main() {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let disk_scheme_name = format!(":disk/{}-{}_scsi", scheme, port);
|
||||
|
||||
// TODO: Use eventfds.
|
||||
let handle = XhciClientHandle::new(scheme, port);
|
||||
|
||||
let desc = handle.get_standard_descs().expect("Failed to get standard descriptors");
|
||||
@@ -41,27 +57,23 @@ fn main() {
|
||||
|
||||
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc).expect("Failed to setup protocol");
|
||||
|
||||
assert_eq!(std::mem::size_of::<StandardInquiryData>(), 96);
|
||||
let mut inquiry_buffer = [0u8; 259]; // additional_len = 255
|
||||
let mut command_buffer = [0u8; 6];
|
||||
// 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 mut socket_file = unsafe { File::from_raw_fd(socket_fd as RawFd) };
|
||||
|
||||
let min_inquiry_len = 5u16;
|
||||
//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 max_inquiry_len = {
|
||||
{
|
||||
let inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap();
|
||||
*inquiry = scsi::cmds::Inquiry::new(false, 0, min_inquiry_len, 0);
|
||||
// TODO: Use nonblocking and put all pending calls in a todo VecDeque. Use an eventfd as well.
|
||||
'scheme_loop: loop {
|
||||
let mut packet = Packet::default();
|
||||
match socket_file.read(&mut packet) {
|
||||
Ok(0) => break 'scheme_loop,
|
||||
Ok(_) => (),
|
||||
Err(err) => panic!("scsid: failed to read disk scheme: {}", err),
|
||||
}
|
||||
protocol.send_command(&command_buffer, DeviceReqData::In(&mut inquiry_buffer[..min_inquiry_len as usize])).expect("Failed to send command");
|
||||
let standard_inquiry_data: &StandardInquiryData = dbg!(plain::from_bytes(&inquiry_buffer).unwrap());
|
||||
4 + u16::from(standard_inquiry_data.additional_len)
|
||||
};
|
||||
{
|
||||
{
|
||||
let inquiry = plain::from_mut_bytes(&mut command_buffer).unwrap();
|
||||
*inquiry = scsi::cmds::Inquiry::new(false, 0, max_inquiry_len, 0);
|
||||
}
|
||||
protocol.send_command(&command_buffer, DeviceReqData::In(&mut inquiry_buffer[..max_inquiry_len as usize])).expect("Failed to send command");
|
||||
let standard_inquiry_data: &StandardInquiryData = dbg!(plain::from_bytes(&inquiry_buffer).unwrap());
|
||||
scsi_scheme.handle(&mut packet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use std::convert::TryInto;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::{io, slice};
|
||||
use std::num::NonZeroU32;
|
||||
use std::slice;
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqDirection, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle};
|
||||
use xhcid_interface::{ConfDesc, DeviceReqData, EndpBinaryDirection, EndpDirection, EndpointStatus, IfDesc, Invalid, PortReqTy, PortReqRecipient, PortTransferStatus, XhciClientHandle, XhciClientHandleError, XhciEndpHandle};
|
||||
|
||||
use super::{Protocol, ProtocolError};
|
||||
use super::{Protocol, ProtocolError, SendCommandStatus};
|
||||
|
||||
pub const CBW_SIGNATURE: u32 = 0x43425355;
|
||||
|
||||
@@ -130,7 +127,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<(), ProtocolError> {
|
||||
fn send_command(&mut self, cb: &[u8], data: DeviceReqData) -> Result<SendCommandStatus, ProtocolError> {
|
||||
self.current_tag += 1;
|
||||
let tag = self.current_tag;
|
||||
|
||||
@@ -138,10 +135,10 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
println!();
|
||||
|
||||
let mut cbw_bytes = [0u8; 31];
|
||||
println!("{}", base64::encode(&cbw_bytes));
|
||||
let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes).unwrap();
|
||||
*cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?;
|
||||
let cbw = *cbw;
|
||||
println!("{}", base64::encode(&cbw_bytes));
|
||||
|
||||
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
|
||||
|
||||
@@ -209,7 +206,13 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
dbg!(self.bulk_in.status()?, self.bulk_out.status()?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
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"));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::io;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::{DeviceReqData, DevDesc, ConfDesc, IfDesc, XhciClientHandle, XhciClientHandleError};
|
||||
@@ -16,10 +17,24 @@ pub enum ProtocolError {
|
||||
|
||||
#[error("attempted recovery failed")]
|
||||
RecoveryFailed,
|
||||
|
||||
#[error("protocol error")]
|
||||
ProtocolError(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SendCommandStatus {
|
||||
Success,
|
||||
Failed { residue: Option<NonZeroU32> },
|
||||
}
|
||||
impl Default for SendCommandStatus {
|
||||
fn default() -> Self {
|
||||
Self::Success
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Protocol {
|
||||
fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<(), ProtocolError>;
|
||||
fn send_command(&mut self, command: &[u8], data: DeviceReqData) -> Result<SendCommandStatus, ProtocolError>;
|
||||
}
|
||||
|
||||
/// Bulk-only transport
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::str;
|
||||
|
||||
use crate::scsi::Scsi;
|
||||
|
||||
use syscall::SchemeMut;
|
||||
use syscall::error::{Error, Result};
|
||||
use syscall::error::{EACCES, EBADF, ENOENT, ENOSYS};
|
||||
use syscall::flag::{O_DIRECTORY, O_STAT};
|
||||
use syscall::flag::{MODE_CHR, MODE_DIR};
|
||||
|
||||
// TODO: Only one disk, right?
|
||||
const LIST_CONTENTS: &'static [u8] = b"disk\n";
|
||||
|
||||
enum Handle {
|
||||
List(usize),
|
||||
Disk(usize),
|
||||
//Partition(usize, u32, usize),
|
||||
}
|
||||
|
||||
pub struct ScsiScheme<'a> {
|
||||
scsi: &'a mut Scsi,
|
||||
handles: BTreeMap<usize, Handle>,
|
||||
next_fd: usize,
|
||||
}
|
||||
|
||||
impl<'a> ScsiScheme<'a> {
|
||||
pub fn new(scsi: &'a mut Scsi) -> Self {
|
||||
Self {
|
||||
scsi,
|
||||
handles: BTreeMap::new(),
|
||||
next_fd: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SchemeMut for ScsiScheme<'a> {
|
||||
fn open(&mut self, path: &[u8], flags: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||
if uid != 0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
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)
|
||||
} else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') {
|
||||
// TODO: Partitions.
|
||||
return Err(Error::new(ENOSYS));
|
||||
} else {
|
||||
Handle::Disk(0)
|
||||
};
|
||||
self.next_fd += 1;
|
||||
self.handles.insert(self.next_fd, handle);
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn fstat(&mut self, fd: usize, stat: &mut syscall::Stat) -> Result<usize> {
|
||||
match self.handles.get(&fd).ok_or(Error::new(EBADF))? {
|
||||
Handle::Disk(_) => {
|
||||
stat.st_mode = MODE_CHR;
|
||||
stat.st_size = self.scsi.get_disk_size();
|
||||
// TODO: stat.st_blocks
|
||||
}
|
||||
Handle::List(_) => {
|
||||
stat.st_mode = MODE_DIR;
|
||||
stat.st_size = LIST_CONTENTS.len() as u64;
|
||||
// TODO: stat.st_blocks
|
||||
}
|
||||
}
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn fpath(&mut self, fd: usize, path: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
fn write(&mut self, fd: usize, buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(ENOSYS))
|
||||
}
|
||||
}
|
||||
+332
-7
@@ -1,3 +1,4 @@
|
||||
use std::{fmt, mem, slice};
|
||||
use super::opcodes::{Opcode, ServiceActionA3};
|
||||
|
||||
#[repr(packed)]
|
||||
@@ -16,7 +17,7 @@ pub struct ReportIdentInfo {
|
||||
unsafe impl plain::Plain for ReportIdentInfo {}
|
||||
|
||||
impl ReportIdentInfo {
|
||||
pub fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self {
|
||||
pub const fn new(alloc_len: u32, info_ty: ReportIdInfoInfoTy, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::ServiceActionA3 as u8,
|
||||
serviceaction: ServiceActionA3::ReportIdentInfo as u8,
|
||||
@@ -72,6 +73,12 @@ impl ReportSuppOpcodes {
|
||||
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;
|
||||
@@ -96,6 +103,8 @@ pub enum ReportSuppOpcodesOptions {
|
||||
/// 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,
|
||||
|
||||
}
|
||||
@@ -104,10 +113,23 @@ pub enum ReportSuppOpcodesOptions {
|
||||
pub struct AllCommandsParam {
|
||||
/// Little endian
|
||||
pub data_len: u32,
|
||||
pub descs: [CommandDescriptor],
|
||||
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,
|
||||
@@ -118,14 +140,47 @@ pub struct CommandDescriptor {
|
||||
pub a: u8,
|
||||
/// little endian
|
||||
pub cdb_len: u16,
|
||||
pub cmd_timeouts_desc: [u8; 12],
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
pub struct OneCommandParam {
|
||||
pub _rsvd: u8,
|
||||
/// bits 2:0 for SUPPOR, bits 6:3 reserved, and bit 7 for CTDP
|
||||
pub a: u8,
|
||||
// TODO
|
||||
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,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
@@ -141,7 +196,7 @@ pub struct Inquiry {
|
||||
unsafe impl plain::Plain for Inquiry {}
|
||||
|
||||
impl Inquiry {
|
||||
pub fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self {
|
||||
pub const fn new(evpd: bool, page_code: u8, alloc_len: u16, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::Inquiry as u8,
|
||||
evpd: evpd as u8,
|
||||
@@ -172,8 +227,278 @@ pub struct StandardInquiryData {
|
||||
pub driver_serial_no: [u8; 8],
|
||||
pub vendor_uniq: [u8; 12],
|
||||
_rsvd1: [u8; 2],
|
||||
pub vendor_descs: [u16; 8],
|
||||
pub version_descs: [u16; 8],
|
||||
_rsvd2: [u8; 22],
|
||||
}
|
||||
|
||||
unsafe impl plain::Plain for StandardInquiryData {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct RequestSense {
|
||||
pub opcode: u8,
|
||||
pub desc: u8, // bits 7:1 reserved
|
||||
_rsvd: u16,
|
||||
pub alloc_len: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
unsafe impl plain::Plain for RequestSense {}
|
||||
|
||||
impl RequestSense {
|
||||
pub const MINIMAL_ALLOC_LEN: u8 = 252;
|
||||
|
||||
pub const fn new(desc: bool, alloc_len: u8, control: u8) -> Self {
|
||||
Self {
|
||||
opcode: Opcode::RequestSense as u8,
|
||||
desc: desc as u8,
|
||||
_rsvd: 0,
|
||||
alloc_len,
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct FixedFormatSenseData {
|
||||
pub a: u8,
|
||||
_obsolete: u8,
|
||||
pub b: u8,
|
||||
pub info: u32,
|
||||
pub add_sense_len: u8,
|
||||
pub command_specific_info: u32,
|
||||
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 add_sense_bytes: [u8; 0],
|
||||
}
|
||||
unsafe impl plain::Plain for FixedFormatSenseData {}
|
||||
|
||||
impl FixedFormatSenseData {
|
||||
pub const fn additional_len(&self) -> u16 {
|
||||
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)
|
||||
}
|
||||
pub fn sense_key(&self) -> SenseKey {
|
||||
let sense_key_raw = self.b & 0b1111;
|
||||
// Safe because all possible values (0-15) are used by the enum.
|
||||
unsafe { mem::transmute(sense_key_raw) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum SenseKey {
|
||||
NoSense = 0x00,
|
||||
RecoveredError = 0x01,
|
||||
NotReady = 0x02,
|
||||
MediumError = 0x03,
|
||||
HardwareError = 0x04,
|
||||
IllegalRequest = 0x05,
|
||||
UnitAttention = 0x06,
|
||||
DataProtect = 0x07,
|
||||
BlankCheck = 0x08,
|
||||
VendorSpecific = 0x09,
|
||||
CopyAborted = 0x0A,
|
||||
AbortedCommand = 0x0B,
|
||||
Reserved = 0x0C,
|
||||
VolumeOverflow = 0x0D,
|
||||
Miscompare = 0x0E,
|
||||
Completed = 0x0F,
|
||||
}
|
||||
impl Default for SenseKey {
|
||||
fn default() -> Self {
|
||||
Self::NoSense
|
||||
}
|
||||
}
|
||||
|
||||
pub const ADD_SENSE_CODE05_INVAL_CDB_FIELD: u8 = 0x24;
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Read16 {
|
||||
pub opcode: u8,
|
||||
pub a: u8,
|
||||
pub lba: u64,
|
||||
pub transfer_len: u32,
|
||||
pub b: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
|
||||
impl Read16 {
|
||||
pub const fn new(lba: u64, transfer_len: u32, control: u8) -> Self {
|
||||
// TODO: RDPROTECT, DPO, FUA, RARC
|
||||
// TODO: DLD
|
||||
// TODO: Group number
|
||||
Self {
|
||||
opcode: Opcode::Read16 as u8,
|
||||
a: 0,
|
||||
lba: u64::to_le(lba),
|
||||
transfer_len: u32::to_le(transfer_len),
|
||||
b: 0,
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ModeSense6 {
|
||||
pub opcode: u8,
|
||||
pub a: u8,
|
||||
pub b: u8,
|
||||
pub subpage_code: u8,
|
||||
pub alloc_len: u8,
|
||||
pub control: u8,
|
||||
}
|
||||
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 {
|
||||
Self {
|
||||
opcode: Opcode::ModeSense6 as u8,
|
||||
a: (dbd as u8) << 3,
|
||||
b: page_code | (pc << 6),
|
||||
subpage_code,
|
||||
alloc_len,
|
||||
control,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ModeSense10 {
|
||||
pub opcode: u8,
|
||||
pub a: u8,
|
||||
pub b: u8,
|
||||
pub subpage_code: u8,
|
||||
pub _rsvd: [u8; 3],
|
||||
pub alloc_len: u16,
|
||||
pub control: u8,
|
||||
}
|
||||
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 {
|
||||
Self {
|
||||
opcode: Opcode::ModeSense10 as u8,
|
||||
a: ((dbd as u8) << 3) | ((llbaa as u8) << 4),
|
||||
b: page_code | ((pc as u8) << 6),
|
||||
subpage_code,
|
||||
_rsvd: [0u8; 3],
|
||||
alloc_len: u16::from_le(alloc_len),
|
||||
control,
|
||||
}
|
||||
}
|
||||
pub const fn get_block_desc(alloc_len: u16, control: u8) -> Self {
|
||||
Self::new(false, true, 0x3F, ModePageControl::CurrentValues, 0x00, alloc_len, control)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum ModePageControl {
|
||||
CurrentValues,
|
||||
ChangeableChanges,
|
||||
DefaultValues,
|
||||
SavedValue,
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ShortLbaModeParamBlkDesc {
|
||||
pub block_count: u32,
|
||||
_rsvd: u8,
|
||||
pub logical_block_len: [u8; 3],
|
||||
}
|
||||
unsafe impl plain::Plain for ShortLbaModeParamBlkDesc {}
|
||||
|
||||
impl ShortLbaModeParamBlkDesc {
|
||||
pub const fn block_count(&self) -> u32 {
|
||||
u32::from_le(self.block_count)
|
||||
}
|
||||
pub const fn logical_block_len(&self) -> u32 {
|
||||
u24_le_to_u32(self.logical_block_len)
|
||||
}
|
||||
}
|
||||
|
||||
const fn u24_le_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).
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GeneralModeParamBlkDesc {
|
||||
pub density_code: u8,
|
||||
pub block_count: [u8; 3],
|
||||
_rsvd: u8,
|
||||
pub block_length: [u8; 3],
|
||||
}
|
||||
unsafe impl plain::Plain for GeneralModeParamBlkDesc {}
|
||||
|
||||
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))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct LongLbaModeParamBlkDesc {
|
||||
pub block_count: u64,
|
||||
_rsvd: u32,
|
||||
pub logical_block_len: u32,
|
||||
}
|
||||
unsafe impl plain::Plain for LongLbaModeParamBlkDesc {}
|
||||
|
||||
impl LongLbaModeParamBlkDesc {
|
||||
pub const fn block_count(&self) -> u64 {
|
||||
u64::from_le(self.block_count)
|
||||
}
|
||||
pub const fn logical_block_len(&self) -> u32 {
|
||||
u32::from_le(self.logical_block_len)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ModeParamHeader6 {
|
||||
pub mode_data_len: u8,
|
||||
pub medium_ty: u8,
|
||||
pub a: u8,
|
||||
pub block_desc_len: u8,
|
||||
}
|
||||
unsafe impl plain::Plain for ModeParamHeader6 {}
|
||||
|
||||
#[repr(packed)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct ModeParamHeader10 {
|
||||
pub mode_data_len: u16,
|
||||
pub medium_ty: u8,
|
||||
pub a: u8,
|
||||
pub b: u8,
|
||||
_rsvd: u8,
|
||||
pub block_desc_len: u16,
|
||||
}
|
||||
unsafe impl plain::Plain for ModeParamHeader10 {}
|
||||
impl ModeParamHeader10 {
|
||||
pub const fn mode_data_len(&self) -> u16 {
|
||||
u16::from_le(self.mode_data_len)
|
||||
}
|
||||
pub const fn block_desc_len(&self) -> u16 {
|
||||
u16::from_le(self.block_desc_len)
|
||||
}
|
||||
pub const fn longlba(&self) -> bool {
|
||||
(self.b & 0x01) != 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,178 @@
|
||||
use std::mem;
|
||||
|
||||
pub mod cmds;
|
||||
pub mod opcodes;
|
||||
|
||||
use thiserror::Error;
|
||||
use xhcid_interface::DeviceReqData;
|
||||
|
||||
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus};
|
||||
use cmds::{SenseKey, StandardInquiryData};
|
||||
use opcodes::Opcode;
|
||||
|
||||
pub struct Scsi {
|
||||
command_buffer: [u8; 16],
|
||||
inquiry_buffer: [u8; 259],
|
||||
data_buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
const INQUIRY_CMD_LEN: u8 = 6;
|
||||
const REPORT_SUPP_OPCODES_CMD_LEN: u8 = 12;
|
||||
const REQUEST_SENSE_CMD_LEN: u8 = 6;
|
||||
const MIN_INQUIRY_ALLOC_LEN: u16 = 5;
|
||||
const MIN_REPORT_SUPP_OPCODES_ALLOC_LEN: u32 = 4;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScsiError {
|
||||
#[error("protocol error when sending command: {0}")]
|
||||
ProtocolError(#[from] ProtocolError),
|
||||
}
|
||||
|
||||
impl Scsi {
|
||||
pub fn new(protocol: &mut dyn Protocol) -> Self {
|
||||
assert_eq!(std::mem::size_of::<StandardInquiryData>(), 96);
|
||||
let mut this = Self {
|
||||
command_buffer: [0u8; 16],
|
||||
inquiry_buffer: [0u8; 259], // additional_len = 255 max
|
||||
data_buffer: Vec::new(),
|
||||
};
|
||||
|
||||
// Get the max length that the device supports, of the Standard Inquiry Data.
|
||||
let max_inquiry_len = this.get_inquiry_alloc_len(protocol);
|
||||
// Get the Standard Inquiry Data.
|
||||
this.get_standard_inquiry_data(protocol, max_inquiry_len);
|
||||
this.res_standard_inquiry_data();
|
||||
|
||||
dbg!(this.get_mode_sense10(protocol).unwrap());
|
||||
|
||||
this
|
||||
}
|
||||
pub fn get_inquiry_alloc_len(&mut self, protocol: &mut dyn Protocol) -> u16 {
|
||||
self.get_standard_inquiry_data(protocol, MIN_INQUIRY_ALLOC_LEN);
|
||||
let standard_inquiry_data = self.res_standard_inquiry_data();
|
||||
4 + u16::from(standard_inquiry_data.additional_len)
|
||||
}
|
||||
pub fn get_standard_inquiry_data(&mut self, protocol: &mut dyn Protocol, max_inquiry_len: u16) {
|
||||
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");
|
||||
}
|
||||
/*/// 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> {
|
||||
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]))? {
|
||||
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 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()))
|
||||
}
|
||||
|
||||
pub fn cmd_inquiry(&mut self) -> &mut cmds::Inquiry {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
pub fn cmd_mode_sense6(&mut self) -> &mut cmds::ModeSense6 {
|
||||
plain::from_mut_bytes(&mut self.command_buffer).unwrap()
|
||||
}
|
||||
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 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()
|
||||
}
|
||||
pub fn res_mode_param_header6(&self) -> &cmds::ModeParamHeader6 {
|
||||
plain::from_bytes(&self.data_buffer).unwrap()
|
||||
}
|
||||
pub fn res_mode_param_header10(&self) -> &cmds::ModeParamHeader10 {
|
||||
plain::from_bytes(&self.data_buffer).unwrap()
|
||||
}
|
||||
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()
|
||||
}
|
||||
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())
|
||||
} 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())
|
||||
}
|
||||
}
|
||||
pub fn get_disk_size(&mut self) -> u64 {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum BlkDescSlice<'a> {
|
||||
//Short(&'a [cmds::ShortLbaModeParamBlkDesc]),
|
||||
General(&'a [cmds::GeneralModeParamBlkDesc]),
|
||||
Long(&'a [cmds::LongLbaModeParamBlkDesc]),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user