usb: work on SCSI

This commit is contained in:
chemicstry 2023-01-23 15:35:05 +02:00
parent 7515369984
commit 2e5a437e59
14 changed files with 455 additions and 5 deletions

View File

@ -28,3 +28,6 @@ heapless = "0.7.10"
# for HID # for HID
usbd-hid = { version = "0.6.0", optional = true } usbd-hid = { version = "0.6.0", optional = true }
ssmarshal = { version = "1.0", default-features = false, optional = true } ssmarshal = { version = "1.0", default-features = false, optional = true }
paste = "1.0"
num_enum = { version = "0.5", default-features = false }

View File

@ -0,0 +1,132 @@
use core::marker::PhantomData;
pub trait Packet {
fn data(&self) -> &[u8];
fn data_mut(&mut self) -> &mut [u8];
#[inline]
fn get<F: AsPacketField>(&self, field: &F) -> F::T {
field.get(self.data())
}
#[inline]
fn set<F: AsPacketField>(&mut self, field: &F, val: F::T) {
field.set(self.data_mut(), val)
}
}
pub trait AsPacketField {
type T;
fn get(&self, data: &[u8]) -> Self::T;
fn set(&self, data: &mut [u8], val: Self::T);
}
pub struct BitField {
pub byte: usize,
pub bit: u8,
pub mask: u8,
}
impl BitField {
pub const fn new(byte: usize, bit: u8, size: u8) -> Self {
let mask = (0xFF >> size) << bit;
Self { byte, bit, mask }
}
}
impl AsPacketField for BitField {
type T = u8;
#[inline]
fn get(&self, data: &[u8]) -> Self::T {
(data[self.byte] & self.mask) >> self.bit
}
#[inline]
fn set(&self, data: &mut [u8], val: Self::T) {
data[self.byte] = (val << self.bit) | (data[self.byte] & !self.mask)
}
}
pub struct BoolField {
pub byte: usize,
pub bit: u8,
}
impl BoolField {
pub const fn new(byte: usize, bit: u8) -> Self {
Self { byte, bit }
}
}
impl AsPacketField for BoolField {
type T = bool;
#[inline]
fn get(&self, data: &[u8]) -> Self::T {
data[self.byte] & (1 << self.bit) != 0
}
#[inline]
fn set(&self, data: &mut [u8], val: Self::T) {
data[self.byte] = ((val as u8) << self.bit) | (data[self.byte] & !(1 << self.bit))
}
}
pub struct Field<T> {
pub byte: usize,
_phantom: PhantomData<T>,
}
impl<T> Field<T> {
pub const fn new(byte: usize) -> Self {
Self {
byte,
_phantom: PhantomData,
}
}
}
impl<T> AsPacketField for Field<T> {
type T = T;
#[inline]
fn get(&self, data: &[u8]) -> Self::T {
unsafe { core::ptr::read(data.as_ptr().offset(self.byte as _) as *const T) }
}
#[inline]
fn set(&self, data: &mut [u8], val: Self::T) {
unsafe { core::ptr::write(data.as_mut_ptr().offset(self.byte as _) as *mut T, val) }
}
}
#[cfg(test)]
mod tests {
use super::{AsPacketField, BitField, BoolField};
#[test]
fn bitfield() {
let field = BitField::new(0, 4, 3);
let mut data = [0b1111_1111];
assert_eq!(field.get(&data), 0b111);
field.set(&mut data, 0b000);
assert_eq!(field.get(&data), 0b000);
assert_eq!(data, [0b1000_1111]);
}
#[test]
fn boolfield() {
let field = BoolField::new(0, 5);
let mut data = [0b1111_1111];
assert_eq!(field.get(&data), true);
field.set(&mut data, false);
assert_eq!(field.get(&data), false);
assert_eq!(data, [0b1101_1111]);
}
}

View File

@ -0,0 +1,19 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BlockDeviceError {
// TODO
}
pub trait BlockDevice {
/// The number of bytes per block. This determines the size of the buffer passed
/// to read/write functions
fn block_size(&self) -> usize;
/// Number of blocks in device (max LBA index)
fn num_blocks(&self) -> u32;
/// Read the block indicated by `lba` into the provided buffer
fn read_block(&self, lba: u32, block: &mut [u8]) -> Result<(), BlockDeviceError>;
/// Write the `block` buffer to the block indicated by `lba`
fn write_block(&mut self, lba: u32, block: &[u8]) -> Result<(), BlockDeviceError>;
}

View File

@ -0,0 +1,10 @@
// #[bitfield(bytes = 1)]
// #[derive(BitfieldSpecifier)]
// pub struct Control {
// pub vendor_specific: B2,
// #[skip]
// __: B3,
// pub naca: B1,
// #[skip]
// __: B2,
// }

View File

