Merge branch 'fix_usb_harddrive' into 'master'

Bunch of improvements to usb storage

See merge request redox-os/drivers!250
This commit is contained in:
Jeremy Soller
2025-03-09 18:00:42 +00:00
10 changed files with 113 additions and 204 deletions
Generated
+2 -1
View File
@@ -1594,10 +1594,11 @@ name = "usbscsid"
version = "0.1.0"
dependencies = [
"base64 0.11.0",
"driver-block",
"libredox",
"plain",
"redox-daemon",
"redox-scheme 0.3.0",
"redox_event",
"redox_syscall",
"thiserror",
"xhcid",
+12 -6
View File
@@ -13,8 +13,8 @@ use redox_scheme::{
};
use syscall::schemev2::NewFdFlags;
use syscall::{
Error, Result, Stat, EACCES, EAGAIN, EBADF, EISDIR, ENOENT, ENOLCK, EOPNOTSUPP, EOVERFLOW,
MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT,
Error, Result, Stat, EACCES, EAGAIN, EBADF, EINVAL, EISDIR, ENOENT, ENOLCK, EOPNOTSUPP,
EOVERFLOW, MODE_DIR, MODE_FILE, O_DIRECTORY, O_STAT,
};
/// Split the read operation into a series of block reads.
@@ -195,6 +195,10 @@ impl<T: Disk> DiskWrapper<T> {
block: u64,
buf: &mut [u8],
) -> syscall::Result<Option<usize>> {
if buf.len() as u64 % u64::from(self.disk.block_size()) != 0 {
return Err(Error::new(EINVAL));
}
if let Some(part_num) = part_num {
let part = self
.pt
@@ -204,8 +208,7 @@ impl<T: Disk> DiskWrapper<T> {
.get(part_num)
.ok_or(syscall::Error::new(EBADF))?;
let block_size = u64::from(self.block_size());
if block >= part.size / block_size {
if block >= part.size {
return Err(syscall::Error::new(EOVERFLOW));
}
@@ -223,6 +226,10 @@ impl<T: Disk> DiskWrapper<T> {
block: u64,
buf: &[u8],
) -> syscall::Result<Option<usize>> {
if buf.len() as u64 % u64::from(self.disk.block_size()) != 0 {
return Err(Error::new(EINVAL));
}
if let Some(part_num) = part_num {
let part = self
.pt
@@ -232,8 +239,7 @@ impl<T: Disk> DiskWrapper<T> {
.get(part_num)
.ok_or(syscall::Error::new(EBADF))?;
let block_size = u64::from(self.block_size());
if block >= part.size / block_size {
if block >= part.size {
return Err(syscall::Error::new(EOVERFLOW));
}
+2 -1
View File
@@ -11,8 +11,9 @@ license = "MIT"
base64 = "0.11" # Only for debugging
libredox = "0.1.3"
plain = "0.2"
driver-block = { path = "../driver-block" }
redox-daemon = "0.1"
redox_event = "0.4"
redox_syscall = { version = "0.5", features = ["std"] }
redox-scheme = { git = "https://gitlab.redox-os.org/redox-os/redox-scheme.git" }
thiserror = "1"
xhcid = { path = "../../xhcid" }
+73 -30
View File
@@ -1,15 +1,15 @@
use std::collections::BTreeMap;
use std::env;
use redox_scheme::{RequestKind, SignalBehavior, Socket};
use driver_block::{Disk, DiskScheme};
use syscall::{Error, EIO};
use xhcid_interface::{ConfigureEndpointsReq, XhciClientHandle};
pub mod protocol;
pub mod scsi;
mod scheme;
use scheme::ScsiScheme;
use scsi::Scsi;
use crate::protocol::Protocol;
use crate::scsi::Scsi;
fn main() {
let mut args = env::args().skip(1);
@@ -37,7 +37,7 @@ fn main() {
.expect("usbscsid: failed to daemonize");
}
fn daemon(daemon: redox_daemon::Daemon, scheme: String, port: usize, protocol: u8) -> ! {
let disk_scheme_name = format!(":disk.usb-{scheme}+{port}-scsi");
let disk_scheme_name = format!("disk.usb-{scheme}+{port}-scsi");
// TODO: Use eventfds.
let handle = XhciClientHandle::new(scheme.to_owned(), port);
@@ -82,38 +82,81 @@ fn daemon(daemon: redox_daemon::Daemon, scheme: String, port: usize, protocol: u
// TODO: Let all of the USB drivers fork or be managed externally, and xhcid won't have to keep
// track of all the drivers.
let socket_fd =
Socket::create(&disk_scheme_name).expect("usbscsid: failed to create disk scheme");
//libredox::call::setrens(0, 0).expect("scsid: failed to enter null namespace");
let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
println!("SCSI initialized");
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);
let event_queue = event::EventQueue::new().unwrap();
// TODO: Use nonblocking and put all pending calls in a todo VecDeque. Use an eventfd as well.
loop {
let req = match socket_fd
.next_request(SignalBehavior::Restart)
.expect("scsid: failed to read disk scheme")
{
Some(r) => {
if let RequestKind::Call(c) = r.kind() {
c
} else {
continue;
}
}
None => break,
};
let resp = req.handle_scheme(&mut scsi_scheme);
socket_fd
.write_response(resp, SignalBehavior::Restart)
.expect("scsid: failed to write cqe");
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
disk_scheme_name,
BTreeMap::from([(
0,
UsbDisk {
scsi: &mut scsi,
protocol: &mut *protocol,
},
)]),
);
//libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
event_queue
.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
)
.unwrap();
for event in event_queue {
match event.unwrap().user_data {
Event::Scheme => scheme.tick().unwrap(),
}
}
std::process::exit(0);
}
struct UsbDisk<'a> {
scsi: &'a mut Scsi,
protocol: &'a mut dyn Protocol,
}
impl Disk for UsbDisk<'_> {
fn block_size(&self) -> u32 {
self.scsi.block_size
}
fn size(&self) -> u64 {
self.scsi.get_disk_size()
}
fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<Option<usize>> {
match self.scsi.read(self.protocol, block, buffer) {
Ok(bytes_read) => Ok(Some(bytes_read as usize)),
Err(err) => {
eprintln!("usbscsid: READ IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<Option<usize>> {
match self.scsi.write(self.protocol, block, buffer) {
Ok(bytes_written) => Ok(Some(bytes_written as usize)),
Err(err) => {
eprintln!("usbscsid: WRITE IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
}
-146
View File
@@ -1,146 +0,0 @@
use std::collections::BTreeMap;
use std::str;
use crate::protocol::Protocol;
use crate::scsi::Scsi;
use redox_scheme::{CallerCtx, OpenResult, Scheme};
use syscall::error::{Error, Result};
use syscall::error::{EACCES, EBADF, EINVAL, EIO, ENOSYS};
use syscall::flag::{MODE_CHR, MODE_DIR};
use syscall::flag::{O_DIRECTORY, O_STAT};
use syscall::schemev2::NewFdFlags;
// TODO: Only one disk, right?
const LIST_CONTENTS: &'static [u8] = b"0\n";
enum Handle {
List,
Disk,
//Partition(usize, u32),
}
pub struct ScsiScheme<'a> {
scsi: &'a mut Scsi,
protocol: &'a mut dyn Protocol,
handles: BTreeMap<usize, Handle>,
next_fd: usize,
}
impl<'a> ScsiScheme<'a> {
pub fn new(scsi: &'a mut Scsi, protocol: &'a mut dyn Protocol) -> Self {
Self {
scsi,
protocol,
handles: BTreeMap::new(),
next_fd: 0,
}
}
}
impl Scheme for ScsiScheme<'_> {
fn xopen(&mut self, path_str: &str, flags: usize, ctx: &CallerCtx) -> Result<OpenResult> {
if ctx.uid != 0 {
return Err(Error::new(EACCES));
}
if flags & O_DIRECTORY != 0 && flags & O_STAT == 0 {
return Err(Error::new(EACCES));
}
let path_str = path_str.trim_start_matches('/');
let handle = if path_str.is_empty() {
// List
Handle::List
} else if let Some(_p_pos) = path_str.chars().position(|c| c == 'p') {
// TODO: Partitions.
return Err(Error::new(ENOSYS));
} else {
Handle::Disk
};
self.next_fd += 1;
self.handles.insert(self.next_fd, handle);
Ok(OpenResult::ThisScheme {
number: self.next_fd,
flags: NewFdFlags::POSITIONED,
})
}
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();
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;
}
}
Ok(0)
}
fn fpath(&mut self, fd: usize, buf: &mut [u8]) -> Result<usize> {
let path = match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
Handle::Disk => "0",
Handle::List => "",
}
.as_bytes();
let min = std::cmp::min(path.len(), buf.len());
buf[..min].copy_from_slice(&path[..min]);
Ok(min)
}
fn fsize(&mut self, fd: usize) -> Result<u64> {
Ok(match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
Handle::Disk => self.scsi.get_disk_size(),
Handle::List => LIST_CONTENTS.len() as u64,
})
}
fn read(&mut self, fd: usize, buf: &mut [u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
Handle::Disk => {
if offset % 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 / u64::from(self.scsi.block_size);
match self.scsi.read(self.protocol, lba, buf) {
Ok(bytes_read) => Ok(bytes_read as usize),
Err(err) => {
eprintln!("usbscsid: READ IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
Handle::List => {
let src = usize::try_from(offset)
.ok()
.and_then(|o| LIST_CONTENTS.get(o..))
.unwrap_or(&[]);
let min = core::cmp::min(src.len(), buf.len());
buf[..min].copy_from_slice(&src[..min]);
Ok(min)
}
}
}
fn write(&mut self, fd: usize, buf: &[u8], offset: u64, _fcntl_flags: u32) -> Result<usize> {
match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? {
Handle::Disk => {
if offset % 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 / u64::from(self.scsi.block_size);
match self.scsi.write(self.protocol, lba, buf) {
Ok(bytes_written) => Ok(bytes_written as usize),
Err(err) => {
eprintln!("usbscsid: WRITE IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
Handle::List => Err(Error::new(EBADF)),
}
}
}
+1 -1
View File
@@ -249,7 +249,7 @@ impl Scsi {
pub fn res_read_capacity10(&self) -> &cmds::ReadCapacity10ParamData {
plain::from_bytes(&self.data_buffer).unwrap()
}
pub fn get_disk_size(&mut self) -> u64 {
pub fn get_disk_size(&self) -> u64 {
self.block_count * u64::from(self.block_size)
}
pub fn read(
-1
View File
@@ -22,7 +22,6 @@
//! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification)
//! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022)
//!
#![warn(missing_docs)]
pub extern crate plain;
mod driver_interface;
-1
View File
@@ -22,7 +22,6 @@
//! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification)
//! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022)
//!
#![warn(missing_docs)]
#[macro_use]
extern crate bitflags;
+6 -1
View File
@@ -300,7 +300,12 @@ impl PortState {
//TODO: fetch using endpoint number instead
fn get_endp_desc(&self, endp_idx: u8) -> Option<&EndpDesc> {
let cfg_idx = self.cfg_idx?;
let config_desc = self.dev_desc.as_ref()?.config_descs.get(cfg_idx as usize)?;
let config_desc = self
.dev_desc
.as_ref()?
.config_descs
.iter()
.find(|desc| desc.configuration_value == cfg_idx)?;
let mut endp_count = 0;
for if_desc in config_desc.interface_descs.iter() {
for endp_desc in if_desc.endpoints.iter() {
+17 -16
View File
@@ -918,8 +918,10 @@ impl Xhci {
.as_ref()
.unwrap()
.config_descs
.get(usize::from(req.config_desc))
.ok_or(Error::new(EBADFD))?;
.iter()
.find(|desc| desc.configuration_value == req.config_desc)
.ok_or(Error::new(EBADFD))
.unwrap();
//TODO: USE ENDPOINTS FROM ALL INTERFACES
let mut endp_desc_count = 0;
@@ -1459,7 +1461,7 @@ impl Xhci {
}
let mut interface_descs = SmallVec::new();
let mut iter = descriptors.into_iter();
let mut iter = descriptors.into_iter().peekable();
while let Some(item) = iter.next() {
if let AnyDescriptor::Interface(idesc) = item {
@@ -1481,21 +1483,20 @@ impl Xhci {
};
let mut endp = EndpDesc::from(next);
if supports_superspeed {
let next = match iter.next() {
Some(AnyDescriptor::SuperSpeedCompanion(n)) => n,
loop {
match iter.peek() {
Some(AnyDescriptor::SuperSpeedCompanion(n)) => {
endp.ssc = Some(SuperSpeedCmp::from(n.clone()));
iter.next().unwrap();
}
Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => {
endp.sspc = Some(SuperSpeedPlusIsochCmp::from(n.clone()));
iter.next().unwrap();
}
_ => break,
};
endp.ssc = Some(SuperSpeedCmp::from(next));
if endp.has_ssp_companion() && supports_superspeedplus {
let next = match iter.next() {
Some(AnyDescriptor::SuperSpeedPlusCompanion(n)) => n,
_ => break,
};
endp.sspc = Some(SuperSpeedPlusIsochCmp::from(next));
}
}
endpoints.push(endp);
}
@@ -1506,7 +1507,7 @@ impl Xhci {
} else {
log::warn!("expected interface, got {:?}", item);
// TODO
break;
//break;
}
}