commit 6eb6cef6ee18ea531f627415fee8c2bd183f60d6 Author: doryan Date: Sat Nov 9 01:29:29 2024 +0400 feat: first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bd04b8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +target +rust-toolchain.toml +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2c3a04f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "usb_avr" +version = "0.1.0" +edition = "2021" + + +[dependencies] +panic-halt = "0.2.0" +ufmt = "0.2.0" +nb = "1.1.0" +embedded-hal = "1.0" +usb-device = "0.3.2" + +[dependencies.avr-device] +version = "0.5.4" +features = ["atmega32u4"] + +# The latest releases of `proc-macro2` do not support the rust toolchain that +# we use. Thus, we must fix this dependency to an older version where our +# toolchain is still supported. See https://github.com/Rahix/avr-hal/issues/537 +[build-dependencies.proc-macro2] +version = "=1.0.79" + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..321be66 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +#![no_std] + +mod types; + +use types::*; + + diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..a967481 --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,3 @@ +mod usb_device; + +pub use usb_device::*; diff --git a/src/types/usb_device.rs b/src/types/usb_device.rs new file mode 100644 index 0000000..0e2dab5 --- /dev/null +++ b/src/types/usb_device.rs @@ -0,0 +1,218 @@ +use core::cmp::max; + +use avr_device::{ + atmega32u4::{PLL, USB_DEVICE}, + interrupt::{free, Mutex}, +}; +use usb_device::{ + bus::UsbBus, + endpoint::{EndpointAddress, EndpointType}, + Result, UsbDirection, UsbError, +}; + +#[allow(unused)] +#[derive(Default, Copy, Clone)] +pub(crate) struct USBEndpoint { + is_allocated: bool, + size: u16, + ep_type: u8, + ep_dir: bool, + banks: u8, +} + +const ENDPOINTS_ALLOC_LAYOUT : [u16; 7] = [64, 256, 64, 64, 64, 64, 64]; + +impl USBEndpoint { + #[inline] + fn set_type(&mut self, ep_type: EndpointType) { + self.ep_type = match ep_type { + EndpointType::Control => 0, // 0 = 0b00 + EndpointType::Isochronous { + synchronization: _, + usage: _, + } => 1, // 1 = 0b01 + EndpointType::Bulk => 2, // 2 = 0b10 + EndpointType::Interrupt => 3, // 3 = 0b11 + }; + } + + #[inline] + fn set_dir(&mut self, dir: UsbDirection) { + self.ep_dir = match dir { + UsbDirection::In => true, + UsbDirection::Out => false, + }; + } + + #[inline] + fn set_size(&mut self, size: u16) { + self.size = size; + } +} + +pub struct UsbDevice { + pll: Mutex, + usb: Mutex, + ep_table: [USBEndpoint; L], + dpram_already_used: u16, +} + +const DPRAM_SIZE: u16 = 832; + +impl UsbDevice { + #[inline] + pub fn new(pll: PLL, usb: USB_DEVICE) -> Self { + let (pll, usb) = (Mutex::new(pll), Mutex::new(usb)); + let ep_table: [USBEndpoint; L] = [Default::default(); L]; + Self { + pll, + usb, + ep_table, + dpram_already_used: 0, + } + } +} + +impl UsbBus for UsbDevice { + fn alloc_ep( + &mut self, + ep_dir: UsbDirection, + ep_addr: Option, + ep_type: EndpointType, + max_packet_size: u16, + _interval: u8, + ) -> Result { + if ep_addr == Some(EndpointAddress::from_parts(0, UsbDirection::In)) { + return Ok(ep_addr.unwrap()); + } + + let address = match ep_addr { + Some(ep_addr) if !self.ep_table[ep_addr.index()].is_allocated => ep_addr, + Some(_) => unreachable!(), + None => { + let endpoint = self + .ep_table + .iter() + .enumerate() + .skip(1) + .find(|(i, &ep)| !ep.is_allocated && max_packet_size <= ENDPOINTS_ALLOC_LAYOUT[*i]) + .ok_or(UsbError::EndpointMemoryOverflow)?; + + EndpointAddress::from_parts(endpoint.0, ep_dir) + } + }; + + let target_endpoint = &mut self.ep_table[address.index()]; + + if DPRAM_SIZE - self.dpram_already_used <= max_packet_size || max_packet_size >= 512 { + Err(UsbError::EndpointMemoryOverflow) + } else { + let max_packet_size = max(8, max_packet_size.next_power_of_two()); + + target_endpoint.set_size(max_packet_size); + target_endpoint.set_dir(ep_dir); + target_endpoint.set_type(ep_type); + + target_endpoint.is_allocated = true; + + self.dpram_already_used = max_packet_size; + + Ok(address) + } + } + + fn enable(&mut self) { + free(|cs| { + let (pll, usb) = (self.pll.borrow(cs), self.usb.borrow(cs)); + + // Enable USB pads regulators. // + + usb.uhwcon.modify(|_, w| w.uvrege().set_bit()); + + // Enable USB interface. // + + usb.usbcon + .modify(|_, w| w.usbe().set_bit().frzclk().set_bit()); + + // Configuring PLL. // + + pll.pllfrq + .modify(|_, w| w.pdiv().mhz96().plltm().factor_15().pllusb().set_bit()); + + // Enable PLL. // + + pll.pllcsr + .modify(|_, w| w.pindiv().set_bit().plle().set_bit()); + + while pll.pllcsr.read().plock().bit_is_clear() {} + + // Unfreeze clock. // + + usb.usbcon + .modify(|_, w| w.frzclk().clear_bit().otgpade().set_bit()); + + // Interrupts. // + + usb.udien + .modify(|_, w| w.eorste().set_bit().sofe().set_bit()); + + // Set high speed and attach the USB. // + + usb.udcon + .modify(|_, w| w.lsm().set_bit().detach().clear_bit()); + }) + } + + fn force_reset(&self) -> usb_device::Result<()> { + free(|cs| { + let usb = self.usb.borrow(cs); + usb.usbcon.modify(|_, w| w.usbe().clear_bit()); + usb.usbcon.modify(|_, w| w.usbe().set_bit()); + Ok(()) + }) + } + + fn is_stalled(&self, ep_addr: usb_device::endpoint::EndpointAddress) -> bool { + todo!(); + } + + fn poll(&self) -> usb_device::bus::PollResult { + todo!(); + } + + fn read( + &self, + ep_addr: usb_device::endpoint::EndpointAddress, + buf: &mut [u8], + ) -> usb_device::Result { + todo!(); + } + + fn reset(&self) { + todo!(); + } + + fn resume(&self) { + todo!(); + } + + fn set_device_address(&self, addr: u8) { + todo!(); + } + + fn set_stalled(&self, ep_addr: usb_device::endpoint::EndpointAddress, stalled: bool) { + todo!(); + } + + fn suspend(&self) { + todo!(); + } + + fn write( + &self, + ep_addr: usb_device::endpoint::EndpointAddress, + buf: &[u8], + ) -> usb_device::Result { + todo!(); + } +}