use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::usb::error::{UsbError, UsbResult};
use crate::usb::host::dwc2::regs::{Dwc2Regs, DCFG, DIEPCTL, DOEPCTL, DSTS};
use crate::usb;
use crate::utils::cache;
use super::desc::{
DT_CONFIG, DT_DEVICE, DT_DEVICE_QUALIFIER, DT_STRING, REQ_GET_CONFIGURATION,
REQ_GET_DESCRIPTOR, REQ_GET_INTERFACE, REQ_GET_STATUS, REQ_SET_ADDRESS,
REQ_SET_CONFIGURATION, REQ_SET_INTERFACE, REQ_TYPE_STANDARD, REQ_RCPT_DEVICE,
REQ_RCPT_INTERFACE, REQ_RCPT_ENDPOINT, REQ_CLEAR_FEATURE, REQ_SET_FEATURE,
};
use super::{Ep0Context, UsbDeviceClass};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum UsbSpeed {
HighSpeed,
FullSpeed,
LowSpeed,
}
#[derive(Clone, Copy, Default, Debug)]
pub struct Setup {
pub bm_request_type: u8,
pub b_request: u8,
pub w_value: u16,
pub w_index: u16,
pub w_length: u16,
}
impl Setup {
fn parse(buf: &[u8]) -> Self {
Self {
bm_request_type: buf[0],
b_request: buf[1],
w_value: u16::from_le_bytes([buf[2], buf[3]]),
w_index: u16::from_le_bytes([buf[4], buf[5]]),
w_length: u16::from_le_bytes([buf[6], buf[7]]),
}
}
#[inline]
pub fn dir_in(&self) -> bool {
self.bm_request_type & 0x80 != 0
}
#[inline]
pub fn req_type(&self) -> u8 {
self.bm_request_type & 0x60
}
#[inline]
pub fn recipient(&self) -> u8 {
self.bm_request_type & 0x1f
}
}
pub enum Ep0Reply {
Stall,
StatusOnly,
Data(usize),
AcceptOut,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Ep0State {
WaitSetup,
InData,
OutData,
StatusIn,
StatusOut,
}
const EP0_SETUP_BUF_LEN: usize = 24;
const EP0_IN_BUF_LEN: usize = 256;
const EP0_OUT_BUF_LEN: usize = 256;
#[repr(C, align(64))]
struct Ep0Bufs {
setup: [u8; EP0_SETUP_BUF_LEN],
in_data: [u8; EP0_IN_BUF_LEN],
out_data: [u8; EP0_OUT_BUF_LEN],
}
static mut EP0_BUFS: Ep0Bufs = Ep0Bufs {
setup: [0; EP0_SETUP_BUF_LEN],
in_data: [0; EP0_IN_BUF_LEN],
out_data: [0; EP0_OUT_BUF_LEN],
};
#[inline]
fn ep0_setup_ptr() -> *mut u8 {
unsafe { core::ptr::addr_of_mut!(EP0_BUFS.setup) as *mut u8 }
}
#[inline]
fn ep0_in_ptr() -> *mut u8 {
unsafe { core::ptr::addr_of_mut!(EP0_BUFS.in_data) as *mut u8 }
}
#[inline]
fn ep0_out_ptr() -> *mut u8 {
unsafe { core::ptr::addr_of_mut!(EP0_BUFS.out_data) as *mut u8 }
}
#[inline]
fn dma_phys(p: *const u8) -> u32 {
usb::usb_dma_phys_for(p)
}
#[inline]
fn regs() -> &'static Dwc2Regs {
usb::dwc2_regs().expect("DWC2 base not set")
}
pub struct Ep0Service {
state: Ep0State,
speed: UsbSpeed,
enumerated: bool,
pending_setup: Setup,
in_remaining: usize,
in_offset: usize,
out_total: usize,
out_received: usize,
current_config: u8,
pending_address: Option<u8>,
}
impl Ep0Service {
pub const fn new() -> Self {
Self {
state: Ep0State::WaitSetup,
speed: UsbSpeed::HighSpeed,
enumerated: false,
pending_setup: Setup {
bm_request_type: 0,
b_request: 0,
w_value: 0,
w_index: 0,
w_length: 0,
},
in_remaining: 0,
in_offset: 0,
out_total: 0,
out_received: 0,
current_config: 0,
pending_address: None,
}
}
pub fn service<C: UsbDeviceClass>(&mut self, class: &mut C) {
if usb::dwc2_base_virt() == 0 {
return;
}
let r = regs();
let gint = r.gintsts.get();
if gint & (1 << 12) != 0 {
r.gintsts.set(1 << 12);
self.handle_usbrst();
}
if gint & (1 << 13) != 0 {
r.gintsts.set(1 << 13);
self.handle_enumdone();
}
self.handle_ep0_out(class);
self.handle_ep0_in(class);
let ctx = Ep0Context { speed: self.speed };
class.poll(&ctx);
}
fn handle_usbrst(&mut self) {
log::info!("USB-DEV USBRST");
let r = regs();
r.dcfg.modify(DCFG::DEVADDR.val(0));
for ep in 0..crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
if r.diep[ep].diepctl.is_set(DIEPCTL::EPENA) {
r.diep[ep]
.diepctl
.modify(DIEPCTL::SNAK::SET + DIEPCTL::EPDIS::SET);
}
if r.doep[ep].doepctl.is_set(DOEPCTL::EPENA) {
r.doep[ep]
.doepctl
.modify(DOEPCTL::SNAK::SET + DOEPCTL::EPDIS::SET);
}
}
self.state = Ep0State::WaitSetup;
self.in_remaining = 0;
self.in_offset = 0;
self.out_total = 0;
self.out_received = 0;
self.current_config = 0;
self.pending_address = None;
self.enumerated = false;
}
fn handle_enumdone(&mut self) {
let r = regs();
let speed = match r.dsts.read(DSTS::ENUMSPD) {
0 => UsbSpeed::HighSpeed,
1 | 3 => UsbSpeed::FullSpeed,
2 => UsbSpeed::LowSpeed,
_ => UsbSpeed::FullSpeed,
};
self.speed = speed;
self.enumerated = true;
log::info!("USB-DEV ENUMDONE speed={:?} DSTS={:#010x}",
speed,
r.dsts.get());
let mps_field: u32 = if speed == UsbSpeed::LowSpeed { 3 } else { 0 };
r.diep[0].diepctl.modify(DIEPCTL::MPS.val(mps_field));
r.doep[0].doepctl.modify(DOEPCTL::MPS.val(mps_field));
self.prime_ep0_setup();
}
fn prime_ep0_setup(&self) {
let r = regs();
unsafe {
cache::dcache_invalidate_after_dma(ep0_setup_ptr(), 24);
}
let pa = dma_phys(ep0_setup_ptr());
r.doep[0].doepdma.set(pa);
let val: u32 = (1 << 29) | (1 << 19) | 8;
r.doep[0].doeptsiz.set(val);
r.doep[0]
.doepctl
.modify(DOEPCTL::EPENA::SET + DOEPCTL::CNAK::SET);
}
fn prime_ep0_out_data(&self, expected_remaining: usize) {
let r = regs();
let mps = self.ep0_mps();
let xfer = expected_remaining.min(mps).max(1);
unsafe {
cache::dcache_invalidate_after_dma(ep0_out_ptr(), mps);
}
let pa = dma_phys(ep0_out_ptr());
r.doep[0].doepdma.set(pa);
let val: u32 = (1 << 19) | ((xfer as u32) & 0x7f);
r.doep[0].doeptsiz.set(val);
let cur = r.doep[0].doepctl.get();
let new_ctrl = (cur & !(1u32 << 30)) | (1u32 << 31) | (1u32 << 26);
r.doep[0].doepctl.set(new_ctrl);
r.doep[0].doepint.set(0xffff_ffff);
let _ = xfer;
}
fn prime_ep0_status_out(&self) {
let r = regs();
let pa = dma_phys(ep0_out_ptr());
r.doep[0].doepdma.set(pa);
let val: u32 = (1 << 19) | 0;
r.doep[0].doeptsiz.set(val);
r.doep[0]
.doepctl
.modify(DOEPCTL::EPENA::SET + DOEPCTL::CNAK::SET);
}
fn prime_ep0_in_data(&self, len: usize) {
let r = regs();
let mps = self.ep0_mps();
let pkt_cnt = if len == 0 { 1 } else { (len + mps - 1) / mps };
let pkt_cnt = pkt_cnt.min(3) as u32;
unsafe {
cache::dcache_clean_for_dma(ep0_in_ptr(), len.max(1));
}
let pa = dma_phys(ep0_in_ptr());
r.diep[0].diepdma.set(pa);
let val: u32 = (pkt_cnt << 19) | ((len as u32) & 0x7f);
r.diep[0].dieptsiz.set(val);
r.diep[0]
.diepctl
.modify(DIEPCTL::EPENA::SET + DIEPCTL::CNAK::SET);
}
fn prime_ep0_status_in(&self) {
let r = regs();
let pa = dma_phys(ep0_in_ptr());
r.diep[0].diepdma.set(pa);
let val: u32 = (1 << 19) | 0;
r.diep[0].dieptsiz.set(val);
r.diep[0]
.diepctl
.modify(DIEPCTL::EPENA::SET + DIEPCTL::CNAK::SET);
}
fn stall_ep0(&mut self) {
let r = regs();
r.diep[0].diepctl.modify(DIEPCTL::STALL::SET);
r.doep[0].doepctl.modify(DOEPCTL::STALL::SET);
self.state = Ep0State::WaitSetup;
self.prime_ep0_setup();
}
#[inline]
fn ep0_mps(&self) -> usize {
if self.speed == UsbSpeed::LowSpeed { 8 } else { 64 }
}
fn handle_ep0_out<C: UsbDeviceClass>(&mut self, class: &mut C) {
let r = regs();
let int = r.doep[0].doepint.get();
if int == 0 {
return;
}
if int & (1 << 3) != 0 {
r.doep[0].doepint.set(0xffff_ffff);
unsafe {
cache::dcache_invalidate_after_dma(ep0_setup_ptr(), 24);
}
let mut s = [0u8; 8];
unsafe {
core::ptr::copy_nonoverlapping(ep0_setup_ptr(), s.as_mut_ptr(), 8);
}
self.pending_setup = Setup::parse(&s);
self.dispatch_setup(class);
return;
}
if int & (1 << 5) != 0 {
r.doep[0].doepint.set(1 << 5);
}
if int & (1 << 0) != 0 {
r.doep[0].doepint.set(1 << 0);
match self.state {
Ep0State::OutData => {
let mps = self.ep0_mps();
let expected = self
.out_total
.saturating_sub(self.out_received)
.min(mps)
.max(1);
let tsiz = r.doep[0].doeptsiz.get();
let residual = (tsiz & 0x7f) as usize;
let actual = expected.saturating_sub(residual);
if actual > 0 {
unsafe {
cache::dcache_invalidate_after_dma(ep0_out_ptr(), actual);
}
}
self.out_received += actual;
let len = actual.min(self.out_total);
let s = self.pending_setup;
let buf = unsafe { core::slice::from_raw_parts(ep0_out_ptr(), len) };
class.class_out_data(&s, buf);
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
let _ = tsiz;
}
Ep0State::StatusOut => {
self.state = Ep0State::WaitSetup;
self.prime_ep0_setup();
}
_ => {
self.state = Ep0State::WaitSetup;
self.prime_ep0_setup();
}
}
}
}
fn handle_ep0_in<C: UsbDeviceClass>(&mut self, _class: &mut C) {
let r = regs();
let int = r.diep[0].diepint.get();
if int == 0 {
return;
}
if int & (1 << 0) != 0 {
r.diep[0].diepint.set(1 << 0);
match self.state {
Ep0State::InData => {
let mps = self.ep0_mps();
let chunk = self.in_remaining.min(mps);
self.in_remaining -= chunk;
self.in_offset += chunk;
if self.in_remaining == 0 {
self.state = Ep0State::StatusOut;
self.prime_ep0_status_out();
} else {
unsafe {
core::ptr::copy(
ep0_in_ptr().add(chunk),
ep0_in_ptr(),
self.in_remaining,
);
}
self.in_offset = 0;
self.prime_ep0_in_data(self.in_remaining);
}
}
Ep0State::StatusIn => {
if let Some(addr) = self.pending_address.take() {
log::info!("USB-DEV SET_ADDRESS({}) status complete",
addr);
}
self.state = Ep0State::WaitSetup;
self.prime_ep0_setup();
}
_ => {
self.state = Ep0State::WaitSetup;
self.prime_ep0_setup();
}
}
}
if int & !(1 << 0) != 0 {
r.diep[0].diepint.set(int & !(1 << 0));
}
}
fn dispatch_setup<C: UsbDeviceClass>(&mut self, class: &mut C) {
let s = self.pending_setup;
if s.req_type() == REQ_TYPE_STANDARD {
self.handle_standard(class);
return;
}
let in_buf = unsafe { core::slice::from_raw_parts_mut(ep0_in_ptr(), EP0_IN_BUF_LEN) };
let reply = class.class_setup(&s, in_buf);
match reply {
Ep0Reply::Stall => self.stall_ep0(),
Ep0Reply::StatusOnly => {
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
}
Ep0Reply::Data(len) => {
let len = len.min(s.w_length as usize).min(in_buf.len());
self.start_in_data(len);
}
Ep0Reply::AcceptOut => {
let len = (s.w_length as usize).min(EP0_OUT_BUF_LEN);
self.out_total = len;
self.out_received = 0;
if len == 0 {
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
} else {
self.state = Ep0State::OutData;
self.prime_ep0_out_data(len);
}
}
}
}
fn handle_standard<C: UsbDeviceClass>(&mut self, class: &mut C) {
let s = self.pending_setup;
match s.b_request {
REQ_GET_DESCRIPTOR => {
let desc_type = (s.w_value >> 8) as u8;
let desc_idx = (s.w_value & 0xff) as u8;
let bytes: Option<&[u8]> = match desc_type {
DT_DEVICE => Some(class.device_descriptor()),
DT_CONFIG => {
if desc_idx == 0 {
Some(class.config_descriptor())
} else {
None
}
}
DT_STRING => class.string_descriptor(desc_idx),
DT_DEVICE_QUALIFIER => None, _ => None,
};
match bytes {
Some(src) => {
let max = (s.w_length as usize).min(src.len()).min(EP0_IN_BUF_LEN);
unsafe {
core::ptr::copy_nonoverlapping(
src.as_ptr(),
ep0_in_ptr(),
max,
);
}
self.start_in_data(max);
}
None => self.stall_ep0(),
}
}
REQ_SET_ADDRESS => {
let addr = (s.w_value & 0x7f) as u8;
regs().dcfg.modify(DCFG::DEVADDR.val(addr as u32));
self.pending_address = Some(addr);
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
}
REQ_SET_CONFIGURATION => {
let cfg = (s.w_value & 0xff) as u8;
self.current_config = cfg;
let ctx = Ep0Context { speed: self.speed };
class.on_configured(cfg, &ctx);
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
}
REQ_GET_CONFIGURATION => {
unsafe {
*ep0_in_ptr() = self.current_config;
}
self.start_in_data(1);
}
REQ_SET_INTERFACE => {
let alt = (s.w_value & 0xff) as u8;
let iface = (s.w_index & 0xff) as u8;
class.on_set_interface(iface, alt);
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
}
REQ_GET_INTERFACE => {
unsafe {
*ep0_in_ptr() = 0;
}
self.start_in_data(1);
}
REQ_GET_STATUS => {
unsafe {
*ep0_in_ptr() = 0;
*ep0_in_ptr().add(1) = 0;
}
self.start_in_data(2);
}
REQ_CLEAR_FEATURE | REQ_SET_FEATURE => {
let _ = (REQ_RCPT_DEVICE, REQ_RCPT_INTERFACE, REQ_RCPT_ENDPOINT);
self.state = Ep0State::StatusIn;
self.prime_ep0_status_in();
}
_ => {
log::info!("USB-DEV unsupported standard request {:#04x}, STALL",
s.b_request);
self.stall_ep0();
}
}
}
fn start_in_data(&mut self, len: usize) {
let mps = self.ep0_mps();
if len == 0 {
self.state = Ep0State::StatusOut;
self.prime_ep0_status_in();
return;
}
self.in_remaining = len;
self.in_offset = 0;
self.state = Ep0State::InData;
let chunk = len.min(mps * 3).min(127);
let _ = chunk;
self.prime_ep0_in_data(len.min(127).min(mps * 3));
}
}
impl Default for Ep0Service {
fn default() -> Self {
Self::new()
}
}
pub fn configure_bulk_in_ep(ep_num: u8, mps: u32, tx_fifo_num: u32) -> UsbResult<()> {
let ep = ep_num as usize;
if ep == 0 || ep >= crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
return Err(UsbError::Protocol("invalid bulk IN ep number"));
}
let r = regs();
r.diep[ep].diepctl.set(0);
r.diep[ep].diepctl.modify(
DIEPCTL::MPS.val(mps & 0x7ff)
+ DIEPCTL::EPTYPE::Bulk
+ DIEPCTL::USBACTEP::SET
+ DIEPCTL::TXFNUM.val(tx_fifo_num & 0xf)
+ DIEPCTL::SETD0PID::SET,
);
let prev = r.daintmsk.get();
r.daintmsk.set(prev | (1u32 << ep));
Ok(())
}
pub fn configure_bulk_out_ep(ep_num: u8, mps: u32) -> UsbResult<()> {
let ep = ep_num as usize;
if ep == 0 || ep >= crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
return Err(UsbError::Protocol("invalid bulk OUT ep number"));
}
let r = regs();
r.doep[ep].doepctl.set(0);
r.doep[ep].doepctl.modify(
DOEPCTL::MPS.val(mps & 0x7ff)
+ DOEPCTL::EPTYPE::Bulk
+ DOEPCTL::USBACTEP::SET
+ DOEPCTL::SETD0PID::SET,
);
let prev = r.daintmsk.get();
r.daintmsk.set(prev | (1u32 << (ep + 16)));
Ok(())
}
pub fn configure_intr_in_ep(ep_num: u8, mps: u32, tx_fifo_num: u32) -> UsbResult<()> {
let ep = ep_num as usize;
if ep == 0 || ep >= crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
return Err(UsbError::Protocol("invalid intr IN ep number"));
}
let r = regs();
r.diep[ep].diepctl.set(0);
r.diep[ep].diepctl.modify(
DIEPCTL::MPS.val(mps & 0x7ff)
+ DIEPCTL::EPTYPE::Interrupt
+ DIEPCTL::USBACTEP::SET
+ DIEPCTL::TXFNUM.val(tx_fifo_num & 0xf)
+ DIEPCTL::SETD0PID::SET,
);
let prev = r.daintmsk.get();
r.daintmsk.set(prev | (1u32 << ep));
Ok(())
}
pub fn prime_bulk_out(ep_num: u8, buf_pa: u32, len: u32, num_packets: u32) -> UsbResult<()> {
let ep = ep_num as usize;
if ep == 0 || ep >= crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
return Err(UsbError::Protocol("invalid bulk OUT ep number"));
}
let r = regs();
r.doep[ep].doepdma.set(buf_pa);
let val: u32 = (num_packets << 19) | (len & 0x7ffff);
r.doep[ep].doeptsiz.set(val);
let cur = r.doep[ep].doepctl.get();
let new_ctl = (cur & !(1u32 << 30)) | (1u32 << 31) | (1u32 << 26);
r.doep[ep].doepctl.set(new_ctl);
Ok(())
}
pub fn start_bulk_in(ep_num: u8, buf_pa: u32, len: u32, num_packets: u32) -> UsbResult<()> {
let ep = ep_num as usize;
if ep == 0 || ep >= crate::usb::host::dwc2::regs::DWC2_MAX_DEV_ENDPOINTS {
return Err(UsbError::Protocol("invalid bulk IN ep number"));
}
let r = regs();
r.diep[ep].diepdma.set(buf_pa);
let val: u32 = (num_packets << 19) | (len & 0x7ffff);
r.diep[ep].dieptsiz.set(val);
let cur = r.diep[ep].diepctl.get();
let new_ctl = (cur & !(1u32 << 30)) | (1u32 << 31) | (1u32 << 26);
r.diep[ep].diepctl.set(new_ctl);
Ok(())
}
pub fn read_clear_doepint(ep_num: u8) -> u32 {
let r = regs();
let v = r.doep[ep_num as usize].doepint.get();
if v != 0 {
r.doep[ep_num as usize].doepint.set(v);
}
v
}
pub fn read_clear_diepint(ep_num: u8) -> u32 {
let r = regs();
let v = r.diep[ep_num as usize].diepint.get();
if v != 0 {
r.diep[ep_num as usize].diepint.set(v);
}
v
}