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
|
// TODO: Let all of the USB drivers fork or be managed externally, and xhcid won't have to keep
|
||||||
// track of all the drivers.
|
// track of all the drivers.
|
||||||
let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
|
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];
|
let mut buffer = [0u8; 512];
|
||||||
scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
|
scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
|
||||||
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
|
println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ pub struct BulkOnlyTransport<'a> {
|
|||||||
bulk_in_num: u8,
|
bulk_in_num: u8,
|
||||||
bulk_out_num: u8,
|
bulk_out_num: u8,
|
||||||
max_lun: u8,
|
max_lun: u8,
|
||||||
|
current_lun: u8,
|
||||||
current_tag: u32,
|
current_tag: u32,
|
||||||
interface_num: u8,
|
interface_num: u8,
|
||||||
}
|
}
|
||||||
@@ -124,6 +125,7 @@ impl<'a> BulkOnlyTransport<'a> {
|
|||||||
bulk_out_num,
|
bulk_out_num,
|
||||||
handle,
|
handle,
|
||||||
max_lun,
|
max_lun,
|
||||||
|
current_lun: 0,
|
||||||
current_tag: 0,
|
current_tag: 0,
|
||||||
interface_num: if_desc.number,
|
interface_num: if_desc.number,
|
||||||
})
|
})
|
||||||
@@ -209,7 +211,7 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|||||||
|
|
||||||
let mut cbw_bytes = [0u8; 31];
|
let mut cbw_bytes = [0u8; 31];
|
||||||
let cbw = plain::from_mut_bytes::<CommandBlockWrapper>(&mut cbw_bytes).unwrap();
|
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;
|
let cbw = *cbw;
|
||||||
|
|
||||||
match self.bulk_out.transfer_write(&cbw_bytes)? {
|
match self.bulk_out.transfer_write(&cbw_bytes)? {
|
||||||
@@ -359,6 +361,12 @@ impl<'a> Protocol for BulkOnlyTransport<'a> {
|
|||||||
residue,
|
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(
|
pub fn bulk_only_mass_storage_reset(
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ pub trait Protocol {
|
|||||||
command: &[u8],
|
command: &[u8],
|
||||||
data: DeviceReqData,
|
data: DeviceReqData,
|
||||||
) -> Result<SendCommandStatus, ProtocolError>;
|
) -> Result<SendCommandStatus, ProtocolError>;
|
||||||
|
|
||||||
|
fn max_lun(&self) -> u8;
|
||||||
|
fn set_lun(&mut self, lun: u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bulk-only transport (BOT)
|
/// Bulk-only transport (BOT)
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ pub struct UasTransport<'a> {
|
|||||||
current_tag: u16,
|
current_tag: u16,
|
||||||
qdepth: u16,
|
qdepth: u16,
|
||||||
use_streams: bool,
|
use_streams: bool,
|
||||||
|
current_lun: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find pipe IDs from endpoint extra descriptors.
|
/// Find pipe IDs from endpoint extra descriptors.
|
||||||
@@ -177,6 +178,7 @@ impl<'a> UasTransport<'a> {
|
|||||||
current_tag: 0,
|
current_tag: 0,
|
||||||
qdepth,
|
qdepth,
|
||||||
use_streams,
|
use_streams,
|
||||||
|
current_lun: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,7 +204,7 @@ impl<'a> Protocol for UasTransport<'a> {
|
|||||||
let cmd_iu = CommandIU {
|
let cmd_iu = CommandIU {
|
||||||
iu_id: IU_ID_COMMAND,
|
iu_id: IU_ID_COMMAND,
|
||||||
tag: tag as u16,
|
tag: tag as u16,
|
||||||
lun: 0, // TODO: LUN support (P4 slice 3)
|
lun: self.current_lun,
|
||||||
command_block: cdb,
|
command_block: cdb,
|
||||||
..CommandIU::default()
|
..CommandIU::default()
|
||||||
};
|
};
|
||||||
@@ -286,4 +288,10 @@ impl<'a> Protocol for UasTransport<'a> {
|
|||||||
_ => Err(ProtocolError::ProtocolError("unknown UAS response IU")),
|
_ => 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 xhcid_interface::DeviceReqData;
|
||||||
|
|
||||||
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
use crate::protocol::{Protocol, ProtocolError, SendCommandStatus, SendCommandStatusKind};
|
||||||
|
use opcodes::Opcode;
|
||||||
use cmds::StandardInquiryData;
|
use cmds::StandardInquiryData;
|
||||||
|
|
||||||
pub struct Scsi {
|
pub struct Scsi {
|
||||||
@@ -305,6 +306,41 @@ impl Scsi {
|
|||||||
)?;
|
)?;
|
||||||
Ok(status.bytes_transferred(bytes_to_write as u32))
|
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)]
|
#[derive(Debug)]
|
||||||
pub enum BlkDescSlice<'a> {
|
pub enum BlkDescSlice<'a> {
|
||||||
|
|||||||
Reference in New Issue
Block a user