usb/cdc-ncm: add embassy-net Device implementation.

This commit is contained in:
Dario Nieuwenhuis
2022-12-07 16:03:03 +01:00
parent aaaf5f23a8
commit e9219405ca
9 changed files with 522 additions and 371 deletions

View File

@ -0,0 +1,449 @@
use core::cell::RefCell;
use core::mem::MaybeUninit;
use core::task::Context;
use embassy_futures::select::{select, Either};
use embassy_net::device::{Device as DeviceTrait, DeviceCapabilities, LinkState, Medium};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::waitqueue::WakerRegistration;
use embassy_usb_driver::Driver;
use super::{CdcNcmClass, Receiver, Sender};
pub struct State<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> {
rx: [PacketBuf<MTU>; N_RX],
tx: [PacketBuf<MTU>; N_TX],
inner: MaybeUninit<StateInner<'d, MTU>>,
}
impl<'d, const MTU: usize, const N_RX: usize, const N_TX: usize> State<'d, MTU, N_RX, N_TX> {
const NEW_PACKET: PacketBuf<MTU> = PacketBuf::new();
pub const fn new() -> Self {
Self {
rx: [Self::NEW_PACKET; N_RX],
tx: [Self::NEW_PACKET; N_TX],
inner: MaybeUninit::uninit(),
}
}
}
struct StateInner<'d, const MTU: usize> {
rx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
tx: zerocopy_channel::Channel<'d, NoopRawMutex, PacketBuf<MTU>>,
link_state: Mutex<NoopRawMutex, RefCell<LinkStateState>>,
}
/// State of the LinkState
struct LinkStateState {
state: LinkState,
waker: WakerRegistration,
}
pub struct Runner<'d, D: Driver<'d>, const MTU: usize> {
tx_usb: Sender<'d, D>,
tx_chan: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
rx_usb: Receiver<'d, D>,
rx_chan: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
}
impl<'d, D: Driver<'d>, const MTU: usize> Runner<'d, D, MTU> {
pub async fn run(mut self) -> ! {
let rx_fut = async move {
loop {
trace!("WAITING for connection");
self.link_state.lock(|s| {
let s = &mut *s.borrow_mut();
s.state = LinkState::Down;
s.waker.wake();
});
self.rx_usb.wait_connection().await.unwrap();
trace!("Connected");
self.link_state.lock(|s| {
let s = &mut *s.borrow_mut();
s.state = LinkState::Up;
s.waker.wake();
});
loop {
let p = self.rx_chan.send().await;
match self.rx_usb.read_packet(&mut p.buf).await {
Ok(n) => {
p.len = n;
self.rx_chan.send_done();
}
Err(e) => {
warn!("error reading packet: {:?}", e);
break;
}
};
}
}
};
let tx_fut = async move {
loop {
let p = self.tx_chan.recv().await;
if let Err(e) = self.tx_usb.write_packet(&p.buf[..p.len]).await {
warn!("Failed to TX packet: {:?}", e);
}
self.tx_chan.recv_done();
}
};
match select(rx_fut, tx_fut).await {
Either::First(x) => x,
Either::Second(x) => x,
}
}
}
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
pub fn into_embassy_net_device<const MTU: usize, const N_RX: usize, const N_TX: usize>(
self,
state: &'d mut State<'d, MTU, N_RX, N_TX>,
ethernet_address: [u8; 6],
) -> (Runner<'d, D, MTU>, Device<'d, MTU>) {
let (tx_usb, rx_usb) = self.split();
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1514; // 1500 IP + 14 ethernet header
caps.medium = Medium::Ethernet;
let state = state.inner.write(StateInner {
rx: zerocopy_channel::Channel::new(&mut state.rx[..]),
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
link_state: Mutex::new(RefCell::new(LinkStateState {
state: LinkState::Down,
waker: WakerRegistration::new(),
})),
});
let (rx_sender, rx_receiver) = state.rx.split();
let (tx_sender, tx_receiver) = state.tx.split();
(
Runner {
tx_usb,
tx_chan: tx_receiver,
rx_usb,
rx_chan: rx_sender,
link_state: &state.link_state,
},
Device {
caps,
ethernet_address,
link_state: &state.link_state,
rx: rx_receiver,
tx: tx_sender,
},
)
}
}
pub struct PacketBuf<const MTU: usize> {
len: usize,
buf: [u8; MTU],
}
impl<const MTU: usize> PacketBuf<MTU> {
pub const fn new() -> Self {
Self { len: 0, buf: [0; MTU] }
}
}
pub struct Device<'d, const MTU: usize> {
rx: zerocopy_channel::Receiver<'d, NoopRawMutex, PacketBuf<MTU>>,
tx: zerocopy_channel::Sender<'d, NoopRawMutex, PacketBuf<MTU>>,
link_state: &'d Mutex<NoopRawMutex, RefCell<LinkStateState>>,
caps: DeviceCapabilities,
ethernet_address: [u8; 6],
}
impl<'d, const MTU: usize> DeviceTrait for Device<'d, MTU> {
type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ;
type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
} else {
None
}
}
/// Construct a transmit token.
fn transmit(&mut self, cx: &mut Context) -> Option<Self::TxToken<'_>> {
if self.tx.poll_send(cx).is_ready() {
Some(TxToken { tx: self.tx.borrow() })
} else {
None
}
}
/// Get a description of device capabilities.
fn capabilities(&self) -> DeviceCapabilities {
self.caps.clone()
}
fn ethernet_address(&self) -> [u8; 6] {
self.ethernet_address
}
fn link_state(&mut self, cx: &mut Context) -> LinkState {
self.link_state.lock(|s| {
let s = &mut *s.borrow_mut();
s.waker.register(cx.waker());
s.state
})
}
}
pub struct RxToken<'a, const MTU: usize> {
rx: zerocopy_channel::Receiver<'a, NoopRawMutex, PacketBuf<MTU>>,
}
impl<'a, const MTU: usize> embassy_net::device::RxToken for RxToken<'a, MTU> {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
let pkt = unwrap!(self.rx.try_recv());
let r = f(&mut pkt.buf[..pkt.len]);
self.rx.recv_done();
r
}
}
pub struct TxToken<'a, const MTU: usize> {
tx: zerocopy_channel::Sender<'a, NoopRawMutex, PacketBuf<MTU>>,
}
impl<'a, const MTU: usize> embassy_net::device::TxToken for TxToken<'a, MTU> {
fn consume<R, F>(mut self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
let pkt = unwrap!(self.tx.try_send());
let r = f(&mut pkt.buf[..len]);
pkt.len = len;
self.tx.send_done();
r
}
}
mod zerocopy_channel {
use core::cell::RefCell;
use core::future::poll_fn;
use core::marker::PhantomData;
use core::task::{Context, Poll};
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embassy_sync::waitqueue::WakerRegistration;
pub struct Channel<'a, M: RawMutex, T> {
buf: *mut T,
phantom: PhantomData<&'a mut T>,
state: Mutex<M, RefCell<State>>,
}
impl<'a, M: RawMutex, T> Channel<'a, M, T> {
pub fn new(buf: &'a mut [T]) -> Self {
let len = buf.len();
assert!(len != 0);
Self {
buf: buf.as_mut_ptr(),
phantom: PhantomData,
state: Mutex::new(RefCell::new(State {
len,
front: 0,
back: 0,
full: false,
send_waker: WakerRegistration::new(),
recv_waker: WakerRegistration::new(),
})),
}
}
pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
(Sender { channel: self }, Receiver { channel: self })
}
}
pub struct Sender<'a, M: RawMutex, T> {
channel: &'a Channel<'a, M, T>,
}
impl<'a, M: RawMutex, T> Sender<'a, M, T> {
pub fn borrow(&mut self) -> Sender<'_, M, T> {
Sender { channel: self.channel }
}
pub fn try_send(&mut self) -> Option<&mut T> {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.push_index() {
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
None => None,
}
})
}
pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.push_index() {
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
None => {
s.recv_waker.register(cx.waker());
Poll::Pending
}
}
})
}
pub async fn send(&mut self) -> &mut T {
let i = poll_fn(|cx| {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.push_index() {
Some(i) => Poll::Ready(i),
None => {
s.recv_waker.register(cx.waker());
Poll::Pending
}
}
})
})
.await;
unsafe { &mut *self.channel.buf.add(i) }
}
pub fn send_done(&mut self) {
self.channel.state.lock(|s| s.borrow_mut().push_done())
}
}
pub struct Receiver<'a, M: RawMutex, T> {
channel: &'a Channel<'a, M, T>,
}
impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
pub fn borrow(&mut self) -> Receiver<'_, M, T> {
Receiver { channel: self.channel }
}
pub fn try_recv(&mut self) -> Option<&mut T> {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.pop_index() {
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
None => None,
}
})
}
pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.pop_index() {
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
None => {
s.send_waker.register(cx.waker());
Poll::Pending
}
}
})
}
pub async fn recv(&mut self) -> &mut T {
let i = poll_fn(|cx| {
self.channel.state.lock(|s| {
let s = &mut *s.borrow_mut();
match s.pop_index() {
Some(i) => Poll::Ready(i),
None => {
s.send_waker.register(cx.waker());
Poll::Pending
}
}
})
})
.await;
unsafe { &mut *self.channel.buf.add(i) }
}
pub fn recv_done(&mut self) {
self.channel.state.lock(|s| s.borrow_mut().pop_done())
}
}
struct State {
len: usize,
/// Front index. Always 0..=(N-1)
front: usize,
/// Back index. Always 0..=(N-1).
back: usize,
/// Used to distinguish "empty" and "full" cases when `front == back`.
/// May only be `true` if `front == back`, always `false` otherwise.
full: bool,
send_waker: WakerRegistration,
recv_waker: WakerRegistration,
}
impl State {
fn increment(&self, i: usize) -> usize {
if i + 1 == self.len {
0
} else {
i + 1
}
}
fn is_full(&self) -> bool {
self.full
}
fn is_empty(&self) -> bool {
self.front == self.back && !self.full
}
fn push_index(&mut self) -> Option<usize> {
match self.is_full() {
true => None,
false => Some(self.back),
}
}
fn push_done(&mut self) {
assert!(!self.is_full());
self.back = self.increment(self.back);
if self.back == self.front {
self.full = true;
}
self.send_waker.wake();
}
fn pop_index(&mut self) -> Option<usize> {
match self.is_empty() {
true => None,
false => Some(self.front),
}
}
fn pop_done(&mut self) {
assert!(!self.is_empty());
self.front = self.increment(self.front);
self.full = false;
self.recv_waker.wake();
}
}
}

