diff --git a/kernel/driver/block/scsi/src/lib.rs b/kernel/driver/block/scsi/src/lib.rs index d6477de6..20ebaa3f 100644 --- a/kernel/driver/block/scsi/src/lib.rs +++ b/kernel/driver/block/scsi/src/lib.rs @@ -2,7 +2,11 @@ #![allow(incomplete_features)] #![no_std] -use core::{mem::MaybeUninit, time::Duration}; +use core::{ + mem::MaybeUninit, + sync::atomic::{AtomicBool, Ordering}, + time::Duration, +}; use alloc::{ boxed::Box, collections::btree_map::BTreeMap, format, string::String, sync::Arc, vec::Vec, @@ -11,11 +15,12 @@ use async_trait::async_trait; use command::{ScsiReadCapacity, ScsiRequestSense, ScsiTestUnitReady}; use device_api::device::Device; use libk::{ + block, device::{block::BlockDevice, manager::probe_partitions}, dma::{DmaBuffer, DmaSlice, DmaSliceMut}, error::Error, fs::devfs, - task::runtime, + task::{runtime, sync::AsyncMutex}, }; use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; use libk_util::{ @@ -31,27 +36,69 @@ pub mod command; pub mod device; pub mod transport; -// TODO SCSI detach -pub struct ScsiDevice { - transport: IrqSafeSpinlock, +pub struct ScsiEnclosure { + transport: AsyncMutex, + units: Vec>>>, + index: OneTimeInit, + shutdown: AtomicBool, +} + +pub struct ScsiUnit { + enclosure: Arc, + lun: u8, lba_count: u64, lba_size: usize, max_lba_per_request: usize, - index: OneTimeInit, names: IrqSafeRwLock>, } -impl ScsiDevice { - // TODO support LUNs other than 0 - pub async fn setup(transport: T) -> Result, Error> { - let mut transport = ScsiTransportWrapper::new(transport); +impl ScsiEnclosure { + pub async fn setup( + transport: Box, + lun_count: usize, + ) -> Result, Error> { + let transport = AsyncMutex::new(ScsiTransportWrapper::new(transport)); + let units = (0..lun_count).map(|_| IrqSafeRwLock::new(None)).collect(); + let this = Arc::new(Self { + transport, + units, + index: OneTimeInit::new(), + shutdown: AtomicBool::new(false), + }); + register_enclosure(this.clone())?; - let mut attempts = 5; - let mut timeout = 100; + // Probe LUNs + for i in 0..lun_count { + if this.probe_lun(i as u8).await { + if let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await { + *this.units[i].write() = Some(unit); + } + } + } + + // Start enclosure poll task + let enclosure = this.clone(); + runtime::spawn(async move { + while !enclosure.shutdown.load(Ordering::Acquire) { + enclosure.poll().await; + runtime::sleep(Duration::from_millis(100)).await; + } + }) + .ok(); + + Ok(this) + } + + async fn probe_lun(self: &Arc, lun: u8) -> bool { + let mut attempts = 3; + let mut timeout = 10; + // TODO get statuses to better see if there's a real error or the LUN is not present while attempts > 0 { + let mut transport = self.transport.lock().await; + // TEST UNIT READY (6) if transport - .perform_command(0, ScsiTestUnitReady) + .perform_command(lun, ScsiTestUnitReady) .await .is_ok() { @@ -59,55 +106,109 @@ impl ScsiDevice { } // If not, send a REQUEST SENSE (6) - if transport.perform_command(0, ScsiRequestSense).await.is_ok() { - break; - } + transport.perform_command(lun, ScsiRequestSense).await.ok(); - log::warn!("scsi: unit not ready [{attempts}/5]"); + drop(transport); runtime::sleep(Duration::from_millis(timeout)).await; timeout *= 2; attempts -= 1; } + if attempts == 0 { - log::error!("scsi: unit not ready"); - return Err(Error::DoesNotExist); + false + } else { + true } + } + + async fn poll(self: &Arc) { + let index = *self.index.get(); + for lun in 0..self.units.len() { + let mut slot = self.units[lun].write(); + let present = self.probe_lun(lun as u8).await; + + if let Some(unit) = slot.as_ref() { + if !present { + log::warn!("scsi{index}u{lun} lost"); + unit.detach(); + *slot = None; + } + } else if present { + if let Ok(unit) = ScsiUnit::setup(self.clone(), lun as u8).await { + log::info!("scsi{index}u{lun} attached"); + *slot = Some(unit); + } else { + log::warn!("scsi{index}u{lun} attached, but could not setup"); + } + } + } + } + + pub fn detach(&self) { + self.shutdown.store(true, Ordering::Release); + let index = self.index.try_get().copied(); + + for unit in self.units.iter() { + if let Some(unit) = unit.write().take() { + unit.detach(); + } + } + + // Deregister the enclosure + if let Some(index) = index { + remove_enclosure(index); + } + } +} + +impl ScsiUnit { + pub async fn setup(enclosure: Arc, lun: u8) -> Result, Error> { + let enclosure_index = *enclosure.index.get(); + let mut transport = enclosure.transport.lock().await; // TODO INQUIRY fails for real USB flash drives // transport.perform_command(0, ScsiInquiry).await?; - let capacity_info = transport.perform_command(0, ScsiReadCapacity).await?; + let capacity_info = transport.perform_command(lun, ScsiReadCapacity).await?; let max_lba_per_request = transport.max_bytes_per_request() / capacity_info.block_size as usize; log::info!( - "scsi: lba_size={}, lba_count={}, max_lba_per_request={}", + "scsi{enclosure_index}u{lun}: lba_size={}, lba_count={}, max_lba_per_request={}", capacity_info.block_size, capacity_info.block_count, max_lba_per_request ); - Ok(Arc::new(Self { - transport: IrqSafeSpinlock::new(transport), + drop(transport); + + let unit = Arc::new(Self { + enclosure, + lun, lba_count: capacity_info.block_count.into(), lba_size: capacity_info.block_size as usize, max_lba_per_request, - index: OneTimeInit::new(), names: IrqSafeRwLock::new(Vec::new()), - })) + }); + + register_unit(enclosure_index, lun, unit.clone()); + + Ok(unit) } - pub fn detach(&self) { - if let Some(&index) = self.index.try_get() { - detach(index); + fn detach(&self) { + let id = *self.enclosure.index.get(); + log::info!("scsi{id}u{} detached", self.lun); + for name in self.names.read().iter() { + devfs::remove_node(name).ok(); } } } #[async_trait] -impl BlockDevice for ScsiDevice { +impl BlockDevice for ScsiUnit { fn allocate_buffer(&self, size: usize) -> Result]>, Error> { - self.transport.lock().allocate_buffer(size) + block!(self.enclosure.transport.lock().await.allocate_buffer(size))? } async fn read_aligned( @@ -133,7 +234,7 @@ impl BlockDevice for ScsiDevice { let lba_end = lba_start + lba_count as u64; - let mut transport = self.transport.lock(); + let mut transport = self.enclosure.transport.lock().await; // TODO DmaSliceMut subslicing let (buffer, range) = buffer.into_parts(); @@ -146,7 +247,9 @@ impl BlockDevice for ScsiDevice { let amount = count * self.lba_size; let dst_slice = buffer.slice_mut(offset..offset + amount); - let len = transport.read(0, lba, count as u16, dst_slice).await?; + let len = transport + .read(self.lun, lba, count as u16, dst_slice) + .await?; if len != amount { return Err(Error::InvalidArgument); } @@ -174,7 +277,7 @@ impl BlockDevice for ScsiDevice { } } -impl PageProvider for ScsiDevice { +impl PageProvider for ScsiUnit { fn get_page(&self, _offset: u64) -> Result { Err(Error::NotImplemented) } @@ -193,53 +296,57 @@ impl PageProvider for ScsiDevice { } } -impl Device for ScsiDevice { +impl Device for ScsiUnit { fn display_name(&self) -> &str { - "SCSI Storage Device" + "SCSI Unit" } } -impl Drop for ScsiDevice { +impl Drop for ScsiUnit { fn drop(&mut self) { - if let Some(index) = self.index.try_get() { - log::info!("scsi{index} dropped"); + if let Some(index) = self.enclosure.index.try_get() { + log::info!("scsi{index}u{} dropped", self.lun); } } } // TODO this is crap -static SCSI_DEVICES: IrqSafeSpinlock>> = +static SCSI_ENCLOSURES: IrqSafeSpinlock>> = IrqSafeSpinlock::new(BTreeMap::new()); static SCSI_BITMAP: IrqSafeSpinlock = IrqSafeSpinlock::new(0); -pub fn attach(device: Arc) -> Result<(), Error> { +fn register_enclosure(enclosure: Arc) -> Result<(), Error> { let index = { let mut bitmap = SCSI_BITMAP.lock(); let index = (0..8) .position(|p| *bitmap & (1 << p) == 0) .ok_or(Error::InvalidOperation) - .inspect_err(|_| log::warn!("Cannot attach SCSI device: too many of them"))? + .inspect_err(|_| log::warn!("Cannot attach SCSI enclosure: too many of them"))? as u32; - let mut devices = SCSI_DEVICES.lock(); + let mut devices = SCSI_ENCLOSURES.lock(); *bitmap |= 1 << index; assert!(!devices.contains_key(&index)); - devices.insert(index, device.clone()); + devices.insert(index, enclosure.clone()); index }; - let name = format!("scsi{index}"); - device.index.init(index); - device.names.write().push(name.clone()); - devfs::add_named_block_device(device.clone(), name.clone(), FileMode::new(0o600)).ok(); - log::info!("{name} attached"); + enclosure.index.init(index); + + Ok(()) +} + +fn register_unit(enclosure_index: u32, lun: u8, unit: Arc) { + let name = format!("scsi{enclosure_index}u{lun}"); + unit.names.write().push(name.clone()); + devfs::add_named_block_device(unit.clone(), name.clone(), FileMode::new(0o600)).ok(); // TODO this code is repeated everywhere runtime::spawn(async move { let name = name; - probe_partitions(device.clone(), |index, partition| { + probe_partitions(unit.clone(), |index, partition| { let partition_name = format!("{name}p{}", index + 1); log::info!("{name}: partition {partition_name}"); - device.names.write().push(partition_name.clone()); + unit.names.write().push(partition_name.clone()); devfs::add_named_block_device( Arc::new(partition), partition_name, @@ -251,23 +358,13 @@ pub fn attach(device: Arc) -> Result<(), Error> { .ok(); }) .ok(); - - Ok(()) } -pub fn detach(index: u32) { - let mut devices = SCSI_DEVICES.lock(); +fn remove_enclosure(index: u32) { + let mut devices = SCSI_ENCLOSURES.lock(); let mut bitmap = SCSI_BITMAP.lock(); - if let Some(device) = devices.remove(&index) { - { - let names = device.names.read(); - for name in names.iter() { - devfs::remove_node(name).ok(); - } - } - - *bitmap &= !(1 << index); - log::info!("scsi{index} detached"); - } + *bitmap &= !(1 << index); + devices.remove(&index); + log::info!("scsi: enclosure {index} detached"); } diff --git a/kernel/driver/block/scsi/src/transport.rs b/kernel/driver/block/scsi/src/transport.rs index ee8bca04..8e6fefc9 100644 --- a/kernel/driver/block/scsi/src/transport.rs +++ b/kernel/driver/block/scsi/src/transport.rs @@ -29,10 +29,8 @@ pub struct ScsiTransportWrapper { } impl ScsiTransportWrapper { - pub fn new(inner: T) -> Self { - Self { - inner: Box::new(inner), - } + pub fn new(inner: Box) -> Self { + Self { inner } } pub async fn read( diff --git a/kernel/driver/bus/usb/src/class_driver/mass_storage.rs b/kernel/driver/bus/usb/src/class_driver/mass_storage.rs index 32538bca..9d0728a8 100644 --- a/kernel/driver/bus/usb/src/class_driver/mass_storage.rs +++ b/kernel/driver/bus/usb/src/class_driver/mass_storage.rs @@ -7,7 +7,7 @@ use libk::{ dma::{DmaBuffer, DmaSliceMut}, error::Error, }; -use ygg_driver_scsi::{transport::ScsiTransport, ScsiDevice}; +use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure}; use crate::{ communication::UsbDirection, @@ -15,7 +15,7 @@ use crate::{ error::UsbError, info::{UsbDeviceClass, UsbEndpointType}, pipe::{ - control::UsbClassSpecificRequest, + control::{ControlTransferSetup, UsbClassSpecificRequest}, normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess}, }, }; @@ -56,7 +56,7 @@ struct Bbb { last_tag: u32, } -struct DetachHandler(Arc); +struct DetachHandler(Arc); impl Bbb { pub fn new( @@ -122,7 +122,6 @@ impl Bbb { return Err(Error::InvalidArgument); } if csw.status != 0x00 { - log::warn!("msc: csw error status {:#02x}", csw.status); return Err(Error::InvalidArgument); } Ok(()) @@ -150,7 +149,6 @@ impl ScsiTransport for Bbb { Ok(self.in_pipe.allocate_dma_buffer(size)?) } - // TODO DMA support for SCSI async fn perform_request_raw( &mut self, lun: u8, @@ -185,11 +183,20 @@ impl UsbDeviceDetachHandler for DetachHandler { #[repr(C)] pub struct BulkOnlyMassStorageReset; +#[derive(Debug, Pod, Zeroable, Clone, Copy)] +#[repr(C)] +pub struct GetMaxLun; + impl UsbClassSpecificRequest for BulkOnlyMassStorageReset { const BM_REQUEST_TYPE: u8 = 0b00100001; const B_REQUEST: u8 = 0b11111111; } +impl UsbClassSpecificRequest for GetMaxLun { + const BM_REQUEST_TYPE: u8 = 0b10100001; + const B_REQUEST: u8 = 0b11111110; +} + #[async_trait] impl UsbDriver for UsbMassStorageDriverBulkOnly { async fn run(self: Arc, device: Arc) -> Result<(), UsbError> { @@ -197,7 +204,6 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly { let config = device.select_configuration(|_| true).await?.unwrap(); // Bulk-in, bulk-out assert_eq!(config.endpoints.len(), 2); - // TODO those indices may be different let control_pipe = device.control_pipe(); let (in_index, in_info) = config .find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In)) @@ -215,21 +221,44 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly { // Perform a Bulk-Only Mass Storage Reset // TODO interface id? control_pipe - .class_specific_request::(0, 0) + .control_transfer(ControlTransferSetup { + bm_request_type: BulkOnlyMassStorageReset::BM_REQUEST_TYPE, + b_request: BulkOnlyMassStorageReset::B_REQUEST, + w_value: 0, + w_index: 0, + w_length: 0, + }) .await?; - // TODO get max LUN + // Get max LUN + // TODO on devices which do not support multiple LUNs, this command may STALL + let mut buffer = [MaybeUninit::uninit()]; + let len = control_pipe + .control_transfer_in( + ControlTransferSetup { + bm_request_type: GetMaxLun::BM_REQUEST_TYPE, + b_request: GetMaxLun::B_REQUEST, + w_value: 0, + w_index: 0, + w_length: 1, + }, + &mut buffer, + ) + .await?; + let max_lun = if len < 1 { + 0 + } else { + unsafe { buffer[0].assume_init() } + }; let bbb = Bbb::new(device.clone(), in_pipe, out_pipe)?; - let scsi = ScsiDevice::setup(bbb) + let scsi = ScsiEnclosure::setup(Box::new(bbb), max_lun as usize + 1) .await .inspect_err(|error| log::error!("msc: scsi error {error:?}")) .map_err(|_| UsbError::DriverError)?; let detach = DetachHandler(scsi.clone()); device.set_detach_handler(Arc::new(detach)); - ygg_driver_scsi::attach(scsi).ok(); - Ok(()) } diff --git a/kernel/driver/bus/usb/src/pipe/control.rs b/kernel/driver/bus/usb/src/pipe/control.rs index 31b3fbb6..f929b309 100644 --- a/kernel/driver/bus/usb/src/pipe/control.rs +++ b/kernel/driver/bus/usb/src/pipe/control.rs @@ -273,20 +273,20 @@ impl UsbControlPipeAccess { .await } - pub async fn class_specific_request( - &self, - w_value: u16, - w_index: u16, - ) -> Result<(), UsbError> { - self.control_transfer(ControlTransferSetup { - bm_request_type: D::BM_REQUEST_TYPE, - b_request: D::B_REQUEST, - w_value, - w_index, - w_length: 0, - }) - .await - } + // pub async fn class_specific_request( + // &self, + // w_value: u16, + // w_index: u16, + // ) -> Result { + // self.control_transfer(ControlTransferSetup { + // bm_request_type: D::BM_REQUEST_TYPE, + // b_request: D::B_REQUEST, + // w_value, + // w_index, + // w_length: 0, + // }) + // .await + // } pub async fn set_configuration(&self, value: u16) -> Result<(), UsbError> { self.perform_action::(value, 0).await