This commit is contained in:
chemicstry 2023-01-23 18:03:34 +02:00
parent 2e5a437e59
commit b5b1802a57
11 changed files with 381 additions and 187 deletions

View File

@ -1,12 +1,6 @@
pub mod subclass; pub mod subclass;
pub mod transport; pub mod transport;
use core::marker::PhantomData;
use crate::driver::Driver;
use crate::types::InterfaceNumber;
use crate::Builder;
/// USB Mass Storage Class ID /// USB Mass Storage Class ID
/// ///
/// Section 4.3 [USB Bulk Only Transport Spec](https://www.usb.org/document-library/mass-storage-bulk-only-10) /// Section 4.3 [USB Bulk Only Transport Spec](https://www.usb.org/document-library/mass-storage-bulk-only-10)

View File

@ -8,3 +8,14 @@
// #[skip] // #[skip]
// __: B2, // __: B2,
// } // }
use crate::packed_struct;
packed_struct! {
pub struct Control<1> {
#[offset = 0, size = 2]
vendor_specific: u8,
#[offset = 5, size = 1]
naca: bool,
}
}

View File

@ -15,3 +15,43 @@
// /// Control byte // /// Control byte
// pub control: Control, // pub control: Control,
// } // }
use super::control::Control;
use crate::class::msc::subclass::scsi::enums::{PeripheralDeviceType, PeripheralQualifier};
use crate::packed::PackedField;
use crate::packed_struct;
packed_struct! {
pub struct InquiryCommand<6> {
#[offset = 0, size = 8]
op_code: u8,
#[offset = 1*8, size = 1]
enable_vital_product_data: bool,
#[offset = 2*8, size = 8]
page_code: u8,
#[offset = 3*8, size = 16]
allocation_length: u16,
#[offset = 5*8, size = 8]
control: Control<T>,
}
}
impl InquiryCommand<[u8; InquiryCommand::SIZE]> {
pub const OPCODE: u8 = 0x12;
}
// impl<T: AsRef<[u8]>> defmt::Format for InquiryCommand<T> {
// fn format(&self, fmt: defmt::Formatter) {
// fmt.
// }
// }
packed_struct! {
/// 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,
}
}

View File