@ -0,0 +1,17 @@
// use super::control::Control;
// #[bitfield(bytes = 6)]
// pub struct InquiryCommand {
// /// Always 0x12
// pub op_code: B8,
// #[skip]
// __: B7,
// /// If set, return vital data related to the page_code field
// pub enable_vital_product_data: B1,
// /// What kind of vital data to return
// pub page_code: B8,
// /// Amount of bytes allocation for data-in transfer
// pub allocation_length: B16,
// /// Control byte
// pub control: Control,
// }

View File

@ -0,0 +1,5 @@
// `bytes` in `#[bitfield(bytes = 6)]` causes a warning
#![allow(redundant_semicolons)]
pub mod control;
pub mod inquiry;

View File

@ -0,0 +1,55 @@
use crate::gen_enum;
gen_enum! {
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PeripheralQualifier<u8> {
/// A peripheral device having the specified peripheral device type is connected to this logical unit. If the device server is unable to determine whether or not a peripheral device is connected, it also shall use this peripheral qualifier. This peripheral qualifier does not mean that the peripheral device connected to the logical unit is ready for access.
Connected = 0b000,
/// A peripheral device having the specified peripheral device type is not connected to this logical unit. However, the device server is capable of supporting the specified peripheral device type on this logical unit.
NotConnected = 0b001,
/// The device server is not capable of supporting a peripheral device on this logical unit. For this peripheral qualifier the peripheral device type shall be set to 1Fh. All other peripheral device type values are reserved for this peripheral qualifier.
Incapable = 0b011,
}
}
gen_enum! {
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PeripheralDeviceType<u8> {
/// Direct access block device (e.g., magnetic disk)
DirectAccessBlock = 0x00,
/// Sequential-access device (e.g., magnetic tape)
SequentialAccess = 0x01,
/// Printer device
Printer = 0x02,
/// Processor device
Processor = 0x03,
/// Write-once device (e.g., some optical disks)
WriteOnce = 0x04,
/// CD/DVD device
CdDvd = 0x05,
/// Optical memory device (e.g., some optical disks)
OpticalMemory = 0x07,
/// Media changer device (e.g., jukeboxes)
MediaChanger = 0x08,
/// Storage array controller device (e.g., RAID)
StorageArrayController = 0x0C,
/// Enclosure services device
EnclosureServices = 0x0D,
/// Simplified direct-access device (e.g., magnetic disk)
SimplifiedDirectAccess = 0x0E,
/// Optical card reader/writer device
OpticaCardReaderWriter = 0x0F,
/// Bridge Controller Commands
BridgeController = 0x10,
/// Object-based Storage Device
ObjectBasedStorage = 0x11,
/// Automation/Drive Interface
AutomationInterface = 0x12,
/// Security manager device
SecurityManager = 0x13,
/// Well known logical unit
WellKnownLogicalUnit = 0x1E,
/// Unknown or no device type
UnknownOrNone = 0x1F,
}
}

View File

@ -0,0 +1,37 @@
// pub mod bitfield;
pub mod block_device;
pub mod commands;
pub mod enums;
pub mod packet;
pub mod responses;
use self::block_device::BlockDevice;
use crate::class::msc::transport::{self, CommandSetHandler};
pub struct Scsi<B: BlockDevice> {
device: B,
}
impl<B: BlockDevice> CommandSetHandler for Scsi<B> {
async fn command_out(
&mut self,
lun: u8,
cmd: &[u8],
pipe: &mut impl transport::DataPipeOut,
) -> Result<(), transport::CommandError> {
assert!(lun == 0, "LUNs are not supported");
Ok(())
}
async fn command_in(
&mut self,
lun: u8,
cmd: &[u8],
pipe: &mut impl transport::DataPipeIn,
) -> Result<(), transport::CommandError> {
assert!(lun == 0, "LUNs are not supported");
Ok(())
}
}

View File

