progress
This commit is contained in:
parent
2e5a437e59
commit
b5b1802a57
@ -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)
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
// }
|
|
||||||
// }
|
|
@ -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();
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
// `bytes` in `#[bitfield(bytes = 6)]` causes a warning
|
|
||||||
#![allow(redundant_semicolons)]
|
|
||||||
|
|
||||||
pub mod inquiry;
|
|
@ -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,
|
||||||
|
@ -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
316
embassy-usb/src/packed.rs
Normal 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,
|
||||||
|
// }
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user