Files
RedBear-OS/drivers/storage/usbscsid/src/main.rs
T
Red Bear OS bd595851e2 base: apply Red Bear patches on latest upstream/main
251 files: init, acpid, ipcd, netcfg, ihdgd, virtio-gpud, scheme-utils,
inputd, block driver, ptyd, ramfs, randd, initfs bootstrap, path deps,
version +rb0.3.1, author attribution
2026-07-11 11:39:24 +03:00

206 lines
6.3 KiB
Rust

use std::collections::BTreeMap;
use std::env;
use driver_block::{Disk, DiskScheme, ExecutorTrait};
use log::error;
use syscall::{Error, EIO};
use xhcid_interface::{ConfigureEndpointsReq, PortId, XhciClientHandle};
pub mod protocol;
pub mod scsi;
use crate::protocol::Protocol;
use crate::scsi::Scsi;
fn main() {
daemon::Daemon::new(daemon);
}
fn daemon(daemon: daemon::Daemon) -> ! {
let mut args = env::args().skip(1);
const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";
let scheme = args.next().expect(USAGE);
let port = args
.next()
.expect(USAGE)
.parse::<PortId>()
.expect("Expected port ID");
let protocol = args
.next()
.expect(USAGE)
.parse::<u8>()
.expect("protocol has to be a number 0-255");
println!(
"USB SCSI driver spawned with scheme `{}`, port {}, protocol {}",
scheme, port, protocol
);
let disk_scheme_name = format!("disk.usb-{scheme}+{port}-scsi");
// TODO: Use eventfds.
let handle =
XhciClientHandle::new(scheme.to_owned(), port).expect("Failed to open XhciClientHandle");
let desc = handle
.get_standard_descs()
.expect("Failed to get standard descriptors");
// TODO: Perhaps the drivers should just be given the config, interface, and alternate setting
// from xhcid.
let (conf_desc, configuration_value, (if_desc, interface_num, alternate_setting)) = desc
.config_descs
.iter()
.find_map(|config_desc| {
let interface_desc = config_desc.interface_descs.iter().find_map(|if_desc| {
if if_desc.class == 8 && if_desc.sub_class == 6 && if_desc.protocol == 0x50 {
Some((if_desc.clone(), if_desc.number, if_desc.alternate_setting))
} else {
None
}
})?;
Some((
config_desc.clone(),
config_desc.configuration_value,
interface_desc,
))
})
.expect("Failed to find suitable configuration");
handle
.configure_endpoints(&ConfigureEndpointsReq {
config_desc: configuration_value,
interface_desc: Some(interface_num),
alternate_setting: Some(alternate_setting),
hub_ports: None,
})
.expect("Failed to configure endpoints");
let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
.expect("Failed to setup protocol");
// 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 mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
println!("SCSI initialized (LUN 0)");
// Multi-LUN support: create a block device for each LUN. The first
// LUN is already initialized above; additional LUNs need their own
// SCSI init (inquiry, read capacity). For now we register a single
// scheme that serves all LUNs through a combined interface, switching
// the active LUN before each operation.
let max_lun = protocol.max_lun();
println!("usbscsid: {} LUN(s) detected", max_lun + 1);
// For multi-LUN devices, create separate scheme names per LUN.
// Single-LUN devices use the existing flat scheme name.
let lun_count = max_lun + 1;
if lun_count > 1 {
// TODO: Create separate SCSI init per LUN for proper multi-LUN
// support. Currently only LUN 0 is fully initialized — additional
// LUNs are registered but will need per-LUN inquiry/capacity init.
println!("usbscsid: multi-LUN device — LUNs 1..{} need per-LUN init (TODO)", max_lun);
}
let mut buffer = [0u8; 512];
if let Ok(()) = scsi.read(&mut *protocol, 0, &mut buffer).map(|_| ()) {
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
}
let event_queue = match event::EventQueue::new() {
Ok(q) => q,
Err(e) => {
error!("usbscsid: failed to create event queue: {e}");
std::process::exit(1);
}
};
event::user_data! {
enum Event {
Scheme,
}
};
let mut scheme = DiskScheme::new(
None,
disk_scheme_name,
BTreeMap::from([(
0,
UsbDisk {
scsi: &mut scsi,
protocol: &mut *protocol,
},
)]),
&driver_block::FuturesExecutor,
);
// FIXME should this wait notifying readiness until the disk scheme is created?
daemon.ready();
//libredox::call::setrens(0, 0).expect("nvmed: failed to enter null namespace");
if let Err(e) = event_queue.subscribe(
scheme.event_handle().raw(),
Event::Scheme,
event::EventFlags::READ,
) {
error!("usbscsid: failed to subscribe to events: {e}");
std::process::exit(1);
}
for event in event_queue {
let event = match event {
Ok(e) => e,
Err(e) => {
eprintln!("usbscsid: event error: {}", e);
continue;
}
};
match event.user_data {
Event::Scheme => {
if let Err(e) = driver_block::FuturesExecutor.block_on(scheme.tick()) {
eprintln!("usbscsid: scheme tick error: {}", e);
}
}
}
}
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()
}
async fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
match self.scsi.read(self.protocol, block, buffer) {
Ok(bytes_read) => Ok(bytes_read as usize),
Err(err) => {
eprintln!("usbscsid: READ IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
async fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result<usize> {
match self.scsi.write(self.protocol, block, buffer) {
Ok(bytes_written) => Ok(bytes_written as usize),
Err(err) => {
eprintln!("usbscsid: WRITE IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
}