@ -0,0 +1,151 @@
pub trait BitField {
type Output;
fn get(data: &[u8], offset: usize, size: usize) -> Self::Output;
fn set(data: &mut [u8], offset: usize, size: usize, val: Self);
}
impl BitField for u8 {
type Output = u8;
#[inline]
fn get(data: &[u8], offset: usize, size: usize) -> Self::Output {
let byte = offset / 8;
let bit = offset % 8;
let mask = (0xFF >> size) << bit;
(data[byte] & mask) >> bit
}
#[inline]
fn set(data: &mut [u8], offset: usize, size: usize, val: Self) {
let byte = offset / 8;
let bit = offset % 8;
data[byte] = 0;
}
}
#[macro_export]
macro_rules! gen_packet {
(
$(#[$meta:meta])*
$sv:vis struct $name:ident<$size:literal> {
$(
#[offset = $offset:expr, size = $bit_size:expr]
$field:ident: $ty:ty,
)*
}
) => {
$(#[$meta])*
$sv struct $name<T: AsRef<[u8]>> {
data: T
}
impl $name<[u8; $size]> {
const SIZE: usize = $size;
pub fn new() -> Self {
Self {
data: [0u8; Self::SIZE]
}
}
}
impl<T: AsRef<[u8]>> $name<T> {
pub const unsafe fn new_unchecked(data: T) -> Self {
Self { data }
}
pub fn from_bytes(buf: T) -> Option<Self> {
if buf.as_ref().len() < $name::SIZE {
None
} else {
Some(unsafe { Self::new_unchecked(buf) })
}
}
$(
#[inline]
pub fn $field(&self) -> <$ty as crate::class::msc::subclass::scsi::packet::BitField>::Output {
const _: () = core::assert!($offset + $bit_size <= $size * 8, "Field offset is out of range");
<$ty as crate::class::msc::subclass::scsi::packet::BitField>::get(self.data.as_ref(), $offset, $bit_size)
}
)*
}
impl<T: AsRef<[u8]> + AsMut<[u8]>> $name<T> {
$(
paste::paste! {
#[inline]
pub fn [<set_$field>](&mut self, val: $ty) {
<$ty as crate::class::msc::subclass::scsi::packet::BitField>::set(self.data.as_mut(), $offset, $bit_size, val)
}
}
)*
}
}
}
// gen_packet!(pub struct Test<8> {
// #[offset = 8 * 7, size = 3]
// test: u8,
// });
#[macro_export]
macro_rules! gen_enum {
(
$(#[$meta:meta])*
$sv:vis enum $name:ident<$ty:ty> {
$(
$(#[$variant_meta:meta])*
$variant:ident = $variant_val:literal,
)*
}
) => {
$(#[$meta])*
$sv enum $name {
$(
$(#[$variant_meta])*
$variant = $variant_val
),*
}
impl TryFrom<$ty> for $name {
type Error = $ty;
fn try_from(value: $ty) -> Result<Self, Self::Error> {
match value {
$($variant_val => Ok($name::$variant),)*
_ => Err(value)
}
}
}
impl From<$name> for $ty {
fn from(value: $name) -> $ty {
value as $ty
}
}
impl crate::class::msc::subclass::scsi::packet::BitField for $name {
type Output = Result<Self, $ty>;
#[inline]
fn get(data: &[u8], offset: usize, size: usize) -> Self::Output {
let val = <$ty as crate::class::msc::subclass::scsi::packet::BitField>::get(data, offset, size);
Self::try_from(val)
}
#[inline]
fn set(data: &mut [u8], offset: usize, size: usize, val: Self) {
<$ty as crate::class::msc::subclass::scsi::packet::BitField>::set(data, offset, size, val.into());
}
}
};
}
// gen_enum! {
// pub enum Testas<u8> {
// Hello = 0b111,
// Test = 0b1111,
// }
// }

View File

@ -0,0 +1,17 @@
use super::super::enums::PeripheralQualifier;
use crate::class::msc::subclass::scsi::enums::PeripheralDeviceType;
use crate::gen_packet;
gen_packet! {
/// Inquiry response can contain many extensions. We support only the minimum required 36 bytes.
pub struct InquiryResponse<36> {
#[offset = 0, size = 5]
peripheral_qualifier: PeripheralQualifier,
#[offset = 5, size = 3]
peripheral_device_type: PeripheralDeviceType,
}
}
fn test() {
let packet = InquiryResponse::new();
}

View File

@ -0,0 +1,4 @@
// `bytes` in `#[bitfield(bytes = 6)]` causes a warning
#![allow(redundant_semicolons)]
pub mod inquiry;

View File

@ -1,15 +1,15 @@
pub mod command_block_wrapper; pub mod cbw;
pub mod command_status_wrapper; pub mod csw;
use core::mem::{size_of, MaybeUninit}; use core::mem::{size_of, MaybeUninit};
use embassy_usb_driver::{Direction, Endpoint, EndpointError, EndpointIn, EndpointOut}; use embassy_usb_driver::{Direction, Endpoint, EndpointError, EndpointIn, EndpointOut};
use self::command_block_wrapper::CommandBlockWrapper; use self::cbw::CommandBlockWrapper;
use self::command_status_wrapper::{CommandStatus, CommandStatusWrapper}; use self::csw::{CommandStatus, CommandStatusWrapper};
use super::{CommandError, CommandSetHandler, DataPipeError, DataPipeIn, DataPipeOut}; use super::{CommandError, CommandSetHandler, DataPipeError, DataPipeIn, DataPipeOut};
use crate::class::msc::{MscProtocol, MscSubclass, USB_CLASS_MSC}; use crate::class::msc::{MscProtocol, MscSubclass, USB_CLASS_MSC};
use crate::control::{ControlHandler, InResponse, OutResponse, Request, RequestType}; use crate::control::{ControlHandler, InResponse, Request, RequestType};
use crate::driver::Driver; use crate::driver::Driver;
use crate::types::InterfaceNumber; use crate::types::InterfaceNumber;
use crate::Builder; use crate::Builder;