#[allow(unused)]
pub mod addr;
#[allow(unused)]
pub mod ctrl_pipe;
#[allow(unused)]
pub mod ext_reg;
#[allow(unused)]
pub mod pck_size;
pub mod register;
#[allow(unused)]
pub mod status_bk;
#[allow(unused)]
pub mod status_pipe;
use addr::Addr;
use ctrl_pipe::CtrlPipe;
use ext_reg::ExtReg;
use pck_size::PckSize;
use status_bk::StatusBk;
use status_pipe::StatusPipe;
use usb_host::{
Endpoint, RequestCode, RequestDirection, RequestType, SetupPacket, TransferType, WValue,
};
use atsamd_hal::target_device::usb::{
self,
host::{BINTERVAL, PCFG, PINTFLAG, PSTATUS, PSTATUSCLR, PSTATUSSET},
};
use core::convert::TryInto;
use log::{trace, warn};
const USB_XFER_TIMEOUT: usize = 50;
const MAX_PIPES: usize = 8;
const NAK_LIMIT: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(unused)]
pub(crate) enum PipeErr {
ShortPacket,
InvalidPipe,
InvalidToken,
Stall,
TransferFail,
PipeErr,
Flow,
HWTimeout,
DataToggle,
SWTimeout,
PID,
DataPID,
CRC16,
}
pub(crate) struct PipeTable {
tbl: [PipeDesc; MAX_PIPES],
}
impl PipeTable {
pub(crate) fn new() -> Self {
let tbl = {
let mut tbl: [core::mem::MaybeUninit<PipeDesc>; MAX_PIPES] =
unsafe { core::mem::MaybeUninit::uninit().assume_init() };
for e in &mut tbl[..] {
unsafe { core::ptr::write(e.as_mut_ptr(), PipeDesc::new()) }
}
unsafe { core::mem::transmute(tbl) }
};
Self { tbl }
}
pub(crate) fn pipe_for<'a, 'b>(
&'a mut self,
host: &'b mut usb::HOST,
endpoint: &dyn Endpoint,
) -> Pipe<'a, 'b> {
let i = if endpoint.endpoint_num() == 0 { 0 } else { 1 };
let pregs = PipeRegs::from(host, i);
let pdesc = &mut self.tbl[i];
pregs.cfg.write(|w| {
let ptype = PType::from(endpoint.transfer_type()) as u8;
unsafe { w.ptype().bits(ptype) }
});
pdesc.bank0.ctrl_pipe.write(|w| {
w.pdaddr().set_addr(endpoint.address());
w.pepnum().set_epnum(endpoint.endpoint_num())
});
pdesc.bank0.pcksize.write(|w| {
let mps = endpoint.max_packet_size();
if mps >= 1023 {
w.size().bytes1024()
} else if mps >= 512 {
w.size().bytes512()
} else if mps >= 256 {
w.size().bytes256()
} else if mps >= 128 {
w.size().bytes128()
} else if mps >= 64 {
w.size().bytes64()
} else if mps >= 32 {
w.size().bytes32()
} else if mps >= 16 {
w.size().bytes16()
} else {
w.size().bytes8()
}
});
Pipe {
num: i,
regs: pregs,
desc: pdesc,
}
}
}
pub(crate) struct Pipe<'a, 'b> {
num: usize,
pub(crate) regs: PipeRegs<'b>,
pub(crate) desc: &'a mut PipeDesc,
}
impl Pipe<'_, '_> {
#[allow(clippy::too_many_arguments)]
pub(crate) fn control_transfer(
&mut self,
ep: &mut dyn Endpoint,
bm_request_type: RequestType,
b_request: RequestCode,
w_value: WValue,
w_index: u16,
buf: Option<&mut [u8]>,
millis: &dyn Fn() -> usize,
) -> Result<usize, PipeErr> {
let buflen = buf.as_ref().map_or(0, |b| b.len() as u16);
let mut setup_packet = SetupPacket {
bm_request_type,
b_request,
w_value,
w_index,
w_length: buflen,
};
self.send(
ep,
PToken::Setup,
&DataBuf::from(&mut setup_packet),
NAK_LIMIT,
millis,
)?;
let mut transfer_len = 0;
if let Some(b) = buf {
match bm_request_type.direction() {
Ok(RequestDirection::DeviceToHost) => {
transfer_len = self.in_transfer(ep, b, NAK_LIMIT, millis)?;
}
Ok(RequestDirection::HostToDevice) => {
transfer_len = self.out_transfer(ep, b, NAK_LIMIT, millis)?;
}
Err(_) => Err(PipeErr::PID)?,
}
}
self.desc.bank0.pcksize.modify(|_, w| {
unsafe { w.byte_count().bits(0) };
unsafe { w.multi_packet_size().bits(0) }
});
let token = match bm_request_type.direction() {
Ok(RequestDirection::DeviceToHost) => PToken::Out,
Ok(RequestDirection::HostToDevice) => PToken::In,
Err(_) => Err(PipeErr::PID)?,
};
trace!("dispatching status stage");
self.dispatch_retries(ep, token, NAK_LIMIT, millis)?;
Ok(transfer_len)
}
fn send(
&mut self,
ep: &mut dyn Endpoint,
token: PToken,
buf: &DataBuf,
nak_limit: usize,
millis: &dyn Fn() -> usize,
) -> Result<(), PipeErr> {
trace!("p{}: sending {:?}", self.num, buf);
self.desc
.bank0
.addr
.write(|w| unsafe { w.addr().bits(buf.ptr as u32) });
self.desc.bank0.pcksize.modify(|_, w| {
unsafe { w.byte_count().bits(buf.len as u16) };
unsafe { w.multi_packet_size().bits(0) }
});
self.dispatch_retries(ep, token, nak_limit, millis)
}
pub(crate) fn in_transfer(
&mut self,
ep: &mut dyn Endpoint,
buf: &mut [u8],
nak_limit: usize,
millis: &dyn Fn() -> usize,
) -> Result<usize, PipeErr> {
let packet_size = ep.max_packet_size() as isize;
let buflen = buf.len() as isize;
trace!("p{}: Should IN for {}b.", self.num, buflen);
self.desc.bank0.pcksize.modify(|_, w| {
unsafe { w.byte_count().bits(buflen as u16) };
unsafe { w.multi_packet_size().bits(0) }
});
let ptr: *mut u8 = buf.as_mut_ptr();
let mut bytes_received = 0isize;
loop {
self.desc
.bank0
.addr
.write(|w| unsafe { w.addr().bits(ptr.offset(bytes_received) as u32) });
self.dispatch_retries(ep, PToken::In, nak_limit, millis)?;
let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as isize;
bytes_received += recvd;
trace!("p{}: read {} of {}", self.num, bytes_received, buflen);
assert!(bytes_received <= buflen);
if bytes_received == buflen {
break;
} else if recvd < packet_size {
warn!("p{}: short packet {}/{}.", self.num, recvd, packet_size);
break;
}
}
self.regs.statusset.write(|w| w.pfreeze().set_bit());
if bytes_received < buflen {
self.log_regs();
Err(PipeErr::ShortPacket)
} else {
Ok(bytes_received as usize)
}
}
pub(crate) fn out_transfer(
&mut self,
ep: &mut dyn Endpoint,
buf: &[u8],
nak_limit: usize,
millis: &dyn Fn() -> usize,
) -> Result<usize, PipeErr> {
let buflen = buf.len();
trace!("p{}: Should OUT for {}b.", self.num, buflen);
self.desc.bank0.pcksize.modify(|_, w| {
unsafe { w.byte_count().bits(buflen as u16) };
unsafe { w.multi_packet_size().bits(0) }
});
let mut bytes_sent = 0;
while bytes_sent < buflen {
self.desc
.bank0
.addr
.write(|w| unsafe { w.addr().bits(buf.as_ptr() as u32 + bytes_sent as u32) });
self.dispatch_retries(ep, PToken::Out, nak_limit, millis)?;
let sent = self.desc.bank0.pcksize.read().byte_count().bits() as usize;
bytes_sent += sent;
trace!("!! wrote {} of {}", bytes_sent, buflen);
}
Ok(bytes_sent)
}
fn dispatch_retries(
&mut self,
ep: &mut dyn Endpoint,
token: PToken,
retries: usize,
millis: &dyn Fn() -> usize,
) -> Result<(), PipeErr> {
self.dispatch_packet(ep, token);
let mut until = millis() + USB_XFER_TIMEOUT;
let mut last_err = PipeErr::SWTimeout;
let mut attempt = 0;
while attempt <= retries && millis() < until {
match self.dispatch_result(token) {
Ok(true) => {
if token == PToken::In {
ep.set_in_toggle(!ep.in_toggle());
} else if token == PToken::Out {
ep.set_out_toggle(!ep.out_toggle());
}
trace!("o");
self.regs.statusset.write(|w| w.pfreeze().set_bit());
return Ok(());
}
Ok(false) => trace!("c"),
Err(e) => {
attempt += 1;
last_err = e;
match last_err {
PipeErr::DataToggle => {
self.dtgl(ep, token);
until = millis() + USB_XFER_TIMEOUT;
self.regs.statusclr.write(|w| w.pfreeze().set_bit());
trace!("d");
}
PipeErr::Flow if ep.transfer_type() == TransferType::Interrupt => {
trace!("f");
break;
}
PipeErr::Stall => break,
_ => trace!("n: {:?} {}/{}", e, attempt, retries),
}
}
}
}
self.regs.statusset.write(|w| w.pfreeze().set_bit());
Err(last_err)
}
fn dispatch_packet(&mut self, ep: &mut dyn Endpoint, token: PToken) {
self.regs
.cfg
.modify(|_, w| unsafe { w.ptoken().bits(token as u8) });
self.desc
.bank0
.status_pipe
.write(|w| w.ercnt().set_count(0));
match token {
PToken::Setup => {
self.dtgl_clear();
ep.set_in_toggle(true);
ep.set_out_toggle(true);
self.regs.intflag.write(|w| w.txstp().set_bit());
self.regs.statusset.write(|w| w.bk0rdy().set_bit());
}
PToken::In => {
if ep.in_toggle() {
self.dtgl_set();
} else {
self.dtgl_clear();
}
self.regs.intflag.write(|w| w.trcpt0().set_bit());
self.regs.statusclr.write(|w| w.bk0rdy().set_bit())
}
PToken::Out => {
if ep.out_toggle() {
self.dtgl_set();
} else {
self.dtgl_clear();
}
self.regs.intflag.write(|w| w.trcpt0().set_bit());
self.regs.statusset.write(|w| w.bk0rdy().set_bit())
}
_ => panic!("bad token during dispatch"),
}
self.regs.statusclr.write(|w| w.pfreeze().set_bit());
}
fn dispatch_result(&mut self, _token: PToken) -> Result<bool, PipeErr> {
let flags = self.regs.intflag.read();
if flags.txstp().bit_is_set() {
self.regs.intflag.write(|w| w.txstp().set_bit());
Ok(true)
} else if flags.trcpt0().bit_is_set() {
self.regs.intflag.write(|w| w.trcpt0().set_bit());
Ok(true)
} else if flags.stall().bit_is_set() {
self.regs.intflag.write(|w| w.stall().set_bit());
trace!("stall");
self.log_regs();
Err(PipeErr::Stall)
} else if flags.trfail().bit_is_set() || flags.perr().bit_is_set() {
self.regs.intflag.write(|w| {
w.trfail().set_bit();
w.perr().set_bit()
});
if self.desc.bank0.status_pipe.read().dtgler().bit_is_set() {
self.desc
.bank0
.status_pipe
.write(|w| w.dtgler().clear_bit());
trace!("dtgler");
self.log_regs();
Err(PipeErr::DataToggle)
} else if self.desc.bank0.status_bk.read().errorflow().bit_is_set() {
self.desc
.bank0
.status_bk
.write(|w| w.errorflow().clear_bit());
trace!("errorflow");
self.log_regs();
Err(PipeErr::Flow)
} else if self.desc.bank0.status_pipe.read().touter().bit_is_set() {
self.desc
.bank0
.status_pipe
.write(|w| w.touter().clear_bit());
trace!("touter");
self.log_regs();
Err(PipeErr::HWTimeout)
} else if self.desc.bank0.status_pipe.read().crc16er().bit_is_set() {
self.desc
.bank0
.status_pipe
.write(|w| w.crc16er().clear_bit());
trace!("crc16er");
self.log_regs();
Err(PipeErr::CRC16)
} else if self.desc.bank0.status_pipe.read().pider().bit_is_set() {
self.desc.bank0.status_pipe.write(|w| w.pider().clear_bit());
trace!("pider");
self.log_regs();
Err(PipeErr::PID)
} else if self.desc.bank0.status_pipe.read().dapider().bit_is_set() {
self.desc
.bank0
.status_pipe
.write(|w| w.dapider().clear_bit());
trace!("dapider");
self.log_regs();
Err(PipeErr::DataPID)
} else {
Err(PipeErr::TransferFail)
}
} else {
Ok(false)
}
}
fn dtgl(&mut self, ep: &mut dyn Endpoint, token: PToken) {
warn!(
"tok: {:?}, dtgl: {}, i: {}, o: {}",
token,
self.regs.status.read().dtgl().bit(),
ep.in_toggle(),
ep.out_toggle(),
);
let toggle = self.regs.status.read().dtgl().bit_is_set();
if token == PToken::In {
ep.set_in_toggle(toggle);
} else if token == PToken::Out {
ep.set_out_toggle(toggle);
}
if toggle {
self.dtgl_set();
} else {
self.dtgl_clear();
}
}
fn dtgl_set(&mut self) {
self.regs.statusset.write(|w| w.dtgl().set_bit());
}
fn dtgl_clear(&mut self) {
self.regs.statusclr.write(|w| unsafe {
w.bits(1)
});
}
fn log_regs(&self) {
let cfg = self.regs.cfg.read().bits();
let bin = self.regs.binterval.read().bits();
let sts = self.regs.status.read().bits();
let ifl = self.regs.intflag.read().bits();
let adr = self.desc.bank0.addr.read().bits();
let pks = self.desc.bank0.pcksize.read().bits();
let ext = self.desc.bank0.extreg.read().bits();
let sbk = self.desc.bank0.status_bk.read().bits();
let hcp = self.desc.bank0.ctrl_pipe.read().bits();
let spi = self.desc.bank0.status_pipe.read().bits();
trace!(
"p{}:\n\tcfg: {:x}, bin: {:x}, sts: {:x}, ifl: {:x}\n\tadr: {:x}, pks: {:x}, ext: {:x}, sbk: {:x}, hcp: {:x}, spi: {:x}",
self.num,
cfg,
bin,
sts,
ifl,
adr,
pks,
ext,
sbk,
hcp,
spi
);
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum PToken {
Setup = 0x0,
In = 0x1,
Out = 0x2,
_Reserved = 0x3,
}
#[allow(unused)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) enum PType {
Disabled = 0x0,
Control = 0x1,
ISO = 0x2,
Bulk = 0x3,
Interrupt = 0x4,
Extended = 0x5,
_Reserved0 = 0x06,
_Reserved1 = 0x07,
}
impl From<TransferType> for PType {
fn from(v: TransferType) -> Self {
match v {
TransferType::Control => Self::Control,
TransferType::Isochronous => Self::ISO,
TransferType::Bulk => Self::Bulk,
TransferType::Interrupt => Self::Interrupt,
}
}
}
struct DataBuf<'a> {
ptr: *mut u8,
len: usize,
_marker: core::marker::PhantomData<&'a ()>,
}
impl DataBuf<'_> {}
impl core::fmt::Debug for DataBuf<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "DataBuf {{ len: {}, ptr: [", self.len)?;
for i in 0..self.len {
write!(f, " {:x}", unsafe {
*self.ptr.offset(i.try_into().unwrap())
})?;
}
write!(f, " ] }}")
}
}
impl<'a, T> From<&'a mut T> for DataBuf<'a> {
fn from(v: &'a mut T) -> Self {
Self {
ptr: v as *mut T as *mut u8,
len: core::mem::size_of::<T>(),
_marker: core::marker::PhantomData,
}
}
}
pub(crate) struct PipeRegs<'a> {
pub(crate) cfg: &'a mut PCFG,
pub(crate) binterval: &'a mut BINTERVAL,
pub(crate) statusclr: &'a mut PSTATUSCLR,
pub(crate) statusset: &'a mut PSTATUSSET,
pub(crate) status: &'a mut PSTATUS,
pub(crate) intflag: &'a mut PINTFLAG,
}
impl<'a> PipeRegs<'a> {
pub(crate) fn from(host: &'a mut usb::HOST, i: usize) -> PipeRegs {
assert!(i < MAX_PIPES);
match i {
0 => Self {
cfg: &mut host.pcfg0,
binterval: &mut host.binterval0,
statusclr: &mut host.pstatusclr0,
statusset: &mut host.pstatusset0,
status: &mut host.pstatus0,
intflag: &mut host.pintflag0,
},
1 => Self {
cfg: &mut host.pcfg1,
binterval: &mut host.binterval1,
statusclr: &mut host.pstatusclr1,
statusset: &mut host.pstatusset1,
status: &mut host.pstatus1,
intflag: &mut host.pintflag1,
},
2 => Self {
cfg: &mut host.pcfg2,
binterval: &mut host.binterval2,
statusclr: &mut host.pstatusclr2,
statusset: &mut host.pstatusset2,
status: &mut host.pstatus2,
intflag: &mut host.pintflag2,
},
3 => Self {
cfg: &mut host.pcfg3,
binterval: &mut host.binterval3,
statusclr: &mut host.pstatusclr3,
statusset: &mut host.pstatusset3,
status: &mut host.pstatus3,
intflag: &mut host.pintflag3,
},
4 => Self {
cfg: &mut host.pcfg4,
binterval: &mut host.binterval4,
statusclr: &mut host.pstatusclr4,
statusset: &mut host.pstatusset4,
status: &mut host.pstatus4,
intflag: &mut host.pintflag4,
},
5 => Self {
cfg: &mut host.pcfg5,
binterval: &mut host.binterval5,
statusclr: &mut host.pstatusclr5,
statusset: &mut host.pstatusset5,
status: &mut host.pstatus5,
intflag: &mut host.pintflag5,
},
6 => Self {
cfg: &mut host.pcfg6,
binterval: &mut host.binterval6,
statusclr: &mut host.pstatusclr6,
statusset: &mut host.pstatusset6,
status: &mut host.pstatus6,
intflag: &mut host.pintflag6,
},
7 => Self {
cfg: &mut host.pcfg7,
binterval: &mut host.binterval7,
statusclr: &mut host.pstatusclr7,
statusset: &mut host.pstatusset7,
status: &mut host.pstatus7,
intflag: &mut host.pintflag7,
},
_ => unreachable!(),
}
}
}
pub(crate) struct PipeDesc {
pub bank0: BankDesc,
pub bank1: BankDesc,
}
impl PipeDesc {
pub fn new() -> Self {
Self {
bank0: BankDesc::new(),
bank1: BankDesc::new(),
}
}
}
#[repr(C)]
pub(crate) struct BankDesc {
pub addr: Addr,
pub pcksize: PckSize,
pub extreg: ExtReg,
pub status_bk: StatusBk,
_reserved0: u8,
pub ctrl_pipe: CtrlPipe,
pub status_pipe: StatusPipe,
_reserved1: u8,
}
impl BankDesc {
fn new() -> Self {
Self {
addr: Addr::from(0),
pcksize: PckSize::from(0),
extreg: ExtReg::from(0),
status_bk: StatusBk::from(0),
_reserved0: 0,
ctrl_pipe: CtrlPipe::from(0),
status_pipe: StatusPipe::from(0),
_reserved1: 0,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bank_desc_sizes() {
assert_eq!(core::mem::size_of::<Addr>(), 4, "Addr register size.");
assert_eq!(core::mem::size_of::<PckSize>(), 4, "PckSize register size.");
assert_eq!(core::mem::size_of::<ExtReg>(), 2, "ExtReg register size.");
assert_eq!(
core::mem::size_of::<StatusBk>(),
1,
"StatusBk register size."
);
assert_eq!(
core::mem::size_of::<CtrlPipe>(),
2,
"CtrlPipe register size."
);
assert_eq!(
core::mem::size_of::<StatusPipe>(),
1,
"StatusPipe register size."
);
assert_eq!(
core::mem::size_of::<BankDesc>(),
16,
"Bank descriptor size."
);
}
#[test]
fn bank_desc_offsets() {
let bd = BankDesc::new();
let base = &bd as *const _ as usize;
assert_offset("Addr", &bd.addr, base, 0x00);
assert_offset("PckSize", &bd.pcksize, base, 0x04);
assert_offset("ExtReg", &bd.extreg, base, 0x08);
assert_offset("StatusBk", &bd.status_bk, base, 0x0a);
assert_offset("CtrlPipe", &bd.ctrl_pipe, base, 0x0c);
assert_offset("StatusPipe", &bd.status_pipe, base, 0x0e);
}
#[test]
fn pipe_desc_size() {
assert_eq!(core::mem::size_of::<PipeDesc>(), 32);
}
#[test]
fn pipe_desc_offsets() {
let pd = PipeDesc::new();
let base = &pd as *const _ as usize;
assert_offset("Bank0", &pd.bank0, base, 0x00);
assert_offset("Bank1", &pd.bank1, base, 0x10);
}
fn assert_offset<T>(name: &str, field: &T, base: usize, offset: usize) {
let ptr = field as *const _ as usize;
assert_eq!(ptr - base, offset, "{} register offset.", name);
}
}