View File

@ -0,0 +1,496 @@
/// CDC-NCM, aka Ethernet over USB.
///
/// # Compatibility
///
/// Windows: NOT supported in Windows 10. Supported in Windows 11.
///
/// Linux: Well-supported since forever.
///
/// Android: Support for CDC-NCM is spotty and varies across manufacturers.
///
/// - On Pixel 4a, it refused to work on Android 11, worked on Android 12.
/// - if the host's MAC address has the "locally-administered" bit set (bit 1 of first byte),
/// it doesn't work! The "Ethernet tethering" option in settings doesn't get enabled.
/// This is due to regex spaghetti: https://android.googlesource.com/platform/frameworks/base/+/refs/tags/android-mainline-12.0.0_r84/core/res/res/values/config.xml#417
/// and this nonsense in the linux kernel: https://github.com/torvalds/linux/blob/c00c5e1d157bec0ef0b0b59aa5482eb8dc7e8e49/drivers/net/usb/usbnet.c#L1751-L1757
use core::intrinsics::copy_nonoverlapping;
use core::mem::{size_of, MaybeUninit};
use crate::control::{self, ControlHandler, InResponse, OutResponse, Request};
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
use crate::types::*;
use crate::Builder;
#[cfg(feature = "embassy-net")]
pub mod embassy_net;
/// This should be used as `device_class` when building the `UsbDevice`.
pub const USB_CLASS_CDC: u8 = 0x02;
const USB_CLASS_CDC_DATA: u8 = 0x0a;
const CDC_SUBCLASS_NCM: u8 = 0x0d;
const CDC_PROTOCOL_NONE: u8 = 0x00;
const CDC_PROTOCOL_NTB: u8 = 0x01;
const CS_INTERFACE: u8 = 0x24;
const CDC_TYPE_HEADER: u8 = 0x00;
const CDC_TYPE_UNION: u8 = 0x06;
const CDC_TYPE_ETHERNET: u8 = 0x0F;
const CDC_TYPE_NCM: u8 = 0x1A;
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
//const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
//const REQ_SET_ETHERNET_MULTICAST_FILTERS: u8 = 0x40;
//const REQ_SET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x41;
//const REQ_GET_ETHERNET_POWER_MANAGEMENT_PATTERN_FILTER: u8 = 0x42;
//const REQ_SET_ETHERNET_PACKET_FILTER: u8 = 0x43;
//const REQ_GET_ETHERNET_STATISTIC: u8 = 0x44;
const REQ_GET_NTB_PARAMETERS: u8 = 0x80;
//const REQ_GET_NET_ADDRESS: u8 = 0x81;
//const REQ_SET_NET_ADDRESS: u8 = 0x82;
//const REQ_GET_NTB_FORMAT: u8 = 0x83;
//const REQ_SET_NTB_FORMAT: u8 = 0x84;
//const REQ_GET_NTB_INPUT_SIZE: u8 = 0x85;
const REQ_SET_NTB_INPUT_SIZE: u8 = 0x86;
//const REQ_GET_MAX_DATAGRAM_SIZE: u8 = 0x87;
//const REQ_SET_MAX_DATAGRAM_SIZE: u8 = 0x88;
//const REQ_GET_CRC_MODE: u8 = 0x89;
//const REQ_SET_CRC_MODE: u8 = 0x8A;
//const NOTIF_MAX_PACKET_SIZE: u16 = 8;
//const NOTIF_POLL_INTERVAL: u8 = 20;
const NTB_MAX_SIZE: usize = 2048;
const SIG_NTH: u32 = 0x484d434e;
const SIG_NDP_NO_FCS: u32 = 0x304d434e;
const SIG_NDP_WITH_FCS: u32 = 0x314d434e;
const ALTERNATE_SETTING_DISABLED: u8 = 0x00;
const ALTERNATE_SETTING_ENABLED: u8 = 0x01;
/// Simple NTB header (NTH+NDP all in one) for sending packets
#[repr(packed)]
#[allow(unused)]
struct NtbOutHeader {
// NTH
nth_sig: u32,
nth_len: u16,
nth_seq: u16,
nth_total_len: u16,
nth_first_index: u16,
// NDP
ndp_sig: u32,
ndp_len: u16,
ndp_next_index: u16,
ndp_datagram_index: u16,
ndp_datagram_len: u16,
ndp_term1: u16,
ndp_term2: u16,
}
#[repr(packed)]
#[allow(unused)]
struct NtbParameters {
length: u16,
formats_supported: u16,
in_params: NtbParametersDir,
out_params: NtbParametersDir,
}
#[repr(packed)]
#[allow(unused)]
struct NtbParametersDir {
max_size: u32,
divisor: u16,
payload_remainder: u16,
out_alignment: u16,
max_datagram_count: u16,
}
fn byteify<T>(buf: &mut [u8], data: T) -> &[u8] {
let len = size_of::<T>();
unsafe { copy_nonoverlapping(&data as *const _ as *const u8, buf.as_mut_ptr(), len) }
&buf[..len]
}
pub struct State<'a> {
comm_control: MaybeUninit<CommControl<'a>>,
data_control: MaybeUninit<DataControl>,
shared: ControlShared,
}
impl<'a> State<'a> {
pub fn new() -> Self {
Self {
comm_control: MaybeUninit::uninit(),
data_control: MaybeUninit::uninit(),
shared: Default::default(),
}
}
}
/// Shared data between Control and CdcAcmClass
struct ControlShared {
mac_addr: [u8; 6],
}
impl Default for ControlShared {
fn default() -> Self {
ControlShared { mac_addr: [0; 6] }
}
}
struct CommControl<'a> {
mac_addr_string: StringIndex,
shared: &'a ControlShared,
mac_addr_str: [u8; 12],
}
impl<'d> ControlHandler for CommControl<'d> {
fn control_out(&mut self, req: control::Request, _data: &[u8]) -> OutResponse {
match req.request {
REQ_SEND_ENCAPSULATED_COMMAND => {
// We don't actually support encapsulated commands but pretend we do for standards
// compatibility.
OutResponse::Accepted
}
REQ_SET_NTB_INPUT_SIZE => {
// TODO
OutResponse::Accepted
}
_ => OutResponse::Rejected,
}
}
fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> InResponse<'a> {
match req.request {
REQ_GET_NTB_PARAMETERS => {
let res = NtbParameters {
length: size_of::<NtbParameters>() as _,
formats_supported: 1, // only 16bit,
in_params: NtbParametersDir {
max_size: NTB_MAX_SIZE as _,
divisor: 4,
payload_remainder: 0,
out_alignment: 4,
max_datagram_count: 0, // not used
},
out_params: NtbParametersDir {
max_size: NTB_MAX_SIZE as _,
divisor: 4,
payload_remainder: 0,
out_alignment: 4,
max_datagram_count: 1, // We only decode 1 packet per NTB
},
};
InResponse::Accepted(byteify(buf, res))
}
_ => InResponse::Rejected,
}
}
fn get_string(&mut self, index: StringIndex, _lang_id: u16) -> Option<&str> {
if index == self.mac_addr_string {
let mac_addr = self.shared.mac_addr;
let s = &mut self.mac_addr_str;
for i in 0..12 {
let n = (mac_addr[i / 2] >> ((1 - i % 2) * 4)) & 0xF;
s[i] = match n {
0x0..=0x9 => b'0' + n,
0xA..=0xF => b'A' + n - 0xA,
_ => unreachable!(),
}
}
Some(unsafe { core::str::from_utf8_unchecked(s) })
} else {
warn!("unknown string index requested");
None
}
}
}
struct DataControl {}
impl ControlHandler for DataControl {
fn set_alternate_setting(&mut self, alternate_setting: u8) {
match alternate_setting {
ALTERNATE_SETTING_ENABLED => info!("ncm: interface enabled"),
ALTERNATE_SETTING_DISABLED => info!("ncm: interface disabled"),
_ => unreachable!(),
}
}
}
pub struct CdcNcmClass<'d, D: Driver<'d>> {
_comm_if: InterfaceNumber,
comm_ep: D::EndpointIn,
data_if: InterfaceNumber,
read_ep: D::EndpointOut,
write_ep: D::EndpointIn,
_control: &'d ControlShared,
}
impl<'d, D: Driver<'d>> CdcNcmClass<'d, D> {
pub fn new(
builder: &mut Builder<'d, D>,
state: &'d mut State<'d>,
mac_address: [u8; 6],
max_packet_size: u16,
) -> Self {
state.shared.mac_addr = mac_address;
let mut func = builder.function(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);
// Control interface
let mut iface = func.interface();
let mac_addr_string = iface.string();
iface.handler(state.comm_control.write(CommControl {
mac_addr_string,
shared: &state.shared,
mac_addr_str: [0; 12],
}));
let comm_if = iface.interface_number();
let mut alt = iface.alt_setting(USB_CLASS_CDC, CDC_SUBCLASS_NCM, CDC_PROTOCOL_NONE);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_HEADER, // bDescriptorSubtype
0x10,
0x01, // bcdCDC (1.10)
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_UNION, // bDescriptorSubtype
comm_if.into(), // bControlInterface
u8::from(comm_if) + 1, // bSubordinateInterface
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_ETHERNET, // bDescriptorSubtype
mac_addr_string.into(), // iMACAddress
0, // bmEthernetStatistics
0, // |
0, // |
0, // |
0xea, // wMaxSegmentSize = 1514
0x05, // |
0, // wNumberMCFilters
0, // |
0, // bNumberPowerFilters
],
);
alt.descriptor(
CS_INTERFACE,
&[
CDC_TYPE_NCM, // bDescriptorSubtype
0x00, // bcdNCMVersion
0x01, // |
0, // bmNetworkCapabilities
],
);
let comm_ep = alt.endpoint_interrupt_in(8, 255);
// Data interface
let mut iface = func.interface();
iface.handler(state.data_control.write(DataControl {}));
let data_if = iface.interface_number();
let _alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB);
let mut alt = iface.alt_setting(USB_CLASS_CDC_DATA, 0x00, CDC_PROTOCOL_NTB);
let read_ep = alt.endpoint_bulk_out(max_packet_size);
let write_ep = alt.endpoint_bulk_in(max_packet_size);
CdcNcmClass {
_comm_if: comm_if,
comm_ep,
data_if,
read_ep,
write_ep,
_control: &state.shared,
}
}
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
(
Sender {
write_ep: self.write_ep,
seq: 0,
},
Receiver {
data_if: self.data_if,
comm_ep: self.comm_ep,
read_ep: self.read_ep,
},
)
}
}
pub struct Sender<'d, D: Driver<'d>> {
write_ep: D::EndpointIn,
seq: u16,
}
impl<'d, D: Driver<'d>> Sender<'d, D> {
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
let seq = self.seq;
self.seq = self.seq.wrapping_add(1);
const MAX_PACKET_SIZE: usize = 64; // TODO unhardcode
const OUT_HEADER_LEN: usize = 28;
let header = NtbOutHeader {
nth_sig: SIG_NTH,
nth_len: 0x0c,
nth_seq: seq,
nth_total_len: (data.len() + OUT_HEADER_LEN) as u16,
nth_first_index: 0x0c,
ndp_sig: SIG_NDP_NO_FCS,
ndp_len: 0x10,
ndp_next_index: 0x00,
ndp_datagram_index: OUT_HEADER_LEN as u16,
ndp_datagram_len: data.len() as u16,
ndp_term1: 0x00,
ndp_term2: 0x00,
};
// Build first packet on a buffer, send next packets straight from `data`.
let mut buf = [0; MAX_PACKET_SIZE];
let n = byteify(&mut buf, header);
assert_eq!(n.len(), OUT_HEADER_LEN);
if OUT_HEADER_LEN + data.len() < MAX_PACKET_SIZE {
// First packet is not full, just send it.
// No need to send ZLP because it's short for sure.
buf[OUT_HEADER_LEN..][..data.len()].copy_from_slice(data);
self.write_ep.write(&buf[..OUT_HEADER_LEN + data.len()]).await?;
} else {
let (d1, d2) = data.split_at(MAX_PACKET_SIZE - OUT_HEADER_LEN);
buf[OUT_HEADER_LEN..].copy_from_slice(d1);
self.write_ep.write(&buf).await?;
for chunk in d2.chunks(MAX_PACKET_SIZE) {
self.write_ep.write(&chunk).await?;
}
// Send ZLP if needed.
if d2.len() % MAX_PACKET_SIZE == 0 {
self.write_ep.write(&[]).await?;
}
}
Ok(())
}
}
pub struct Receiver<'d, D: Driver<'d>> {
data_if: InterfaceNumber,
comm_ep: D::EndpointIn,
read_ep: D::EndpointOut,
}
impl<'d, D: Driver<'d>> Receiver<'d, D> {
/// Reads a single packet from the OUT endpoint.
pub async fn read_packet(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> {
// Retry loop
loop {
// read NTB
let mut ntb = [0u8; NTB_MAX_SIZE];
let mut pos = 0;
loop {
let n = self.read_ep.read(&mut ntb[pos..]).await?;
pos += n;
if n < self.read_ep.info().max_packet_size as usize || pos == NTB_MAX_SIZE {
break;
}
}
let ntb = &ntb[..pos];
// Process NTB header (NTH)
let nth = match ntb.get(..12) {
Some(x) => x,
None => {
warn!("Received too short NTB");
continue;
}
};
let sig = u32::from_le_bytes(nth[0..4].try_into().unwrap());
if sig != SIG_NTH {
warn!("Received bad NTH sig.");
continue;
}
let ndp_idx = u16::from_le_bytes(nth[10..12].try_into().unwrap()) as usize;
// Process NTB Datagram Pointer (NDP)
let ndp = match ntb.get(ndp_idx..ndp_idx + 12) {
Some(x) => x,
None => {
warn!("NTH has an NDP pointer out of range.");
continue;
}
};
let sig = u32::from_le_bytes(ndp[0..4].try_into().unwrap());
if sig != SIG_NDP_NO_FCS && sig != SIG_NDP_WITH_FCS {
warn!("Received bad NDP sig.");
continue;
}
let datagram_index = u16::from_le_bytes(ndp[8..10].try_into().unwrap()) as usize;
let datagram_len = u16::from_le_bytes(ndp[10..12].try_into().unwrap()) as usize;
if datagram_index == 0 || datagram_len == 0 {
// empty, ignore. This is allowed by the spec, so don't warn.
continue;
}
// Process actual datagram, finally.
let datagram = match ntb.get(datagram_index..datagram_index + datagram_len) {
Some(x) => x,
None => {
warn!("NDP has a datagram pointer out of range.");
continue;
}
};
buf[..datagram_len].copy_from_slice(datagram);
return Ok(datagram_len);
}
}
/// Waits for the USB host to enable this interface
pub async fn wait_connection(&mut self) -> Result<(), EndpointError> {
loop {
self.read_ep.wait_enabled().await;
self.comm_ep.wait_enabled().await;
let buf = [
0xA1, //bmRequestType
0x00, //bNotificationType = NETWORK_CONNECTION
0x01, // wValue = connected
0x00,
self.data_if.into(), // wIndex = interface
0x00,
0x00, // wLength
0x00,
];
match self.comm_ep.write(&buf).await {
Ok(()) => break, // Done!
Err(EndpointError::Disabled) => {} // Got disabled again, wait again.
Err(e) => return Err(e),
}
}
Ok(())
}
}