scsi: support multiple units per single enclosure
This commit is contained in:
parent
7358852f67
commit
80e5e72bb7
@ -2,7 +2,11 @@
|
|||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use core::{mem::MaybeUninit, time::Duration};
|
use core::{
|
||||||
|
mem::MaybeUninit,
|
||||||
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
boxed::Box, collections::btree_map::BTreeMap, format, string::String, sync::Arc, vec::Vec,
|
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 command::{ScsiReadCapacity, ScsiRequestSense, ScsiTestUnitReady};
|
||||||
use device_api::device::Device;
|
use device_api::device::Device;
|
||||||
use libk::{
|
use libk::{
|
||||||
|
block,
|
||||||
device::{block::BlockDevice, manager::probe_partitions},
|
device::{block::BlockDevice, manager::probe_partitions},
|
||||||
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
|
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
|
||||||
error::Error,
|
error::Error,
|
||||||
fs::devfs,
|
fs::devfs,
|
||||||
task::runtime,
|
task::{runtime, sync::AsyncMutex},
|
||||||
};
|
};
|
||||||
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
|
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
|
||||||
use libk_util::{
|
use libk_util::{
|
||||||
@ -31,27 +36,69 @@ pub mod command;
|
|||||||
pub mod device;
|
pub mod device;
|
||||||
pub mod transport;
|
pub mod transport;
|
||||||
|
|
||||||
// TODO SCSI detach
|
pub struct ScsiEnclosure {
|
||||||
pub struct ScsiDevice {
|
transport: AsyncMutex<ScsiTransportWrapper>,
|
||||||
transport: IrqSafeSpinlock<ScsiTransportWrapper>,
|
units: Vec<IrqSafeRwLock<Option<Arc<ScsiUnit>>>>,
|
||||||
|
index: OneTimeInit<u32>,
|
||||||
|
shutdown: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScsiUnit {
|
||||||
|
enclosure: Arc<ScsiEnclosure>,
|
||||||
|
lun: u8,
|
||||||
lba_count: u64,
|
lba_count: u64,
|
||||||
lba_size: usize,
|
lba_size: usize,
|
||||||
max_lba_per_request: usize,
|
max_lba_per_request: usize,
|
||||||
index: OneTimeInit<u32>,
|
|
||||||
names: IrqSafeRwLock<Vec<String>>,
|
names: IrqSafeRwLock<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScsiDevice {
|
impl ScsiEnclosure {
|
||||||
// TODO support LUNs other than 0
|
pub async fn setup(
|
||||||
pub async fn setup<T: ScsiTransport + 'static>(transport: T) -> Result<Arc<Self>, Error> {
|
transport: Box<dyn ScsiTransport>,
|
||||||
let mut transport = ScsiTransportWrapper::new(transport);
|
lun_count: usize,
|
||||||
|
) -> Result<Arc<Self>, 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;
|
// Probe LUNs
|
||||||
let mut timeout = 100;
|
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<Self>, 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 {
|
while attempts > 0 {
|
||||||
|
let mut transport = self.transport.lock().await;
|
||||||
|
|
||||||
// TEST UNIT READY (6)
|
// TEST UNIT READY (6)
|
||||||
if transport
|
if transport
|
||||||
.perform_command(0, ScsiTestUnitReady)
|
.perform_command(lun, ScsiTestUnitReady)
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
@ -59,55 +106,109 @@ impl ScsiDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If not, send a REQUEST SENSE (6)
|
// If not, send a REQUEST SENSE (6)
|
||||||
if transport.perform_command(0, ScsiRequestSense).await.is_ok() {
|
transport.perform_command(lun, ScsiRequestSense).await.ok();
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
log::warn!("scsi: unit not ready [{attempts}/5]");
|
drop(transport);
|
||||||
|
|
||||||
runtime::sleep(Duration::from_millis(timeout)).await;
|
runtime::sleep(Duration::from_millis(timeout)).await;
|
||||||
timeout *= 2;
|
timeout *= 2;
|
||||||
attempts -= 1;
|
attempts -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if attempts == 0 {
|
if attempts == 0 {
|
||||||
log::error!("scsi: unit not ready");
|
false
|
||||||
return Err(Error::DoesNotExist);
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn poll(self: &Arc<Self>) {
|
||||||
|
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<ScsiEnclosure>, lun: u8) -> Result<Arc<Self>, Error> {
|
||||||
|
let enclosure_index = *enclosure.index.get();
|
||||||
|
let mut transport = enclosure.transport.lock().await;
|
||||||
|
|
||||||
// TODO INQUIRY fails for real USB flash drives
|
// TODO INQUIRY fails for real USB flash drives
|
||||||
// transport.perform_command(0, ScsiInquiry).await?;
|
// 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 =
|
let max_lba_per_request =
|
||||||
transport.max_bytes_per_request() / capacity_info.block_size as usize;
|
transport.max_bytes_per_request() / capacity_info.block_size as usize;
|
||||||
log::info!(
|
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_size,
|
||||||
capacity_info.block_count,
|
capacity_info.block_count,
|
||||||
max_lba_per_request
|
max_lba_per_request
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Arc::new(Self {
|
drop(transport);
|
||||||
transport: IrqSafeSpinlock::new(transport),
|
|
||||||
|
let unit = Arc::new(Self {
|
||||||
|
enclosure,
|
||||||
|
lun,
|
||||||
lba_count: capacity_info.block_count.into(),
|
lba_count: capacity_info.block_count.into(),
|
||||||
lba_size: capacity_info.block_size as usize,
|
lba_size: capacity_info.block_size as usize,
|
||||||
max_lba_per_request,
|
max_lba_per_request,
|
||||||
index: OneTimeInit::new(),
|
|
||||||
names: IrqSafeRwLock::new(Vec::new()),
|
names: IrqSafeRwLock::new(Vec::new()),
|
||||||
}))
|
});
|
||||||
|
|
||||||
|
register_unit(enclosure_index, lun, unit.clone());
|
||||||
|
|
||||||
|
Ok(unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detach(&self) {
|
fn detach(&self) {
|
||||||
if let Some(&index) = self.index.try_get() {
|
let id = *self.enclosure.index.get();
|
||||||
detach(index);
|
log::info!("scsi{id}u{} detached", self.lun);
|
||||||
|
for name in self.names.read().iter() {
|
||||||
|
devfs::remove_node(name).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BlockDevice for ScsiDevice {
|
impl BlockDevice for ScsiUnit {
|
||||||
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||||
self.transport.lock().allocate_buffer(size)
|
block!(self.enclosure.transport.lock().await.allocate_buffer(size))?
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_aligned(
|
async fn read_aligned(
|
||||||
@ -133,7 +234,7 @@ impl BlockDevice for ScsiDevice {
|
|||||||
|
|
||||||
let lba_end = lba_start + lba_count as u64;
|
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
|
// TODO DmaSliceMut subslicing
|
||||||
let (buffer, range) = buffer.into_parts();
|
let (buffer, range) = buffer.into_parts();
|
||||||
@ -146,7 +247,9 @@ impl BlockDevice for ScsiDevice {
|
|||||||
let amount = count * self.lba_size;
|
let amount = count * self.lba_size;
|
||||||
|
|
||||||
let dst_slice = buffer.slice_mut(offset..offset + amount);
|
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 {
|
if len != amount {
|
||||||
return Err(Error::InvalidArgument);
|
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<PhysicalAddress, Error> {
|
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
|
||||||
Err(Error::NotImplemented)
|
Err(Error::NotImplemented)
|
||||||
}
|
}
|
||||||
@ -193,53 +296,57 @@ impl PageProvider for ScsiDevice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device for ScsiDevice {
|
impl Device for ScsiUnit {
|
||||||
fn display_name(&self) -> &str {
|
fn display_name(&self) -> &str {
|
||||||
"SCSI Storage Device"
|
"SCSI Unit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ScsiDevice {
|
impl Drop for ScsiUnit {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(index) = self.index.try_get() {
|
if let Some(index) = self.enclosure.index.try_get() {
|
||||||
log::info!("scsi{index} dropped");
|
log::info!("scsi{index}u{} dropped", self.lun);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is crap
|
// TODO this is crap
|
||||||
static SCSI_DEVICES: IrqSafeSpinlock<BTreeMap<u32, Arc<ScsiDevice>>> =
|
static SCSI_ENCLOSURES: IrqSafeSpinlock<BTreeMap<u32, Arc<ScsiEnclosure>>> =
|
||||||
IrqSafeSpinlock::new(BTreeMap::new());
|
IrqSafeSpinlock::new(BTreeMap::new());
|
||||||
static SCSI_BITMAP: IrqSafeSpinlock<u32> = IrqSafeSpinlock::new(0);
|
static SCSI_BITMAP: IrqSafeSpinlock<u32> = IrqSafeSpinlock::new(0);
|
||||||
|
|
||||||
pub fn attach(device: Arc<ScsiDevice>) -> Result<(), Error> {
|
fn register_enclosure(enclosure: Arc<ScsiEnclosure>) -> Result<(), Error> {
|
||||||
let index = {
|
let index = {
|
||||||
let mut bitmap = SCSI_BITMAP.lock();
|
let mut bitmap = SCSI_BITMAP.lock();
|
||||||
let index = (0..8)
|
let index = (0..8)
|
||||||
.position(|p| *bitmap & (1 << p) == 0)
|
.position(|p| *bitmap & (1 << p) == 0)
|
||||||
.ok_or(Error::InvalidOperation)
|
.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;
|
as u32;
|
||||||
let mut devices = SCSI_DEVICES.lock();
|
let mut devices = SCSI_ENCLOSURES.lock();
|
||||||
*bitmap |= 1 << index;
|
*bitmap |= 1 << index;
|
||||||
assert!(!devices.contains_key(&index));
|
assert!(!devices.contains_key(&index));
|
||||||
devices.insert(index, device.clone());
|
devices.insert(index, enclosure.clone());
|
||||||
index
|
index
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = format!("scsi{index}");
|
enclosure.index.init(index);
|
||||||
device.index.init(index);
|
|
||||||
device.names.write().push(name.clone());
|
Ok(())
|
||||||
devfs::add_named_block_device(device.clone(), name.clone(), FileMode::new(0o600)).ok();
|
}
|
||||||
log::info!("{name} attached");
|
|
||||||
|
fn register_unit(enclosure_index: u32, lun: u8, unit: Arc<ScsiUnit>) {
|
||||||
|
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
|
// TODO this code is repeated everywhere
|
||||||
runtime::spawn(async move {
|
runtime::spawn(async move {
|
||||||
let name = name;
|
let name = name;
|
||||||
probe_partitions(device.clone(), |index, partition| {
|
probe_partitions(unit.clone(), |index, partition| {
|
||||||
let partition_name = format!("{name}p{}", index + 1);
|
let partition_name = format!("{name}p{}", index + 1);
|
||||||
log::info!("{name}: partition {partition_name}");
|
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(
|
devfs::add_named_block_device(
|
||||||
Arc::new(partition),
|
Arc::new(partition),
|
||||||
partition_name,
|
partition_name,
|
||||||
@ -251,23 +358,13 @@ pub fn attach(device: Arc<ScsiDevice>) -> Result<(), Error> {
|
|||||||
.ok();
|
.ok();
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detach(index: u32) {
|
fn remove_enclosure(index: u32) {
|
||||||
let mut devices = SCSI_DEVICES.lock();
|
let mut devices = SCSI_ENCLOSURES.lock();
|
||||||
let mut bitmap = SCSI_BITMAP.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);
|
*bitmap &= !(1 << index);
|
||||||
log::info!("scsi{index} detached");
|
devices.remove(&index);
|
||||||
}
|
log::info!("scsi: enclosure {index} detached");
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,8 @@ pub struct ScsiTransportWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ScsiTransportWrapper {
|
impl ScsiTransportWrapper {
|
||||||
pub fn new<T: ScsiTransport + 'static>(inner: T) -> Self {
|
pub fn new(inner: Box<dyn ScsiTransport>) -> Self {
|
||||||
Self {
|
Self { inner }
|
||||||
inner: Box::new(inner),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(
|
pub async fn read(
|
||||||
|
@ -7,7 +7,7 @@ use libk::{
|
|||||||
dma::{DmaBuffer, DmaSliceMut},
|
dma::{DmaBuffer, DmaSliceMut},
|
||||||
error::Error,
|
error::Error,
|
||||||
};
|
};
|
||||||
use ygg_driver_scsi::{transport::ScsiTransport, ScsiDevice};
|
use ygg_driver_scsi::{transport::ScsiTransport, ScsiEnclosure};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
communication::UsbDirection,
|
communication::UsbDirection,
|
||||||
@ -15,7 +15,7 @@ use crate::{
|
|||||||
error::UsbError,
|
error::UsbError,
|
||||||
info::{UsbDeviceClass, UsbEndpointType},
|
info::{UsbDeviceClass, UsbEndpointType},
|
||||||
pipe::{
|
pipe::{
|
||||||
control::UsbClassSpecificRequest,
|
control::{ControlTransferSetup, UsbClassSpecificRequest},
|
||||||
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
normal::{UsbBulkInPipeAccess, UsbBulkOutPipeAccess},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -56,7 +56,7 @@ struct Bbb {
|
|||||||
last_tag: u32,
|
last_tag: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DetachHandler(Arc<ScsiDevice>);
|
struct DetachHandler(Arc<ScsiEnclosure>);
|
||||||
|
|
||||||
impl Bbb {
|
impl Bbb {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
@ -122,7 +122,6 @@ impl Bbb {
|
|||||||
return Err(Error::InvalidArgument);
|
return Err(Error::InvalidArgument);
|
||||||
}
|
}
|
||||||
if csw.status != 0x00 {
|
if csw.status != 0x00 {
|
||||||
log::warn!("msc: csw error status {:#02x}", csw.status);
|
|
||||||
return Err(Error::InvalidArgument);
|
return Err(Error::InvalidArgument);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -150,7 +149,6 @@ impl ScsiTransport for Bbb {
|
|||||||
Ok(self.in_pipe.allocate_dma_buffer(size)?)
|
Ok(self.in_pipe.allocate_dma_buffer(size)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO DMA support for SCSI
|
|
||||||
async fn perform_request_raw(
|
async fn perform_request_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
lun: u8,
|
lun: u8,
|
||||||
@ -185,11 +183,20 @@ impl UsbDeviceDetachHandler for DetachHandler {
|
|||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct BulkOnlyMassStorageReset;
|
pub struct BulkOnlyMassStorageReset;
|
||||||
|
|
||||||
|
#[derive(Debug, Pod, Zeroable, Clone, Copy)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GetMaxLun;
|
||||||
|
|
||||||
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
impl UsbClassSpecificRequest for BulkOnlyMassStorageReset {
|
||||||
const BM_REQUEST_TYPE: u8 = 0b00100001;
|
const BM_REQUEST_TYPE: u8 = 0b00100001;
|
||||||
const B_REQUEST: u8 = 0b11111111;
|
const B_REQUEST: u8 = 0b11111111;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UsbClassSpecificRequest for GetMaxLun {
|
||||||
|
const BM_REQUEST_TYPE: u8 = 0b10100001;
|
||||||
|
const B_REQUEST: u8 = 0b11111110;
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
||||||
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
async fn run(self: Arc<Self>, device: Arc<UsbDeviceAccess>) -> Result<(), UsbError> {
|
||||||
@ -197,7 +204,6 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
|||||||
let config = device.select_configuration(|_| true).await?.unwrap();
|
let config = device.select_configuration(|_| true).await?.unwrap();
|
||||||
// Bulk-in, bulk-out
|
// Bulk-in, bulk-out
|
||||||
assert_eq!(config.endpoints.len(), 2);
|
assert_eq!(config.endpoints.len(), 2);
|
||||||
// TODO those indices may be different
|
|
||||||
let control_pipe = device.control_pipe();
|
let control_pipe = device.control_pipe();
|
||||||
let (in_index, in_info) = config
|
let (in_index, in_info) = config
|
||||||
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
|
.find_endpoint(|ep| ep.is(UsbEndpointType::Bulk, UsbDirection::In))
|
||||||
@ -215,21 +221,44 @@ impl UsbDriver for UsbMassStorageDriverBulkOnly {
|
|||||||
// Perform a Bulk-Only Mass Storage Reset
|
// Perform a Bulk-Only Mass Storage Reset
|
||||||
// TODO interface id?
|
// TODO interface id?
|
||||||
control_pipe
|
control_pipe
|
||||||
.class_specific_request::<BulkOnlyMassStorageReset>(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?;
|
.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 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
|
.await
|
||||||
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
|
.inspect_err(|error| log::error!("msc: scsi error {error:?}"))
|
||||||
.map_err(|_| UsbError::DriverError)?;
|
.map_err(|_| UsbError::DriverError)?;
|
||||||
let detach = DetachHandler(scsi.clone());
|
let detach = DetachHandler(scsi.clone());
|
||||||
device.set_detach_handler(Arc::new(detach));
|
device.set_detach_handler(Arc::new(detach));
|
||||||
|
|
||||||
ygg_driver_scsi::attach(scsi).ok();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,20 +273,20 @@ impl UsbControlPipeAccess {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn class_specific_request<D: UsbClassSpecificRequest>(
|
// pub async fn class_specific_request<D: UsbClassSpecificRequest>(
|
||||||
&self,
|
// &self,
|
||||||
w_value: u16,
|
// w_value: u16,
|
||||||
w_index: u16,
|
// w_index: u16,
|
||||||
) -> Result<(), UsbError> {
|
// ) -> Result<usize, UsbError> {
|
||||||
self.control_transfer(ControlTransferSetup {
|
// self.control_transfer(ControlTransferSetup {
|
||||||
bm_request_type: D::BM_REQUEST_TYPE,
|
// bm_request_type: D::BM_REQUEST_TYPE,
|
||||||
b_request: D::B_REQUEST,
|
// b_request: D::B_REQUEST,
|
||||||
w_value,
|
// w_value,
|
||||||
w_index,
|
// w_index,
|
||||||
w_length: 0,
|
// w_length: 0,
|
||||||
})
|
// })
|
||||||
.await
|
// .await
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn set_configuration(&self, value: u16) -> Result<(), UsbError> {
|
pub async fn set_configuration(&self, value: u16) -> Result<(), UsbError> {
|
||||||
self.perform_action::<SetConfiguration>(value, 0).await
|
self.perform_action::<SetConfiguration>(value, 0).await
|
||||||
|
Loading…
x
Reference in New Issue
Block a user