@ -1,6 +1,6 @@
use crate::gen_enum; use crate::packed_enum;
gen_enum! { packed_enum! {
#[derive(Clone, Copy, Eq, PartialEq, Debug)] #[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PeripheralQualifier<u8> { 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. /// 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.
@ -12,7 +12,7 @@ gen_enum! {
} }
} }
gen_enum! { packed_enum! {
#[derive(Clone, Copy, Eq, PartialEq, Debug)] #[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PeripheralDeviceType<u8> { pub enum PeripheralDeviceType<u8> {
/// Direct access block device (e.g., magnetic disk) /// Direct access block device (e.g., magnetic disk)

View File

@ -2,10 +2,9 @@
pub mod block_device; pub mod block_device;
pub mod commands; pub mod commands;
pub mod enums; pub mod enums;
pub mod packet;
pub mod responses;
use self::block_device::BlockDevice; use self::block_device::BlockDevice;
use crate::class::msc::subclass::scsi::commands::inquiry::InquiryCommand;
use crate::class::msc::transport::{self, CommandSetHandler}; use crate::class::msc::transport::{self, CommandSetHandler};
pub struct Scsi<B: BlockDevice> { pub struct Scsi<B: BlockDevice> {
@ -21,6 +20,15 @@ impl<B: BlockDevice> CommandSetHandler for Scsi<B> {
) -> Result<(), transport::CommandError> { ) -> Result<(), transport::CommandError> {
assert!(lun == 0, "LUNs are not supported"); assert!(lun == 0, "LUNs are not supported");
let op_code = cmd[0];
match op_code {
InquiryCommand::OPCODE => {
let cmd = InquiryCommand::from_bytes(cmd);
// info!("inquiry: {:#?}", cmd);
}
_ => warn!("Unknown opcode: {}", op_code),
}
Ok(()) Ok(())
} }

View File

@ -1,151 +0,0 @@
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

@ -1,17 +0,0 @@
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

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

View File

@ -11,7 +11,6 @@ use super::{CommandError, CommandSetHandler, DataPipeError, DataPipeIn, DataPipe
use crate::class::msc::{MscProtocol, MscSubclass, USB_CLASS_MSC}; use crate::class::msc::{MscProtocol, MscSubclass, USB_CLASS_MSC};
use crate::control::{ControlHandler, InResponse, Request, RequestType}; use crate::control::{ControlHandler, InResponse, Request, RequestType};
use crate::driver::Driver; use crate::driver::Driver;
use crate::types::InterfaceNumber;
use crate::Builder; use crate::Builder;
const REQ_GET_MAX_LUN: u8 = 0xFE; const REQ_GET_MAX_LUN: u8 = 0xFE;
@ -51,7 +50,6 @@ impl ControlHandler for Control {
} }
pub struct BulkOnlyTransport<'d, D: Driver<'d>, C: CommandSetHandler> { pub struct BulkOnlyTransport<'d, D: Driver<'d>, C: CommandSetHandler> {
msc_if: InterfaceNumber,
read_ep: D::EndpointOut, read_ep: D::EndpointOut,
write_ep: D::EndpointIn, write_ep: D::EndpointIn,
max_packet_size: u16, max_packet_size: u16,
@ -77,14 +75,12 @@ impl<'d, D: Driver<'d>, C: CommandSetHandler> BulkOnlyTransport<'d, D, C> {
let mut iface = func.interface(); let mut iface = func.interface();
iface.handler(control); iface.handler(control);
let msc_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_MSC, subclass as _, MscProtocol::BulkOnlyTransport as _); let mut alt = iface.alt_setting(USB_CLASS_MSC, subclass as _, MscProtocol::BulkOnlyTransport as _);
let read_ep = alt.endpoint_bulk_out(max_packet_size); let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(max_packet_size); let write_ep = alt.endpoint_bulk_in(max_packet_size);
Self { Self {
msc_if,
read_ep, read_ep,
write_ep, write_ep,
max_packet_size, max_packet_size,

View File

@ -12,6 +12,7 @@ pub mod class;
pub mod control; pub mod control;
pub mod descriptor; pub mod descriptor;
mod descriptor_reader; mod descriptor_reader;
mod packed;
pub mod types; pub mod types;
use embassy_futures::select::{select, Either}; use embassy_futures::select::{select, Either};

316
embassy-usb/src/packed.rs Normal file
View File

@ -0,0 +1,316 @@
/// A hack to allow compile-time assertions on const parameters.
/// Gets around `can't use generic parameters from outer function` error.
/// For some reason this assert is not shown in rust-analyzer, but cargo build catches it.
macro_rules! const_assert {
($($list:ident : $ty:ty),* => $expr:expr $(,$msg:literal)?) => {{
struct Assert<$(const $list: usize,)*>;
impl<$(const $list: $ty,)*> Assert<$($list,)*> {
const OK: () = core::assert!($expr, $($msg)?);
}
Assert::<$($list,)*>::OK
}};
}
pub trait PackedField {
type Output<'a>;
fn assert<const OFFSET: usize, const SIZE: usize>() {}
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a>;
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self);
}
impl PackedField for &[u8] {
type Output<'a> = &'a [u8];
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(OFFSET: usize => OFFSET % 8 == 0, "bit packing for u8 slices is not supported");
}
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
data
}
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
data.copy_from_slice(val)
}
}
impl PackedField for &mut [u8] {
type Output<'a> = &'a [u8];
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(OFFSET: usize => OFFSET % 8 == 0, "bit packing for u8 slices is not supported");
}
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
data
}
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
data.copy_from_slice(val)
}
}
impl<const N: usize> PackedField for [u8; N] {
type Output<'a> = [u8; N];
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(N: usize, SIZE: usize => SIZE == N, "Incorrect array size");
const_assert!(OFFSET: usize => OFFSET % 8 == 0, "bit packing for arrays is not supported");
}
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
data.try_into().unwrap()
}
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
data.copy_from_slice(&val)
}
}
impl PackedField for bool {
type Output<'a> = bool;
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(SIZE: usize => SIZE == 1, "bool size must equal 1");
}
#[inline]
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
let byte = OFFSET / 8;
let bit = OFFSET % 8;
(data[byte] & (1 << bit)) != 0
}
#[inline]
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
let byte = OFFSET / 8;
let bit = OFFSET % 8;
let mask = (val as u8) << bit;
data[byte] = mask | (data[byte] & !mask);
}
}
impl PackedField for u8 {
type Output<'a> = u8;
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(SIZE: usize => SIZE <= 8, "u8 is not large enough");
const_assert!(OFFSET: usize, SIZE: usize => {
let bit = OFFSET % 8;
SIZE <= (8 - bit)
}, "bit packing across byte boundary is not supported");
}
#[inline]
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
let byte = OFFSET / 8;
let bit = OFFSET % 8;
let mask = (0xFF >> SIZE) << bit;
(data[byte] & mask) >> bit
}
#[inline]
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
let byte = OFFSET / 8;
let bit = OFFSET % 8;
let mask = (0xFF >> SIZE) << bit;
data[byte] = (val << bit) | (data[byte] & !mask);
}
}
macro_rules! impl_packed_field_int {
($ty:ty, $size:literal) => {
impl PackedField for $ty {
type Output<'a> = $ty;
fn assert<const OFFSET: usize, const SIZE: usize>() {
const_assert!(SIZE: usize => SIZE <= $size, "type is not large enough");
// most protocols only use bit packing at byte (u8) boundaries, so this is okay for now
const_assert!(OFFSET: usize => OFFSET % 8 == 0, "bit packing for this type is not supported");
}
#[inline]
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
let byte = OFFSET / 8;
unsafe { *(data[byte..].as_ptr() as *const $ty)}
}
#[inline]
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
let byte = OFFSET / 8;
unsafe { *(data[byte..].as_ptr() as *mut $ty) = val; }
}
}
};
}
impl_packed_field_int!(u16, 16);
impl_packed_field_int!(u32, 32);
impl_packed_field_int!(u64, 64);
impl_packed_field_int!(i8, 8);
impl_packed_field_int!(i16, 16);
impl_packed_field_int!(i32, 32);
impl_packed_field_int!(i64, 64);
#[macro_export]
macro_rules! packed_struct {
(
$(#[$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]>> {
pub data: T
}
impl $name<[u8; $size]> {
const SIZE: usize = $size;
pub fn new() -> Self {
Self {
data: [0u8; Self::SIZE]
}
}
}
impl<'d, T: AsRef<[u8]> + crate::packed::PackedField<Output<'d> = T> + 'd> $name<T> {
pub const unsafe fn from_bytes_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::from_bytes_unchecked(buf) })
}
}
$(
#[inline]
pub fn $field(&self) -> <$ty as crate::packed::PackedField>::Output<'d> {
const _: () = core::assert!($offset + $bit_size <= $size * 8, "Field offset is out of range");
<$ty as crate::packed::PackedField>::assert::<{$offset}, {$bit_size}>();
<$ty as crate::packed::PackedField>::get::<{$offset}, {$bit_size}>(self.data.as_ref())
}
)*
}
impl<'d, T: AsRef<[u8]> + AsMut<[u8]> + crate::packed::PackedField<Output<'d> = T> + 'd> $name<T> {
$(
paste::paste! {
#[inline]
pub fn [<set_$field>](&mut self, val: $ty) {
const _: () = core::assert!($offset + $bit_size <= $size * 8, "Field offset is out of range");
<$ty as crate::packed::PackedField>::assert::<{$offset}, {$bit_size}>();
<$ty as crate::packed::PackedField>::set::<{$offset}, {$bit_size}>(self.data.as_mut(), val)
}
}
)*
}
impl<T: AsRef<[u8]> + for<'a> crate::packed::PackedField<Output<'a> = T>> crate::packed::PackedField for $name<T> {
type Output<'a> = Self;
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
<T as crate::packed::PackedField>::assert::<OFFSET, SIZE>();
let val = <T as crate::packed::PackedField>::get::<OFFSET, SIZE>(data);
unsafe { Self::from_bytes_unchecked(val) }
}
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
<T as crate::packed::PackedField>::assert::<OFFSET, SIZE>();
<T as crate::packed::PackedField>::set::<OFFSET, SIZE>(data, val.data);
}
}
impl<T: AsRef<[u8]> + for<'a> crate::packed::PackedField<Output<'a> = T>> core::fmt::Debug for $name<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(stringify!($name))
$(
.field(stringify!($field), &self.$field())
)*
.finish()
}
}
}
}
// packed_struct!(pub struct Test<8> {
// #[offset = 8 * 6, size = 8]
// test: u8,
// });
// pub fn test() -> u8 {
// let t = Test::new();
// t.test()
// }
#[macro_export]
macro_rules! packed_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::packed::PackedField for $name {
type Output<'a> = Result<Self, $ty>;
#[inline]
fn get<'a, const OFFSET: usize, const SIZE: usize>(data: &'a [u8]) -> Self::Output<'a> {
<$ty as crate::packed::PackedField>::assert::<OFFSET, SIZE>();
let val = <$ty as crate::packed::PackedField>::get::<OFFSET, SIZE>(data);
Self::try_from(val)
}
#[inline]
fn set<const OFFSET: usize, const SIZE: usize>(data: &mut [u8], val: Self) {
<$ty as crate::packed::PackedField>::assert::<OFFSET, SIZE>();
<$ty as crate::packed::PackedField>::set::<OFFSET, SIZE>(data, val.into());
}
}
};
}
// gen_enum! {
// pub enum Testas<u8> {
// Hello = 0b111,
// Test = 0b1111,
// }
// }