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 "; let scheme = args.next().expect(USAGE); let port = args .next() .expect(USAGE) .parse::() .expect("Expected port ID"); let protocol = args .next() .expect(USAGE) .parse::() .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 { 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 { 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)) } } } }