feat(UsbBus): implement write and read methods and borrow poll method implementation from atmega_usbd

This commit is contained in:
doryan 2024-11-14 01:52:41 +04:00
parent 4ca8c4e8cf
commit e9d661e9e3

View File

@ -1,7 +1,173 @@
#![no_std]
mod types;
fn poll(&self) -> PollResult {
free(|cs| {
let usb = self.usb.borrow(cs);
use types::*;
let (usbint, udint, udien) = (usb.usbint.read(), usb.udint.read(), usb.udien.read());
if usbint.vbusti().bit_is_set() {
usb.usbint.write(|w| w.vbusti().clear_bit());
if usb.usbsta.read().vbus().bit_is_set() {
return PollResult::Resume;
} else {
return PollResult::Suspend;
}
}
if udint.suspi().bit_is_set() && udien.suspe().bit_is_set() {
return PollResult::Suspend;
}
if udint.wakeupi().bit_is_set() && udien.wakeupe().bit_is_set() {
return PollResult::Resume;
}
if udint.eorsti().bit_is_set() {
return PollResult::Reset;
}
if udint.sofi().bit_is_set() {
usb.udint.write(|w| w.sofi().clear_bit());
}
// Can only query endpoints while clock is running
// (e.g. not in suspend state)
if usb.usbcon.read().frzclk().bit_is_clear() {
let (mut ep_out, mut ep_setup, mut ep_in_complete) = (0u8, 0u8, 0u8);
for (index, _ep) in self
.ep_table
.iter()
.enumerate()
.filter(|(_i, e)| e.is_allocated)
{
if self.select_endpoint(cs, index).is_err() {
// Endpoint selection has stopped working...
break;
}
let ueintx = usb.ueintx.read();
if ueintx.rxouti().bit_is_set() {
ep_out |= 1 << index;
}
if ueintx.rxstpi().bit_is_set() {
ep_setup |= 1 << index;
}
if ueintx.txini().bit_is_set() {
ep_in_complete |= 1 << index;
}
}
if ep_out | ep_setup | ep_in_complete != 0 {
return PollResult::Data {
ep_out: ep_out as u16,
ep_in_complete: ep_in_complete as u16,
ep_setup: ep_setup as u16,
};
}
}
PollResult::None
})
}
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> UsbResult<usize> {
free(|cs| {
let usb = self.usb.borrow(cs);
match self.select_endpoint(cs, ep_addr.index()) {
Ok(()) => {
let target_endpoint = self.ep_table[ep_addr.index()];
let ueintx = usb.ueintx.read();
if ueintx.rxouti().bit_is_clear() {
return Err(UsbError::WouldBlock);
}
if target_endpoint.ep_type == 0 {
let bytes_count_to_read: usize = (usb.uebchx.read().bits() as usize) << 8
| (usb.uebclx.read().bits() as usize);
if bytes_count_to_read > buf.len() {
return Err(UsbError::BufferOverflow);
}
for slot in &mut buf[..bytes_count_to_read] {
*slot = usb.uedatx.read().bits();
}
usb.ueintx
.write(|w| w.rxouti().clear_bit().rxstpi().clear_bit());
Ok(bytes_count_to_read)
} else {
usb.ueintx.write(|w| w.rxouti().clear_bit());
let mut bytes_read = 0;
for slot in buf {
if usb.ueintx.read().rwal().bit_is_clear() {
break;
}
*slot = usb.uedatx.read().bits();
bytes_read += 1;
}
if usb.ueintx.read().rwal().bit_is_set() {
return Err(UsbError::BufferOverflow);
}
usb.ueintx.write(|w| w.fifocon().clear_bit());
Ok(bytes_read)
}
}
Err(err) => Err(err),
}
})
}
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> UsbResult<usize> {
free(|cs| {
let usb = self.usb.borrow(cs);
match self.select_endpoint(cs, ep_addr.index()) {
Ok(()) => {
let target_endpoint = self.ep_table[ep_addr.index()];
let ueintx = usb.ueintx.read();
if ueintx.rxouti().bit_is_clear() {
return Err(UsbError::WouldBlock);
}
if target_endpoint.ep_type == 0 {
let bytes_count_to_read: usize = (usb.uebchx.read().bits() as usize) << 8
| (usb.uebclx.read().bits() as usize);
if bytes_count_to_read > buf.len() {
return Err(UsbError::BufferOverflow);
}
buf.iter()
.for_each(|&byte| usb.uedatx.write(|w| w.bits(byte)));
usb.ueintx
.write(|w| w.rxouti().clear_bit().rxstpi().clear_bit());
Ok(bytes_count_to_read)
} else {
usb.ueintx
.write(|w| w.txini().clear_bit().rxouti().clear_bit());
for &byte in buf {
if usb.ueintx.read().rwal().bit_is_set() {
return Err(UsbError::BufferOverflow);
} else {
usb.uedatx.write(|w| w.bits(byte));
}
}
usb.ueintx
.write(|w| w.fifocon().clear_bit().rxouti().clear_bit());
Ok(buf.len())
}
}
Err(err) => Err(err),
}
})
}