USB: P4-B multi-LUN support — Protocol trait + BOT/UAS LUN propagation + REPORT_LUNS
Cross-referenced with Linux 7.1 drivers/usb/storage/usb.c and SPC-3 §6.27. Protocol trait: - Added max_lun() and set_lun(lun) to Protocol trait - BOT: current_lun field, used in CommandBlockWrapper constructor (was hardcoded lun=0 at bot.rs:212) - UAS: current_lun field, used in CommandIU.lun field (was hardcoded lun=0 with TODO comment) - get_max_lun() already existed (BOT class-specific request 0xFE) SCSI: - Added report_luns() method — SCSI REPORT_LUNS command (opcode 0xA0). Returns Vec<u64> of 8-byte LUN addresses per SPC-3 format. Handles big-endian LUN list length and per-entry parsing. - Import opcodes::Opcode main.rs: - Prints max_lun detection (GET_MAX_LUN result) - Multi-LUN device detection with per-LUN init TODO marker - Per-LUN inquiry/capacity init deferred to next round (P4-B slice 2) Per-LUN SCSI init and separate scheme registration per LUN deferred to P4-B slice 3 — this round provides the protocol infrastructure and LUN propagation through the full stack.
This commit is contained in:
@@ -82,7 +82,26 @@ fn daemon(daemon: daemon::Daemon) -> ! {
|
||||
// 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");
|
||||
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];
|
||||
scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
|
||||
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
|
||||
|
||||
@@ -89,6 +89,7 @@ pub struct BulkOnlyTransport<'a> {
|
||||
bulk_in_num: u8,
|
||||
bulk_out_num: u8,
|
||||
max_lun: u8,
|
||||
current_lun: u8,
|
||||
current_tag: u32,
|
||||
interface_num: u8,
|
||||
}
|
||||
@@ -124,6 +125,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
||||
bulk_out_num,
|
||||
handle,
|
||||
max_lun,
|
||||
current_lun: 0,
|
||||
current_tag: 0,
|
||||
interface_num: if_desc.number,
|
||||
})
|
||||
@@ -209,7 +211,7 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
|
||||
let mut cbw_bytes = [0u8; 31];
|
||||
let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes).unwrap();
|
||||
*cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), 0, cb)?;
|
||||
*cbw = CommandBlockWrapper::new(tag, data.len() as u32, data.direction().into(), self.current_lun, cb)?;
|
||||
let cbw = *cbw;
|
||||
|
||||
match self.bulk_out.transfer_write(&cbw_bytes)? {
|
||||
@@ -359,6 +361,12 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
||||
residue,
|
||||
})
|
||||
}
|
||||
fn max_lun(&self) -> u8 {
|
||||
self.max_lun
|
||||
}
|
||||
fn set_lun(&mut self, lun: u8) {
|
||||
self.current_lun = lun;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bulk_only_mass_storage_reset(
|
||||
|
||||
@@ -60,6 +60,9 @@ pub trait Protocol {
|
||||
command: &[u8],
|
||||
data: DeviceReqData,
|
||||
) -> Result<SendCommandStatus, ProtocolError>;
|
||||
|
||||
fn max_lun(&self) -> u8;
|
||||
fn set_lun(&mut self, lun: u8);
|
||||
}
|
||||
|
||||
/// Bulk-only transport (BOT)
|
||||
|
||||
@@ -91,6 +91,7 @@ pub struct UasTransport<'a> {
|
||||
current_tag: u16,
|
||||
qdepth: u16,
|
||||
use_streams: bool,
|
||||
current_lun: u8,
|
||||
}
|
||||
|
||||
/// Find pipe IDs from endpoint extra descriptors.
|
||||
@@ -177,6 +178,7 @@ impl<'a> UasTransport<'a> {
|
||||
current_tag: 0,
|
||||
qdepth,
|
||||
use_streams,
|
||||
current_lun: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -202,7 +204,7 @@ impl<'a> Protocol for UasTransport<'a> {
|
||||
let cmd_iu = CommandIU {
|
||||
iu_id: IU_ID_COMMAND,
|
||||
tag: tag as u16,
|
||||
lun: 0, // TODO: LUN support (P4 slice 3)
|
||||
lun: self.current_lun,
|
||||
command_block: cdb,
|
||||
..CommandIU::default()
|
||||
};
|
||||
@@ -286,4 +288,10 @@ impl<'a> Protocol for UasTransport<'a> {
|
||||
_ => Err(ProtocolError::ProtocolError("unknown UAS response IU")),
|
||||
}
|
||||
}
|
||||
fn max_lun(&self) -> u8 {
|
||||
0 // UAS max_lun is obtained via REPORT_LUNS, hardcoded for now
|
||||
}
|
||||
fn set_lun(&mut self, lun: u8) {
|
||||
self.current_lun = lun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use thiserror::Error;
|
||||
use xhcid_interface::DeviceReqData;
|
||||
|
||||
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
||||
use opcodes::Opcode;
|
||||
use cmds::StandardInquiryData;
|
||||
|
||||
pub struct Scsi {
|
||||
@@ -305,6 +306,41 @@ impl Scsi {
|
||||
)?;
|
||||
Ok(status.bytes_transferred(bytes_to_write as u32))
|
||||
}
|
||||
pub fn report_luns(&mut self, protocol: &mut dyn Protocol) -> Result<Vec<u64>> {
|
||||
// REPORT_LUNS (0xA0) — SPC-3 §6.27
|
||||
// Returns a list of 8-byte LUN entries (4 bytes LUN, 4 bytes reserved).
|
||||
// Cross-referenced with Linux 7.1 drivers/usb/storage/usb.c
|
||||
let mut buf = vec![0u8; 256]; // up to 32 LUNs
|
||||
self.command_buffer.fill(0);
|
||||
self.command_buffer[0] = Opcode::ReportLuns as u8;
|
||||
self.command_buffer[1] = 0x00; // select_report = 0 (all LUNs)
|
||||
self.command_buffer[6] = ((buf.len() >> 24) & 0xFF) as u8;
|
||||
self.command_buffer[7] = ((buf.len() >> 16) & 0xFF) as u8;
|
||||
self.command_buffer[8] = ((buf.len() >> 8) & 0xFF) as u8;
|
||||
self.command_buffer[9] = (buf.len() & 0xFF) as u8;
|
||||
let status = protocol.send_command(
|
||||
&self.command_buffer[..12],
|
||||
DeviceReqData::In(&mut buf),
|
||||
)?;
|
||||
let transferred = status.bytes_transferred(buf.len() as u32) as usize;
|
||||
if transferred < 8 {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
// First 4 bytes: LUN list length (big-endian)
|
||||
let list_len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]) as usize;
|
||||
let lun_count = (list_len.min(transferred - 8)) / 8;
|
||||
let mut luns = Vec::with_capacity(lun_count);
|
||||
for i in 0..lun_count {
|
||||
let offset = 8 + i * 8;
|
||||
let lun_addr = u64::from_be_bytes([
|
||||
buf[offset], buf[offset+1], buf[offset+2], buf[offset+3],
|
||||
buf[offset+4], buf[offset+5], buf[offset+6], buf[offset+7],
|
||||
]);
|
||||
// Single-level LUN structure: LUN 0 = 0x0000_0000_0000_0000
|
||||
luns.push(lun_addr);
|
||||
}
|
||||
Ok(luns)
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum BlkDescSlice<'a> {
|
||||
|
||||
Reference in New Issue
Block a user