feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"701ca024d73962baf82bb92a24dfb7f539fca5537616cff15451aca22d8191cc",".github/workflows/ci.yml":"dadcf27ab2d2180eda0028066e7ea7a4bdae3bbbf6798c85233a0b9e650e8867",".github/workflows/rustfmt.yml":"c7a7b46c60ea10c8af4293e8cbf13a15c58bf1f69e8cc9cc998752e3a5631903","CHANGELOG.md":"d0bc9e2bb6263e969e77f87ba10e1f209487d4fbbf0a1a89e712ab0f8b3eaf9e","Cargo.toml":"6b093cbb921cfd1703e6bc426c3b175dea863cd8816afe2dc8a8e1355d49ef06","Cargo.toml.orig":"16949174f37f13f2a9dd6f279575ccc17eb3e06aad288b0dfba0851d0158618a","LICENSE":"1d308accd0b1a61e1108ea36020dcda3db9dd1f5cfdb17f3a124bac54f5a6b07","README.md":"a9ceb97827cf4d9f257d382e8b9983a65dd12afc48762bf39d4b453bf03b947b","build.rs":"ae0e916f3b7e02a4eb57533e8059e840573115a0dacfb1b6fbd664dbba873717","src/bus.rs":"4493801a804dcc706383b19aca9e59502588cbc1c65ef68c19ab996d6941caff","src/endpoint.rs":"f5abce9da9f6f957e99eea8d292c502ba17bf103feecf4c9cfeb5642bc4b3d7b","src/endpoint_memory.rs":"4fb8355d51ec54dda8091f17f43d8ffd2fcbc452c97e2a3a151aba6623dbd5f2","src/lib.rs":"9f9b330d40e38ac899e2bb6823a1e638bfe817c9e580bafbe39e08f516c71a21","src/ral/instances/mod.rs":"880db01502226a9e1a18d4ca5e463d827bb8d3af2be724e86750dc9b05b6b96f","src/ral/instances/otg_fs_device_f401_f405_f407_f411_f427_f429.rs":"e477be7312b6d17c8de96ae186ec5ef6a8cb7d05800b04f11dbb65d622189abf","src/ral/instances/otg_fs_global_f401_f405_f407_f411_f427_f429.rs":"60f44afc1d21bd75baaaab684aa49812f0a0c741d6c4b85e0989324bd66f267c","src/ral/instances/otg_fs_host.rs":"1c393fc2c78f1f88b719a28c1cefc9d53cfe84fcf1058051404c54d17915c2f2","src/ral/instances/otg_hs_device.rs":"22d3963a75b14c7697eea02de0c3e44014117be7ebf4a99a3990615b1de8c269","src/ral/instances/otg_hs_global.rs":"3d11b355ea322c7c6094b260d2604da72d17e6b791eaa89141d354a86b1c2198","src/ral/instances/otg_hs_host.rs":"4a246ca830d5aea337d7d163af3c886a233f59f22cf975ca8e65d753b0545576","src/ral/instances/otg_s_pwrclk.rs":"3bd943b97ccd6440c1d6d99507077aa7058cb9666ed3a983046bba616d6b8332","src/ral/mod.rs":"46226a9f3b949c7bb1ce230bf60037611e1a9841b6721850bdbf2ba96812fd91","src/ral/peripherals/mod.rs":"74bc65b43f83d394e359de8866174f85ac82179be0c7e188dafe9cea26ae6998","src/ral/peripherals/otg_fs_device_v1.rs":"67763c1326c46b9ae8c4c6087dd66a4802aa0cef805cd97fd8bb8121e04f39a8","src/ral/peripherals/otg_fs_global_v1.rs":"271e2f3d8a95db86aedc0fc5a0365341681eda8e97d74deab32e3fd530396c60","src/ral/peripherals/otg_fs_host.rs":"c35266e6b5884ddeb882120704d8b3adbe5fcb25e0edc420b8cadbbd90c0ff24","src/ral/peripherals/otg_fs_pwrclk.rs":"0bd4f85bd7701216462090900238f3f7687fb049061c18955636334a5d90bbf7","src/ral/peripherals/otg_hs_device.rs":"d7028b42c7d90aa5293d05ec895d49c92557631612dbd8e6ef401a6a2ffbe19a","src/ral/peripherals/otg_hs_global.rs":"6494b730b822067bcbd89c0003e2676da446554fee17943de03412847068fb5b","src/ral/peripherals/otg_hs_host.rs":"fe2d36cd4fec6446a4690119e01af51a1061f2000692c8ade3bb3b73aed1d65e","src/ral/stm32f429/mod.rs":"9be54fb00a9cc686dd0b32b0fdbc8cb2266750e17c2ab099d923a4b42aebf703","src/target.rs":"84552c3b87e97ac9e77e10991dcf0e87eed1299db4165fceca3a21661ad1fde5","src/transition.rs":"b82cba76de2059cabfa46a70e61c43b91a9ddb8b43b741ae061dcf72cb116a2b"},"package":"8938451cb19032f13365328ea66ab38c8d16deecdf322067442297110eb74468"}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "467716c2f038c1ac9d340f3f8d2f62a3a4120fde"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: CI
|
||||
|
||||
jobs:
|
||||
ci-linux:
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: ${{ matrix.experimental || false }}
|
||||
strategy:
|
||||
matrix:
|
||||
rust: [stable, nightly]
|
||||
include:
|
||||
# Nightly is only for reference and allowed to fail
|
||||
- rust: nightly
|
||||
experimental: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
|
||||
- name: Check code (fs)
|
||||
run: cargo check --features fs
|
||||
|
||||
- name: Check code (hs)
|
||||
run: cargo check --features hs
|
||||
|
||||
- name: Check code (hs xcvrdly)
|
||||
run: cargo check --features "hs xcvrdly"
|
||||
@@ -0,0 +1,17 @@
|
||||
on:
|
||||
push:
|
||||
branches: [ staging, trying, master ]
|
||||
pull_request:
|
||||
|
||||
name: Code formatting check
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: Rustfmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
* Use `critical-section` crate for critical sections.
|
||||
* Replaced bundled RAL macros with `ral-registers`
|
||||
|
||||
|
||||
## [v0.4.0] - 2023-11-18
|
||||
|
||||
### Changed
|
||||
|
||||
* `usb-device` version bumped to v0.3.0
|
||||
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/stm32-rs/synopsys-usb-otg/compare/v0.4.0...HEAD
|
||||
[0.4.0]: https://github.com/stm32-rs/synopsys-usb-otg/compare/v0.4.0...v0.3.2
|
||||
[0.3.2]: https://github.com/stm32-rs/synopsys-usb-otg/compare/v0.3.2...v0.3.1
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "esp-synopsys-usb-otg"
|
||||
version = "0.4.2"
|
||||
authors = [
|
||||
"Vadim Kaushan <admin@disasm.info>",
|
||||
"The ESP-RS Team",
|
||||
]
|
||||
build = "build.rs"
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "'usb-device' implementation for Synopsys USB OTG IP cores"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"no-std",
|
||||
"embedded",
|
||||
"usb",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/esp-rs-compat/synopsys-usb-otg"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["fs"]
|
||||
|
||||
[lib]
|
||||
name = "esp_synopsys_usb_otg"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.critical-section]
|
||||
version = "1.1.1"
|
||||
|
||||
[dependencies.embedded-hal]
|
||||
version = "0.2.4"
|
||||
|
||||
[dependencies.ral-registers]
|
||||
version = "0.1.3"
|
||||
|
||||
[dependencies.usb-device]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.vcell]
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
esp32sx = []
|
||||
fs = []
|
||||
hs = []
|
||||
xcvrdly = []
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2020 Matti Virkkunen, Vadim Kaushan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
[](https://crates.io/crates/esp-synopsys-usb-otg)
|
||||
[](https://crates.io/crates/esp-synopsys-usb-otg)
|
||||
|
||||
# This is a fork of [synopsys-usb-otg](https://crates.io/crates/synopsys-usb-otg) with support for ESP32-Sx added - please use https://github.com/stm32-rs/synopsys-usb-otg when not targeting ESP32-Sx
|
||||
|
||||
# `synopsys-usb-otg`
|
||||
|
||||
> [usb-device](https://github.com/mvirkkunen/usb-device) implementation for Synopsys USB OTG IP cores.
|
||||
|
||||
This project is a successor to the [great work](https://github.com/mvirkkunen/stm32f103xx-usb)
|
||||
started by [@mvirkkunen](https://github.com/mvirkkunen).
|
||||
|
||||
## Supported microcontrollers
|
||||
|
||||
* `STM32F429xx` (OTG_FS and OTG_HS in FS mode)
|
||||
* `STM32F401xx`
|
||||
* `STM32F446xx` (OTG_FS and OTG_HS in FS mode)
|
||||
* `STM32F723xx` (OTG_FS and OTG_HS with internal HS PHY)
|
||||
* `STM32H7xxxx` (OTG1_HS and OTG2_HS in FS mode, and OTG1_HS with external HS PHY)
|
||||
* And others...
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
This driver is intended for use through a device hal library.
|
||||
Such hal library should implement `UsbPeripheral` for the corresponding USB peripheral object.
|
||||
This trait declares all the peripheral properties that may vary from one device family to the other.
|
||||
Additionally, hal should pass `fs` of `hs` feature to the `synopsys-usb-otg` library to
|
||||
define a peripheral type:
|
||||
* `fs` - for FullSpeed peripherals
|
||||
* `hs` - for HighSpeed peripherals
|
||||
|
||||
Only one peripheral type can be selected at the moment.
|
||||
|
||||
### ULPI Transciever Delay
|
||||
|
||||
Some ULPI PHYs like the Microchip USB334x series require a delay between the ULPI register write that initiates
|
||||
the HS Chirp and the subsequent transmit command, otherwise the HS Chirp does not get executed and the deivce
|
||||
enumerates in FS mode. Some USB Link IP like those in the STM32H7 series support adding this delay to work with the
|
||||
affected PHYs. Enable the `xcvrdly` feature to add this delay.
|
||||
|
||||
## Examples
|
||||
|
||||
See the [usb-otg-workspace](https://github.com/Disasm/usb-otg-workspace) repo for different device-specific examples.
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
fn main() {
|
||||
let profile = std::env::var("PROFILE").unwrap();
|
||||
let target = std::env::var("TARGET").unwrap();
|
||||
|
||||
if profile == "debug" && !target.starts_with("x86_64") {
|
||||
println!("cargo:warning=synopsys-usb-otg is being compiled in debug mode. This driver works reliably only in release mode!");
|
||||
}
|
||||
}
|
||||
+851
@@ -0,0 +1,851 @@
|
||||
use crate::ral::{
|
||||
modify_reg, otg_device, otg_global, otg_global_dieptxfx, otg_pwrclk, read_reg, write_reg,
|
||||
};
|
||||
use crate::transition::{EndpointConfig, EndpointDescriptor};
|
||||
use core::marker::PhantomData;
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use embedded_hal::blocking::delay::DelayMs;
|
||||
use usb_device::bus::{PollResult, UsbBusAllocator};
|
||||
use usb_device::endpoint::{EndpointAddress, EndpointType};
|
||||
use usb_device::{Result, UsbDirection, UsbError};
|
||||
|
||||
use crate::endpoint::{EndpointIn, EndpointOut};
|
||||
use crate::endpoint_memory::{EndpointBufferState, EndpointMemoryAllocator};
|
||||
use crate::target::UsbRegisters;
|
||||
use crate::{PhyType, UsbPeripheral};
|
||||
|
||||
/// USB peripheral driver for STM32 microcontrollers.
|
||||
pub struct UsbBus<USB> {
|
||||
peripheral: USB,
|
||||
regs: Mutex<UsbRegisters>,
|
||||
allocator: EndpointAllocator<USB>,
|
||||
}
|
||||
|
||||
impl<USB: UsbPeripheral> UsbBus<USB> {
|
||||
/// Constructs a new USB peripheral driver.
|
||||
pub fn new(peripheral: USB, ep_memory: &'static mut [u32]) -> UsbBusAllocator<Self> {
|
||||
let bus = UsbBus {
|
||||
peripheral,
|
||||
regs: Mutex::new(UsbRegisters::new::<USB>()),
|
||||
allocator: EndpointAllocator::new(ep_memory),
|
||||
};
|
||||
|
||||
UsbBusAllocator::new(bus)
|
||||
}
|
||||
|
||||
pub fn free(self) -> USB {
|
||||
self.peripheral
|
||||
}
|
||||
|
||||
fn configure_all(&self, cs: CriticalSection<'_>) {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
// Rx FIFO
|
||||
// This calculation doesn't correspond to one in a Reference Manual.
|
||||
// In fact, the required number of words is higher than indicated in RM.
|
||||
// The following numbers are pessimistic and were figured out empirically.
|
||||
let rx_fifo_size = if USB::HIGH_SPEED {
|
||||
self.allocator.memory_allocator.total_rx_buffer_size_words() + 30
|
||||
} else {
|
||||
// F429 requires 35+ words for the (EP0[8] + EP2[64]) setup
|
||||
// F446 requires 39+ words for the same setup
|
||||
self.allocator.memory_allocator.total_rx_buffer_size_words() + 30
|
||||
};
|
||||
write_reg!(otg_global, regs.global(), GRXFSIZ, rx_fifo_size as u32);
|
||||
let mut fifo_top = rx_fifo_size;
|
||||
|
||||
// Tx FIFO #0
|
||||
let fifo_size = self.allocator.memory_allocator.tx_fifo_size_words(0);
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
write_reg!(
|
||||
otg_global,
|
||||
regs.global(),
|
||||
DIEPTXF0,
|
||||
TX0FD: fifo_size as u32,
|
||||
TX0FSA: fifo_top as u32
|
||||
);
|
||||
#[cfg(feature = "hs")]
|
||||
write_reg!(
|
||||
otg_global,
|
||||
regs.global(),
|
||||
GNPTXFSIZ,
|
||||
TX0FD: fifo_size as u32,
|
||||
TX0FSA: fifo_top as u32
|
||||
);
|
||||
|
||||
fifo_top += fifo_size;
|
||||
|
||||
// Tx FIFOs
|
||||
for i in 1..USB::ENDPOINT_COUNT {
|
||||
let fifo_size = self.allocator.memory_allocator.tx_fifo_size_words(i);
|
||||
|
||||
let dieptxfx = regs.dieptxfx(i);
|
||||
write_reg!(
|
||||
otg_global_dieptxfx,
|
||||
dieptxfx,
|
||||
DIEPTXFx,
|
||||
INEPTXFD: fifo_size as u32,
|
||||
INEPTXSA: fifo_top as u32
|
||||
);
|
||||
|
||||
fifo_top += fifo_size;
|
||||
}
|
||||
|
||||
assert!(fifo_top as usize <= USB::FIFO_DEPTH_WORDS);
|
||||
|
||||
// Flush Rx & Tx FIFOs
|
||||
modify_reg!(otg_global, regs.global(), GRSTCTL, RXFFLSH: 1, TXFFLSH: 1, TXFNUM: 0x10);
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, RXFFLSH, TXFFLSH) != (0, 0) {}
|
||||
|
||||
for ep in &self.allocator.endpoints_in {
|
||||
if let Some(ep) = ep {
|
||||
// enabling EP TX interrupt
|
||||
modify_reg!(otg_device, regs.device(), DAINTMSK, |v| v
|
||||
| (0x0001 << ep.address().index()));
|
||||
|
||||
ep.configure(cs);
|
||||
}
|
||||
}
|
||||
|
||||
for ep in &self.allocator.endpoints_out {
|
||||
if let Some(ep) = ep {
|
||||
if ep.address().index() == 0 {
|
||||
// enabling RX interrupt from EP0
|
||||
modify_reg!(otg_device, regs.device(), DAINTMSK, |v| v | 0x00010000);
|
||||
}
|
||||
|
||||
ep.configure(cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deconfigure_all(&self, cs: CriticalSection<'_>) {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
// disable interrupts
|
||||
modify_reg!(otg_device, regs.device(), DAINTMSK, IEPM: 0, OEPM: 0);
|
||||
|
||||
for ep in &self.allocator.endpoints_in {
|
||||
if let Some(ep) = ep {
|
||||
ep.deconfigure(cs);
|
||||
}
|
||||
}
|
||||
|
||||
for ep in &self.allocator.endpoints_out {
|
||||
if let Some(ep) = ep {
|
||||
ep.deconfigure(cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn force_reset(&self, delay: &mut impl DelayMs<u32>) -> Result<()> {
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
write_reg!(otg_device, regs.device(), DCTL, SDIS: 1); // Soft disconnect
|
||||
delay.delay_ms(3);
|
||||
write_reg!(otg_device, regs.device(), DCTL, SDIS: 0); // Soft connect
|
||||
delay.delay_ms(3);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
/// Reads from a ULPI register in an external ULPI PHY.
|
||||
///
|
||||
/// Interrupts are disabled for the duration of the function call.
|
||||
///
|
||||
/// **Panics:** if `phy_type` is not `PhyType::ExternalHighSpeed`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use synopsys_usb_otg::{UsbPeripheral, UsbBus};
|
||||
/// fn read_usb_vid_pid<USB: UsbPeripheral>(bus: &UsbBus<USB>) -> (u16, u16) {
|
||||
/// let mut vid: u16 = bus.ulpi_read(0x00) as u16;
|
||||
/// vid |= (bus.ulpi_read(0x01) as u16) << 8;
|
||||
/// let mut pid: u16 = bus.ulpi_read(0x02) as u16;
|
||||
/// pid |= (bus.ulpi_read(0x03) as u16) << 8;
|
||||
/// (vid, pid)
|
||||
/// }
|
||||
/// ```
|
||||
pub fn ulpi_read(&self, addr: u8) -> core::result::Result<u8, UlpiError> {
|
||||
if self.peripheral.phy_type() != PhyType::ExternalHighSpeed {
|
||||
panic!("ulpi_read is only supported with external ULPI PHYs");
|
||||
}
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
// Begin ULPI register read transaction
|
||||
modify_reg!(otg_global, regs.global(), PHYCR,
|
||||
NEW: 1,
|
||||
RW: 0,
|
||||
ADDR: addr as u32
|
||||
);
|
||||
|
||||
// ULPI transactions should take less than 1us. 1000 iterations should be enough even for fast MCUs.
|
||||
let mut timeout = 1000;
|
||||
while timeout > 0 {
|
||||
// Wait for transaction to complete
|
||||
if read_reg!(otg_global, regs.global(), PHYCR, DONE) != 0 {
|
||||
// Read transaction data
|
||||
return Ok((read_reg!(otg_global, regs.global(), PHYCR, DATA) & 0xFF) as u8);
|
||||
}
|
||||
timeout -= 1;
|
||||
}
|
||||
Err(UlpiError::Timeout)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
/// Writes to a ULPI register in an external ULPI PHY.
|
||||
///
|
||||
/// Interrupts are disabled for the duration of the function call.
|
||||
///
|
||||
/// **Panics:** if `phy_type` is not `PhyType::ExternalHighSpeed`.
|
||||
pub fn ulpi_write(&self, addr: u8, data: u8) -> core::result::Result<(), UlpiError> {
|
||||
if self.peripheral.phy_type() != PhyType::ExternalHighSpeed {
|
||||
panic!("ulpi_write is only supported with external ULPI PHYs");
|
||||
}
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
// Begin ULPI register write transaction
|
||||
modify_reg!(otg_global, regs.global(), PHYCR,
|
||||
NEW: 1,
|
||||
RW: 1,
|
||||
ADDR: addr as u32,
|
||||
DATA: data as u32
|
||||
);
|
||||
|
||||
// ULPI transactions should take less than 1us. 1000 iterations should be enough even for fast MCUs.
|
||||
let mut timeout = 1000;
|
||||
while timeout > 0 {
|
||||
// Wait for transaction to complete
|
||||
if read_reg!(otg_global, regs.global(), PHYCR, DONE) != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
timeout -= 1;
|
||||
}
|
||||
Err(UlpiError::Timeout)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
#[derive(Debug)]
|
||||
/// Errors that can occur while interfacing with a ULPI PHY.
|
||||
pub enum UlpiError {
|
||||
/// The action has timed out.
|
||||
Timeout,
|
||||
}
|
||||
|
||||
pub(crate) struct EndpointAllocator<USB> {
|
||||
bitmap_in: u8,
|
||||
bitmap_out: u8,
|
||||
endpoints_in: [Option<EndpointIn>; 9],
|
||||
endpoints_out: [Option<EndpointOut>; 9],
|
||||
memory_allocator: EndpointMemoryAllocator<USB>,
|
||||
_marker: PhantomData<USB>,
|
||||
}
|
||||
|
||||
impl<USB: UsbPeripheral> EndpointAllocator<USB> {
|
||||
fn new(memory: &'static mut [u32]) -> Self {
|
||||
assert!(USB::ENDPOINT_COUNT <= 9);
|
||||
Self {
|
||||
bitmap_in: 0,
|
||||
bitmap_out: 0,
|
||||
// [None; 9] requires Copy
|
||||
endpoints_in: [None, None, None, None, None, None, None, None, None],
|
||||
endpoints_out: [None, None, None, None, None, None, None, None, None],
|
||||
memory_allocator: EndpointMemoryAllocator::new(memory),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_number(bitmap: &mut u8, number: Option<u8>) -> Result<u8> {
|
||||
if let Some(number) = number {
|
||||
if number as usize >= USB::ENDPOINT_COUNT {
|
||||
return Err(UsbError::InvalidEndpoint);
|
||||
}
|
||||
if *bitmap & (1 << number) == 0 {
|
||||
*bitmap |= 1 << number;
|
||||
Ok(number)
|
||||
} else {
|
||||
Err(UsbError::InvalidEndpoint)
|
||||
}
|
||||
} else {
|
||||
// Skip EP0
|
||||
for number in 1..USB::ENDPOINT_COUNT {
|
||||
if *bitmap & (1 << number) == 0 {
|
||||
*bitmap |= 1 << number;
|
||||
return Ok(number as u8);
|
||||
}
|
||||
}
|
||||
Err(UsbError::EndpointOverflow)
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc(
|
||||
bitmap: &mut u8,
|
||||
config: &EndpointConfig,
|
||||
direction: UsbDirection,
|
||||
) -> Result<EndpointDescriptor> {
|
||||
let number = Self::alloc_number(bitmap, config.number)?;
|
||||
let address = EndpointAddress::from_parts(number as usize, direction);
|
||||
Ok(EndpointDescriptor {
|
||||
address,
|
||||
ep_type: config.ep_type,
|
||||
max_packet_size: config.max_packet_size,
|
||||
interval: config.interval,
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_in(&mut self, config: &EndpointConfig) -> Result<EndpointIn> {
|
||||
let descr = Self::alloc(&mut self.bitmap_in, config, UsbDirection::In)?;
|
||||
|
||||
self.memory_allocator
|
||||
.allocate_tx_buffer(descr.address.index() as u8, descr.max_packet_size as usize)?;
|
||||
let ep = EndpointIn::new::<USB>(descr);
|
||||
|
||||
Ok(ep)
|
||||
}
|
||||
|
||||
fn alloc_out(&mut self, config: &EndpointConfig) -> Result<EndpointOut> {
|
||||
let descr = Self::alloc(&mut self.bitmap_out, config, UsbDirection::Out)?;
|
||||
|
||||
let buffer = self
|
||||
.memory_allocator
|
||||
.allocate_rx_buffer(descr.max_packet_size as usize)?;
|
||||
let ep = EndpointOut::new::<USB>(descr, buffer);
|
||||
|
||||
Ok(ep)
|
||||
}
|
||||
|
||||
fn alloc_ep(
|
||||
&mut self,
|
||||
ep_dir: UsbDirection,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<EndpointAddress> {
|
||||
let ep_type = unsafe { core::mem::transmute(ep_type) };
|
||||
let number = ep_addr.map(|a| a.index() as u8);
|
||||
|
||||
let config = EndpointConfig {
|
||||
ep_type,
|
||||
max_packet_size,
|
||||
interval,
|
||||
number,
|
||||
pair_of: None,
|
||||
};
|
||||
match ep_dir {
|
||||
UsbDirection::Out => {
|
||||
let ep = self.alloc_out(&config)?;
|
||||
let address = ep.address();
|
||||
self.endpoints_out[address.index()] = Some(ep);
|
||||
Ok(address)
|
||||
}
|
||||
UsbDirection::In => {
|
||||
let ep = self.alloc_in(&config)?;
|
||||
let address = ep.address();
|
||||
self.endpoints_in[address.index()] = Some(ep);
|
||||
Ok(address)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<USB: UsbPeripheral> usb_device::bus::UsbBus for UsbBus<USB> {
|
||||
fn alloc_ep(
|
||||
&mut self,
|
||||
ep_dir: UsbDirection,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<EndpointAddress> {
|
||||
self.allocator
|
||||
.alloc_ep(ep_dir, ep_addr, ep_type, max_packet_size, interval)
|
||||
}
|
||||
|
||||
fn enable(&mut self) {
|
||||
// Enable USB_OTG in RCC
|
||||
USB::enable();
|
||||
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
let core_id = read_reg!(otg_global, regs.global(), CID);
|
||||
|
||||
// Wait for AHB ready
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, AHBIDL) == 0 {}
|
||||
|
||||
// Configure OTG as device
|
||||
#[cfg(feature = "fs")]
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
SRPCAP: 0, // SRP capability is not enabled
|
||||
FDMOD: 1 // Force device mode
|
||||
);
|
||||
#[cfg(feature = "hs")]
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
SRPCAP: 0, // SRP capability is not enabled
|
||||
TOCAL: 0x1,
|
||||
FDMOD: 1 // Force device mode
|
||||
);
|
||||
|
||||
// Configure USB PHY
|
||||
#[cfg(feature = "hs")]
|
||||
match self.peripheral.phy_type() {
|
||||
PhyType::InternalFullSpeed => {
|
||||
// Select FS Embedded PHY
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG, PHYSEL: 1);
|
||||
}
|
||||
PhyType::InternalHighSpeed => {
|
||||
// Turn off PHY
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, PWRDWN: 0);
|
||||
|
||||
// Init The UTMI Interface
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
TSDPS: 0,
|
||||
ULPIFSLS: 0,
|
||||
PHYSEL: 0 // ULPI or UTMI
|
||||
);
|
||||
|
||||
// Select VBUS source
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
ULPIEVBUSD: 0,
|
||||
ULPIEVBUSI: 0
|
||||
);
|
||||
|
||||
// Select UTMI Interace
|
||||
//modify_reg!(otg_global, regs.global(), GUSBCFG, ULPISEL: 0);
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG, |r| r & !(1 << 4));
|
||||
|
||||
// This is a secret bit from ST that is not mentioned anywhere except
|
||||
// the driver code shipped with STM32CubeIDE.
|
||||
//modify_reg!(otg_global, regs.global(), GCCFG, PHYHSEN: 1);
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, |r| r | (1 << 23));
|
||||
|
||||
self.peripheral.setup_internal_hs_phy();
|
||||
}
|
||||
PhyType::ExternalHighSpeed => {
|
||||
// Turn off embedded PHY
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, PWRDWN: 0);
|
||||
|
||||
// Init The ULPI Interface
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
TSDPS: 0,
|
||||
ULPIFSLS: 0,
|
||||
PHYSEL: 0 // ULPI or UTMI
|
||||
);
|
||||
|
||||
// Select VBUS source
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG,
|
||||
ULPIEVBUSD: 0,
|
||||
ULPIEVBUSI: 0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform core soft-reset
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, AHBIDL) == 0 {}
|
||||
modify_reg!(otg_global, regs.global(), GRSTCTL, CSRST: 1);
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, CSRST) == 1 {}
|
||||
|
||||
if self.peripheral.phy_type() == PhyType::InternalFullSpeed {
|
||||
// Activate the USB Transceiver
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, PWRDWN: 1);
|
||||
}
|
||||
|
||||
// Configuring Vbus sense and SOF output
|
||||
match core_id {
|
||||
0x0000_1200 | 0x0000_1100 => {
|
||||
// F429-like chips have the GCCFG.NOVBUSSENS bit
|
||||
|
||||
//modify_reg!(otg_global, regs.global, GCCFG, NOVBUSSENS: 1);
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, |r| r | (1 << 21));
|
||||
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, VBUSASEN: 0, VBUSBSEN: 0, SOFOUTEN: 0);
|
||||
}
|
||||
0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => {
|
||||
// F446-like chips have the GCCFG.VBDEN bit with the opposite meaning
|
||||
|
||||
//modify_reg!(otg_global, regs.global, GCCFG, VBDEN: 0);
|
||||
modify_reg!(otg_global, regs.global(), GCCFG, |r| r & !(1 << 21));
|
||||
|
||||
// Force B-peripheral session
|
||||
//modify_reg!(otg_global, regs.global, GOTGCTL, BVALOEN: 1, BVALOVAL: 1);
|
||||
modify_reg!(otg_global, regs.global(), GOTGCTL, |r| r | (0b11 << 6));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Enable PHY clock
|
||||
write_reg!(otg_pwrclk, regs.pwrclk(), PCGCCTL, 0);
|
||||
|
||||
// Soft disconnect device
|
||||
modify_reg!(otg_device, regs.device(), DCTL, SDIS: 1);
|
||||
|
||||
// Setup USB speed and frame interval
|
||||
let speed = match (USB::HIGH_SPEED, self.peripheral.phy_type()) {
|
||||
(false, _) => 0b11,
|
||||
(true, PhyType::InternalFullSpeed) => 0b11,
|
||||
(true, PhyType::InternalHighSpeed) => 0b00,
|
||||
(true, PhyType::ExternalHighSpeed) => 0b00,
|
||||
};
|
||||
modify_reg!(otg_device, regs.device(), DCFG,
|
||||
PFIVL: 0b00,
|
||||
DSPD: speed
|
||||
);
|
||||
#[cfg(feature = "xcvrdly")]
|
||||
modify_reg!(otg_device, regs.device(), DCFG, XCVRDLY: 1);
|
||||
|
||||
// unmask EP interrupts
|
||||
write_reg!(otg_device, regs.device(), DIEPMSK, XFRCM: 1);
|
||||
|
||||
// unmask core interrupts
|
||||
write_reg!(otg_global, regs.global(), GINTMSK,
|
||||
USBRST: 1, ENUMDNEM: 1,
|
||||
USBSUSPM: 1, WUIM: 1,
|
||||
IEPINT: 1, RXFLVLM: 1,
|
||||
IISOIXFRM: 1
|
||||
);
|
||||
|
||||
// clear pending interrupts
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, 0xffffffff);
|
||||
|
||||
// unmask global interrupt
|
||||
modify_reg!(otg_global, regs.global(), GAHBCFG, GINT: 1);
|
||||
|
||||
// connect(true)
|
||||
modify_reg!(otg_device, regs.device(), DCTL, SDIS: 0);
|
||||
});
|
||||
}
|
||||
|
||||
fn reset(&self) {
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
self.configure_all(cs);
|
||||
|
||||
modify_reg!(otg_device, regs.device(), DCFG, DAD: 0);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_device_address(&self, addr: u8) {
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
modify_reg!(otg_device, regs.device(), DCFG, DAD: addr as u32);
|
||||
});
|
||||
}
|
||||
|
||||
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize> {
|
||||
if !ep_addr.is_in() || ep_addr.index() >= USB::ENDPOINT_COUNT {
|
||||
return Err(UsbError::InvalidEndpoint);
|
||||
}
|
||||
if let Some(ep) = &self.allocator.endpoints_in[ep_addr.index()] {
|
||||
ep.write(buf).map(|_| buf.len())
|
||||
} else {
|
||||
Err(UsbError::InvalidEndpoint)
|
||||
}
|
||||
}
|
||||
|
||||
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize> {
|
||||
if !ep_addr.is_out() || ep_addr.index() >= USB::ENDPOINT_COUNT {
|
||||
return Err(UsbError::InvalidEndpoint);
|
||||
}
|
||||
|
||||
if let Some(ep) = &self.allocator.endpoints_out[ep_addr.index()] {
|
||||
ep.read(buf)
|
||||
} else {
|
||||
Err(UsbError::InvalidEndpoint)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) {
|
||||
if ep_addr.index() >= USB::ENDPOINT_COUNT {
|
||||
return;
|
||||
}
|
||||
|
||||
let regs = UsbRegisters::new::<USB>();
|
||||
crate::endpoint::set_stalled(regs, ep_addr, stalled)
|
||||
}
|
||||
|
||||
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool {
|
||||
if ep_addr.index() >= USB::ENDPOINT_COUNT {
|
||||
return true;
|
||||
}
|
||||
|
||||
let regs = UsbRegisters::new::<USB>();
|
||||
crate::endpoint::is_stalled(regs, ep_addr)
|
||||
}
|
||||
|
||||
fn suspend(&self) {
|
||||
// Nothing to do here?
|
||||
}
|
||||
|
||||
fn resume(&self) {
|
||||
// Nothing to do here?
|
||||
}
|
||||
|
||||
fn poll(&self) -> PollResult {
|
||||
critical_section::with(|cs| {
|
||||
let regs = self.regs.borrow(cs);
|
||||
|
||||
let core_id = read_reg!(otg_global, regs.global(), CID);
|
||||
|
||||
let (wakeup, suspend, enum_done, reset, iep, rxflvl, iisoixfr) = read_reg!(
|
||||
otg_global,
|
||||
regs.global(),
|
||||
GINTSTS,
|
||||
WKUPINT,
|
||||
USBSUSP,
|
||||
ENUMDNE,
|
||||
USBRST,
|
||||
IEPINT,
|
||||
RXFLVL,
|
||||
IISOIXFR
|
||||
);
|
||||
|
||||
if reset != 0 {
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, USBRST: 1);
|
||||
|
||||
self.deconfigure_all(cs);
|
||||
|
||||
// Flush RX
|
||||
modify_reg!(otg_global, regs.global(), GRSTCTL, RXFFLSH: 1);
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, RXFFLSH) == 1 {}
|
||||
}
|
||||
|
||||
if enum_done != 0 {
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, ENUMDNE: 1);
|
||||
|
||||
let speed = read_reg!(otg_device, regs.device(), DSTS, ENUMSPD);
|
||||
|
||||
// Compute and update TRDT
|
||||
let mut trdt = 0;
|
||||
match speed {
|
||||
0b00 => {
|
||||
// High speed
|
||||
|
||||
// From RM0431 (F72xx), RM0090 (F429), RM0390 (F446)
|
||||
if self.peripheral.ahb_frequency_hz() >= 30_000_000 {
|
||||
trdt = 0x9;
|
||||
} else {
|
||||
panic!("AHB frequency is too low")
|
||||
}
|
||||
}
|
||||
0b01 | 0b11 => {
|
||||
// Full speed
|
||||
|
||||
// From RM0431 (F72xx), RM0090 (F429)
|
||||
#[cfg(not(feature = "esp32sx"))]
|
||||
let _trdt = match self.peripheral.ahb_frequency_hz() {
|
||||
0..=14_199_999 => panic!("AHB frequency is too low"),
|
||||
14_200_000..=14_999_999 => 0xF,
|
||||
15_000_000..=15_999_999 => 0xE,
|
||||
16_000_000..=17_199_999 => 0xD,
|
||||
17_200_000..=18_499_999 => 0xC,
|
||||
18_500_000..=19_999_999 => 0xB,
|
||||
20_000_000..=21_799_999 => 0xA,
|
||||
21_800_000..=23_999_999 => 0x9,
|
||||
24_000_000..=27_499_999 => 0x8,
|
||||
27_500_000..=31_999_999 => 0x7, // 27.7..32 in code from CubeIDE
|
||||
32_000_000..=u32::MAX => 0x6,
|
||||
};
|
||||
|
||||
#[cfg(feature = "esp32sx")]
|
||||
let _trdt = 5;
|
||||
|
||||
trdt = trdt;
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
modify_reg!(otg_global, regs.global(), GUSBCFG, TRDT: trdt);
|
||||
|
||||
PollResult::Reset
|
||||
} else if wakeup != 0 {
|
||||
// Clear the interrupt
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, WKUPINT: 1);
|
||||
|
||||
PollResult::Resume
|
||||
} else if suspend != 0 {
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, USBSUSP: 1);
|
||||
|
||||
PollResult::Suspend
|
||||
} else if iisoixfr != 0 {
|
||||
use crate::ral::endpoint_in;
|
||||
|
||||
// Incomplete isochronous IN transfer; see
|
||||
// RM0383 Rev 3 pp797 "Incomplete isochronous IN data transfers"
|
||||
write_reg!(otg_global, regs.global(), GINTSTS, IISOIXFR: 1);
|
||||
|
||||
let in_endpoints = self
|
||||
.allocator
|
||||
.endpoints_in
|
||||
.iter()
|
||||
.flatten()
|
||||
.map(|ep| ep.address().index());
|
||||
|
||||
let mut iep: u16 = 0;
|
||||
|
||||
for epnum in in_endpoints {
|
||||
let ep_regs = regs.endpoint_in(epnum);
|
||||
|
||||
// Filter out non-isochronous endpoints
|
||||
if read_reg!(endpoint_in, ep_regs, DIEPCTL, EPTYP) & 0x11 != 0x01 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// RM0383 states that the NAK bit in DIEPINT is set when a
|
||||
// zero length packet is transmitted due to unavailability
|
||||
// of data in the Tx FIFO.
|
||||
// However, this bit is not defined in the RAL and
|
||||
// by checking several STM32 RMs, it's even unclear
|
||||
// if it actually exists on OTG_FS peripherals.
|
||||
// While testing with macOS, the bit was never set.
|
||||
// Therefore, an alternative method is used to check if
|
||||
// an endpoint is affected.
|
||||
// TODO: investigate if this check works correctly and
|
||||
// does not affect other isochronous IN endpoints by mistake.
|
||||
if read_reg!(endpoint_in, ep_regs, DIEPCTL, NAKSTS) == 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Set NAK
|
||||
modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1);
|
||||
while read_reg!(endpoint_in, ep_regs, DIEPINT, INEPNE) == 0 {}
|
||||
|
||||
// Disable the endpoint
|
||||
modify_reg!(endpoint_in, ep_regs, DIEPCTL, SNAK: 1, EPDIS: 1);
|
||||
while read_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD) == 0 {}
|
||||
modify_reg!(endpoint_in, ep_regs, DIEPINT, EPDISD: 1);
|
||||
|
||||
// Flush the TX FIFO
|
||||
modify_reg!(otg_global, regs.global(), GRSTCTL, TXFNUM: epnum as u32, TXFFLSH: 1);
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, TXFFLSH) == 1 {}
|
||||
|
||||
iep |= 1 << epnum;
|
||||
}
|
||||
|
||||
PollResult::Data {
|
||||
ep_out: 0,
|
||||
ep_in_complete: iep,
|
||||
ep_setup: 0,
|
||||
}
|
||||
} else {
|
||||
let mut ep_out = 0;
|
||||
let mut ep_in_complete = 0;
|
||||
let mut ep_setup = 0;
|
||||
|
||||
use crate::ral::{endpoint_in, endpoint_out};
|
||||
|
||||
// RXFLVL & IEPINT flags are read-only, there is no need to clear them
|
||||
if rxflvl != 0 {
|
||||
let (epnum, data_size, status) =
|
||||
read_reg!(otg_global, regs.global(), GRXSTSR, EPNUM, BCNT, PKTSTS);
|
||||
match status {
|
||||
0x02 => {
|
||||
// OUT received
|
||||
ep_out |= 1 << epnum;
|
||||
}
|
||||
0x06 => {
|
||||
// SETUP received
|
||||
// flushing TX if something stuck in control endpoint
|
||||
let ep = regs.endpoint_in(epnum as usize);
|
||||
if read_reg!(endpoint_in, ep, DIEPTSIZ, PKTCNT) != 0 {
|
||||
modify_reg!(otg_global, regs.global(), GRSTCTL, TXFNUM: epnum, TXFFLSH: 1);
|
||||
while read_reg!(otg_global, regs.global(), GRSTCTL, TXFFLSH) == 1 {}
|
||||
}
|
||||
ep_setup |= 1 << epnum;
|
||||
}
|
||||
0x03 | 0x04 => {
|
||||
// OUT completed | SETUP completed
|
||||
// Re-enable the endpoint, F429-like chips only
|
||||
if core_id == 0x4f54_400a
|
||||
|| core_id == 0x0000_1200
|
||||
|| core_id == 0x0000_1100
|
||||
{
|
||||
let ep = regs.endpoint_out(epnum as usize);
|
||||
modify_reg!(endpoint_out, ep, DOEPCTL, CNAK: 1, EPENA: 1);
|
||||
}
|
||||
read_reg!(otg_global, regs.global(), GRXSTSP); // pop GRXSTSP
|
||||
}
|
||||
_ => {
|
||||
read_reg!(otg_global, regs.global(), GRXSTSP); // pop GRXSTSP
|
||||
}
|
||||
}
|
||||
|
||||
if status == 0x02 || status == 0x06 {
|
||||
if let Some(ep) = &self.allocator.endpoints_out[epnum as usize] {
|
||||
let mut buffer = ep.buffer.borrow_ref_mut(cs);
|
||||
if buffer.state() == EndpointBufferState::Empty {
|
||||
read_reg!(otg_global, regs.global(), GRXSTSP); // pop GRXSTSP
|
||||
|
||||
let is_setup = status == 0x06;
|
||||
buffer
|
||||
.fill_from_fifo(*regs, data_size as u16, is_setup)
|
||||
.ok();
|
||||
|
||||
// Re-enable the endpoint, F446-like chips only
|
||||
if core_id == 0x4f54_400a
|
||||
|| core_id == 0x0000_2000
|
||||
|| core_id == 0x0000_2100
|
||||
|| core_id == 0x0000_2300
|
||||
|| core_id == 0x0000_3000
|
||||
|| core_id == 0x0000_3100
|
||||
{
|
||||
let ep = regs.endpoint_out(epnum as usize);
|
||||
modify_reg!(endpoint_out, ep, DOEPCTL, CNAK: 1, EPENA: 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if iep != 0 {
|
||||
for ep in &self.allocator.endpoints_in {
|
||||
if let Some(ep) = ep {
|
||||
let ep_regs = regs.endpoint_in(ep.address().index());
|
||||
if read_reg!(endpoint_in, ep_regs, DIEPINT, XFRC) != 0 {
|
||||
write_reg!(endpoint_in, ep_regs, DIEPINT, XFRC: 1);
|
||||
ep_in_complete |= 1 << ep.address().index();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ep in &self.allocator.endpoints_out {
|
||||
if let Some(ep) = ep {
|
||||
match ep.buffer_state() {
|
||||
EndpointBufferState::DataOut => {
|
||||
ep_out |= 1 << ep.address().index();
|
||||
}
|
||||
EndpointBufferState::DataSetup => {
|
||||
ep_setup |= 1 << ep.address().index();
|
||||
}
|
||||
EndpointBufferState::Empty => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ep_in_complete | ep_out | ep_setup) != 0 {
|
||||
PollResult::Data {
|
||||
ep_out,
|
||||
ep_in_complete,
|
||||
ep_setup,
|
||||
}
|
||||
} else {
|
||||
PollResult::None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = true;
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
use crate::endpoint_memory::{EndpointBuffer, EndpointBufferState};
|
||||
use crate::ral::{
|
||||
endpoint0_out, endpoint_in, endpoint_out, modify_reg, otg_device, read_reg, write_reg,
|
||||
};
|
||||
use crate::target::{fifo_write, UsbRegisters};
|
||||
use crate::transition::EndpointDescriptor;
|
||||
use crate::UsbPeripheral;
|
||||
use core::cell::RefCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use usb_device::endpoint::{EndpointAddress, EndpointType};
|
||||
use usb_device::{Result, UsbDirection, UsbError};
|
||||
|
||||
pub fn set_stalled(usb: UsbRegisters, address: EndpointAddress, stalled: bool) {
|
||||
critical_section::with(|_| match address.direction() {
|
||||
UsbDirection::Out => {
|
||||
let ep = usb.endpoint_out(address.index() as usize);
|
||||
modify_reg!(endpoint_out, ep, DOEPCTL, STALL: stalled as u32);
|
||||
}
|
||||
UsbDirection::In => {
|
||||
let ep = usb.endpoint_in(address.index() as usize);
|
||||
modify_reg!(endpoint_in, ep, DIEPCTL, STALL: stalled as u32);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_stalled(usb: UsbRegisters, address: EndpointAddress) -> bool {
|
||||
let stall = match address.direction() {
|
||||
UsbDirection::Out => {
|
||||
let ep = usb.endpoint_out(address.index());
|
||||
read_reg!(endpoint_out, ep, DOEPCTL, STALL)
|
||||
}
|
||||
UsbDirection::In => {
|
||||
let ep = usb.endpoint_in(address.index());
|
||||
read_reg!(endpoint_in, ep, DIEPCTL, STALL)
|
||||
}
|
||||
};
|
||||
stall != 0
|
||||
}
|
||||
|
||||
/// Arbitrates access to the endpoint-specific registers and packet buffer memory.
|
||||
pub struct Endpoint {
|
||||
descriptor: EndpointDescriptor,
|
||||
usb: UsbRegisters,
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
pub fn new<USB: UsbPeripheral>(descriptor: EndpointDescriptor) -> Endpoint {
|
||||
Endpoint {
|
||||
descriptor,
|
||||
usb: UsbRegisters::new::<USB>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> EndpointAddress {
|
||||
self.descriptor.address
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self) -> u8 {
|
||||
self.descriptor.address.index() as u8
|
||||
}
|
||||
|
||||
pub fn ep_type(&self) -> EndpointType {
|
||||
self.descriptor.ep_type
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndpointIn {
|
||||
common: Endpoint,
|
||||
}
|
||||
|
||||
impl EndpointIn {
|
||||
pub fn new<USB: UsbPeripheral>(descriptor: EndpointDescriptor) -> EndpointIn {
|
||||
EndpointIn {
|
||||
common: Endpoint::new::<USB>(descriptor),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(&self, _cs: CriticalSection<'_>) {
|
||||
if self.index() == 0 {
|
||||
let mpsiz = match self.descriptor.max_packet_size {
|
||||
8 => 0b11,
|
||||
16 => 0b10,
|
||||
32 => 0b01,
|
||||
64 => 0b00,
|
||||
other => panic!("Unsupported EP0 size: {}", other),
|
||||
};
|
||||
|
||||
let regs = self.usb.endpoint_in(self.index() as usize);
|
||||
write_reg!(endpoint_in, regs, DIEPCTL, MPSIZ: mpsiz as u32, SNAK: 1);
|
||||
write_reg!(endpoint_in, regs, DIEPTSIZ, PKTCNT: 0, XFRSIZ: self.descriptor.max_packet_size as u32);
|
||||
} else {
|
||||
let regs = self.usb.endpoint_in(self.index() as usize);
|
||||
write_reg!(endpoint_in, regs, DIEPCTL,
|
||||
SNAK: 1,
|
||||
USBAEP: 1,
|
||||
EPTYP: self.descriptor.ep_type.to_bm_attributes() as u32,
|
||||
SD0PID_SEVNFRM: 1,
|
||||
TXFNUM: self.index() as u32,
|
||||
MPSIZ: self.descriptor.max_packet_size as u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deconfigure(&self, _cs: CriticalSection<'_>) {
|
||||
let regs = self.usb.endpoint_in(self.index() as usize);
|
||||
|
||||
// deactivating endpoint
|
||||
modify_reg!(endpoint_in, regs, DIEPCTL, USBAEP: 0);
|
||||
|
||||
// TODO: flushing FIFO
|
||||
|
||||
// disabling endpoint
|
||||
if read_reg!(endpoint_in, regs, DIEPCTL, EPENA) != 0 && self.index() != 0 {
|
||||
modify_reg!(endpoint_in, regs, DIEPCTL, EPDIS: 1)
|
||||
}
|
||||
|
||||
// clean EP interrupts
|
||||
write_reg!(endpoint_in, regs, DIEPINT, 0xff);
|
||||
|
||||
// TODO: deconfiguring TX FIFO
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<()> {
|
||||
let ep = self.usb.endpoint_in(self.index() as usize);
|
||||
let device = self.usb.device();
|
||||
if self.index() != 0 && read_reg!(endpoint_in, ep, DIEPCTL, EPENA) != 0 {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
|
||||
if buf.len() > self.descriptor.max_packet_size as usize {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
|
||||
if !buf.is_empty() {
|
||||
// Check for FIFO free space
|
||||
let size_words = (buf.len() + 3) / 4;
|
||||
if size_words > read_reg!(endpoint_in, ep, DTXFSTS, INEPTFSAV) as usize {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
}
|
||||
|
||||
write_reg!(endpoint_in, ep, DIEPTSIZ, MCNT: 1, PKTCNT: 1, XFRSIZ: buf.len() as u32);
|
||||
|
||||
match self.descriptor.ep_type {
|
||||
// Isochronous endpoints must set the correct even/odd frame bit to
|
||||
// correspond with the next frame's number.
|
||||
EndpointType::Isochronous { .. } => {
|
||||
// Previous frame number is OTG_DSTS.FNSOF
|
||||
let frame_number = read_reg!(otg_device, device, DSTS, FNSOF);
|
||||
if frame_number & 0x1 == 1 {
|
||||
// Previous frame number is odd, so upcoming frame is even
|
||||
modify_reg!(endpoint_in, ep, DIEPCTL, SD0PID_SEVNFRM: 1);
|
||||
} else {
|
||||
// Previous frame number is even, so upcoming frame is odd
|
||||
#[cfg(feature = "fs")]
|
||||
modify_reg!(endpoint_in, ep, DIEPCTL, SODDFRM_SD1PID: 1);
|
||||
#[cfg(feature = "hs")]
|
||||
modify_reg!(endpoint_in, ep, DIEPCTL, SODDFRM: 1);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
modify_reg!(endpoint_in, ep, DIEPCTL, CNAK: 1, EPENA: 1);
|
||||
|
||||
fifo_write(self.usb, self.index(), buf);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndpointOut {
|
||||
common: Endpoint,
|
||||
pub(crate) buffer: Mutex<RefCell<EndpointBuffer>>,
|
||||
}
|
||||
|
||||
impl EndpointOut {
|
||||
pub fn new<USB: UsbPeripheral>(
|
||||
descriptor: EndpointDescriptor,
|
||||
buffer: EndpointBuffer,
|
||||
) -> EndpointOut {
|
||||
EndpointOut {
|
||||
common: Endpoint::new::<USB>(descriptor),
|
||||
buffer: Mutex::new(RefCell::new(buffer)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(&self, _cs: CriticalSection<'_>) {
|
||||
if self.index() == 0 {
|
||||
let mpsiz = match self.descriptor.max_packet_size {
|
||||
8 => 0b11,
|
||||
16 => 0b10,
|
||||
32 => 0b01,
|
||||
64 => 0b00,
|
||||
other => panic!("Unsupported EP0 size: {}", other),
|
||||
};
|
||||
|
||||
let regs = self.usb.endpoint0_out();
|
||||
write_reg!(endpoint0_out, regs, DOEPTSIZ0, STUPCNT: 1, PKTCNT: 1, XFRSIZ: self.descriptor.max_packet_size as u32);
|
||||
modify_reg!(endpoint0_out, regs, DOEPCTL0, MPSIZ: mpsiz as u32, EPENA: 1, CNAK: 1);
|
||||
} else {
|
||||
let regs = self.usb.endpoint_out(self.index() as usize);
|
||||
write_reg!(endpoint_out, regs, DOEPCTL,
|
||||
SD0PID_SEVNFRM: 1,
|
||||
CNAK: 1,
|
||||
EPENA: 1,
|
||||
USBAEP: 1,
|
||||
EPTYP: self.descriptor.ep_type.to_bm_attributes() as u32,
|
||||
MPSIZ: self.descriptor.max_packet_size as u32
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deconfigure(&self, _cs: CriticalSection<'_>) {
|
||||
let regs = self.usb.endpoint_out(self.index() as usize);
|
||||
|
||||
// deactivating endpoint
|
||||
modify_reg!(endpoint_out, regs, DOEPCTL, USBAEP: 0);
|
||||
|
||||
// disabling endpoint
|
||||
if read_reg!(endpoint_out, regs, DOEPCTL, EPENA) != 0 && self.index() != 0 {
|
||||
modify_reg!(endpoint_out, regs, DOEPCTL, EPDIS: 1)
|
||||
}
|
||||
|
||||
// clean EP interrupts
|
||||
write_reg!(endpoint_out, regs, DOEPINT, 0xff);
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let ep = self.usb.endpoint_out(self.index() as usize);
|
||||
let device = self.usb.device();
|
||||
|
||||
match self.descriptor.ep_type {
|
||||
// Isochronous endpoints must set the correct even/odd frame bit to
|
||||
// correspond with the next frame's number.
|
||||
EndpointType::Isochronous { .. } => {
|
||||
// Previous frame number is OTG_DSTS.FNSOF
|
||||
let frame_number = read_reg!(otg_device, device, DSTS, FNSOF);
|
||||
if frame_number & 0x1 == 1 {
|
||||
// Previous frame number is odd, so upcoming frame is even
|
||||
modify_reg!(endpoint_out, ep, DOEPCTL, SD0PID_SEVNFRM: 1);
|
||||
} else {
|
||||
// Previous frame number is even, so upcoming frame is odd
|
||||
modify_reg!(endpoint_out, ep, DOEPCTL, SODDFRM: 1);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
critical_section::with(|cs| self.buffer.borrow_ref_mut(cs).read_packet(buf))
|
||||
}
|
||||
|
||||
pub fn buffer_state(&self) -> EndpointBufferState {
|
||||
critical_section::with(|cs| self.buffer.borrow_ref(cs).state())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EndpointIn {
|
||||
type Target = Endpoint;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for EndpointIn {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.common
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for EndpointOut {
|
||||
type Target = Endpoint;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.common
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for EndpointOut {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.common
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
#![allow(dead_code)]
|
||||
use crate::target::{fifo_read_into, UsbRegisters};
|
||||
use crate::UsbPeripheral;
|
||||
use core::marker::PhantomData;
|
||||
use core::slice;
|
||||
use usb_device::{Result, UsbError};
|
||||
use vcell::VolatileCell;
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum EndpointBufferState {
|
||||
Empty,
|
||||
DataOut,
|
||||
DataSetup,
|
||||
}
|
||||
|
||||
pub struct EndpointBuffer {
|
||||
buffer: &'static mut [VolatileCell<u32>],
|
||||
data_size: u16,
|
||||
has_data: bool,
|
||||
is_setup: bool,
|
||||
}
|
||||
|
||||
impl EndpointBuffer {
|
||||
pub fn new(buffer: &'static mut [u32]) -> Self {
|
||||
Self {
|
||||
buffer: unsafe { &mut *(buffer as *mut [u32] as *mut [VolatileCell<u32>]) },
|
||||
data_size: 0,
|
||||
has_data: false,
|
||||
is_setup: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_packet(&mut self, mut buf: &mut [u8]) -> Result<usize> {
|
||||
if !self.has_data {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
|
||||
let data_size = self.data_size as usize;
|
||||
|
||||
if buf.len() < data_size {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
|
||||
let mut index = 0;
|
||||
let mut current_size = data_size;
|
||||
while current_size >= 4 {
|
||||
let word = self.buffer[index].get();
|
||||
index += 1;
|
||||
|
||||
let bytes = word.to_ne_bytes();
|
||||
buf[..4].copy_from_slice(&bytes);
|
||||
buf = &mut buf[4..];
|
||||
|
||||
current_size -= 4;
|
||||
}
|
||||
if current_size > 0 {
|
||||
let word = self.buffer[index].get();
|
||||
let bytes = word.to_ne_bytes();
|
||||
buf[..current_size].copy_from_slice(&bytes[..current_size]);
|
||||
}
|
||||
|
||||
self.has_data = false;
|
||||
|
||||
Ok(data_size)
|
||||
}
|
||||
|
||||
pub fn fill_from_fifo(
|
||||
&mut self,
|
||||
usb: UsbRegisters,
|
||||
data_size: u16,
|
||||
is_setup: bool,
|
||||
) -> Result<()> {
|
||||
if self.has_data {
|
||||
return Err(UsbError::WouldBlock);
|
||||
}
|
||||
|
||||
if data_size as usize > self.capacity() {
|
||||
return Err(UsbError::BufferOverflow);
|
||||
}
|
||||
|
||||
let words = (data_size as usize + 3) / 4;
|
||||
fifo_read_into(usb, &self.buffer[..words]);
|
||||
|
||||
self.is_setup = is_setup;
|
||||
self.data_size = data_size;
|
||||
self.has_data = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn state(&self) -> EndpointBufferState {
|
||||
if self.has_data {
|
||||
if self.is_setup {
|
||||
EndpointBufferState::DataSetup
|
||||
} else {
|
||||
EndpointBufferState::DataOut
|
||||
}
|
||||
} else {
|
||||
EndpointBufferState::Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.buffer.len() * 4
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EndpointBuffer {
|
||||
fn default() -> Self {
|
||||
EndpointBuffer::new(&mut [])
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EndpointMemoryAllocator<USB> {
|
||||
next_free_offset: usize,
|
||||
max_size_words: usize,
|
||||
memory: &'static mut [u32],
|
||||
tx_fifo_size_words: [u16; 9],
|
||||
_marker: PhantomData<USB>,
|
||||
}
|
||||
|
||||
impl<USB: UsbPeripheral> EndpointMemoryAllocator<USB> {
|
||||
pub fn new(memory: &'static mut [u32]) -> Self {
|
||||
Self {
|
||||
next_free_offset: 0,
|
||||
max_size_words: 0,
|
||||
memory,
|
||||
tx_fifo_size_words: [0; 9],
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate_rx_buffer(&mut self, size: usize) -> Result<EndpointBuffer> {
|
||||
let size_words = (size + 3) / 4;
|
||||
|
||||
let offset = self.next_free_offset;
|
||||
if offset + size_words > self.memory.len() {
|
||||
return Err(UsbError::EndpointMemoryOverflow);
|
||||
}
|
||||
|
||||
self.next_free_offset += size_words;
|
||||
self.max_size_words = core::cmp::max(self.max_size_words, size_words);
|
||||
|
||||
let buffer = unsafe {
|
||||
let ptr = self.memory.as_mut_ptr().add(offset);
|
||||
slice::from_raw_parts_mut(ptr, size_words)
|
||||
};
|
||||
Ok(EndpointBuffer::new(buffer))
|
||||
}
|
||||
|
||||
pub fn allocate_tx_buffer(&mut self, ep_number: u8, size: usize) -> Result<()> {
|
||||
let ep_number = ep_number as usize;
|
||||
assert!(ep_number < self.tx_fifo_size_words.len());
|
||||
|
||||
if self.tx_fifo_size_words[ep_number] != 0 {
|
||||
return Err(UsbError::InvalidEndpoint);
|
||||
}
|
||||
|
||||
let used = 30
|
||||
+ self.total_rx_buffer_size_words() as usize
|
||||
+ self.tx_fifo_size_words.iter().sum::<u16>() as usize;
|
||||
|
||||
let size_words = core::cmp::max((size + 3) / 4, 16);
|
||||
if (used + size_words) > USB::FIFO_DEPTH_WORDS {
|
||||
return Err(UsbError::EndpointMemoryOverflow);
|
||||
}
|
||||
|
||||
self.tx_fifo_size_words[ep_number] = size_words as u16;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the size of memory allocated for OUT endpoints in words
|
||||
pub fn total_rx_buffer_size_words(&self) -> u16 {
|
||||
self.next_free_offset as u16
|
||||
}
|
||||
|
||||
pub fn tx_fifo_size_words(&self, ep_number: usize) -> u16 {
|
||||
self.tx_fifo_size_words[ep_number]
|
||||
}
|
||||
|
||||
pub fn max_buffer_size_words(&self) -> usize {
|
||||
self.max_size_words
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
//! USB peripheral driver for Synopsys USB OTG peripherals.
|
||||
|
||||
#![no_std]
|
||||
|
||||
#[cfg(all(feature = "fs", feature = "hs"))]
|
||||
compile_error!("choose only one USB mode");
|
||||
|
||||
#[cfg(not(any(feature = "fs", feature = "hs")))]
|
||||
compile_error!("select USB mode feature (fs/hs)");
|
||||
|
||||
mod endpoint;
|
||||
mod endpoint_memory;
|
||||
|
||||
mod target;
|
||||
|
||||
/// USB peripheral driver.
|
||||
pub mod bus;
|
||||
|
||||
pub use crate::bus::UsbBus;
|
||||
|
||||
mod ral;
|
||||
mod transition;
|
||||
|
||||
/// A trait for device-specific USB peripherals. Implement this to add support for a new hardware
|
||||
/// platform. Peripherals that have this trait must have the same register block as STM32 USB OTG
|
||||
/// peripherals.
|
||||
pub unsafe trait UsbPeripheral: Send + Sync {
|
||||
/// Pointer to the register block
|
||||
const REGISTERS: *const ();
|
||||
|
||||
/// true for High Speed variants of the peripheral, false for Full Speed
|
||||
const HIGH_SPEED: bool;
|
||||
|
||||
/// FIFO size in 32-bit words
|
||||
const FIFO_DEPTH_WORDS: usize;
|
||||
|
||||
/// Number of (bidirectional) endpoints
|
||||
const ENDPOINT_COUNT: usize;
|
||||
|
||||
/// Enables USB device on its peripheral bus
|
||||
fn enable();
|
||||
|
||||
/// AHB frequency in hertz
|
||||
fn ahb_frequency_hz(&self) -> u32;
|
||||
|
||||
/// Returns PHY type that should be used for USB peripheral
|
||||
fn phy_type(&self) -> PhyType {
|
||||
PhyType::InternalFullSpeed
|
||||
}
|
||||
|
||||
/// Performs initial setup of the internal high-speed PHY
|
||||
///
|
||||
/// This function should turn on LDO and PLL and wait for PHY clock to become stable.
|
||||
fn setup_internal_hs_phy(&self) {}
|
||||
}
|
||||
|
||||
/// USB PHY type
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum PhyType {
|
||||
/// Internal Full-Speed PHY
|
||||
///
|
||||
/// Available on most High-Speed peripherals.
|
||||
InternalFullSpeed,
|
||||
/// Internal High-Speed PHY
|
||||
///
|
||||
/// Available on a few STM32 chips.
|
||||
InternalHighSpeed,
|
||||
/// External ULPI High-Speed PHY
|
||||
ExternalHighSpeed,
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#![allow(unused)]
|
||||
pub mod otg_fs_device_f401_f405_f407_f411_f427_f429;
|
||||
pub mod otg_fs_global_f401_f405_f407_f411_f427_f429;
|
||||
pub mod otg_fs_host;
|
||||
pub mod otg_hs_device;
|
||||
pub mod otg_hs_global;
|
||||
pub mod otg_hs_host;
|
||||
pub mod otg_s_pwrclk;
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go full speed
|
||||
//!
|
||||
//! Used by: stm32f401, stm32f405, stm32f407, stm32f411, stm32f427, stm32f429
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_fs_device_v1::Instance;
|
||||
pub use super::super::peripherals::otg_fs_device_v1::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_fs_device_v1::{
|
||||
DAINT, DAINTMSK, DCFG, DCTL, DIEPCTL0, DIEPCTL1, DIEPCTL2, DIEPCTL3, DIEPEMPMSK, DIEPINT0,
|
||||
DIEPINT1, DIEPINT2, DIEPINT3, DIEPMSK, DIEPTSIZ0, DIEPTSIZ1, DIEPTSIZ2, DIEPTSIZ3, DOEPCTL0,
|
||||
DOEPCTL1, DOEPCTL2, DOEPCTL3, DOEPINT0, DOEPINT1, DOEPINT2, DOEPINT3, DOEPMSK, DOEPTSIZ0,
|
||||
DOEPTSIZ1, DOEPTSIZ2, DOEPTSIZ3, DSTS, DTXFSTS0, DTXFSTS1, DTXFSTS2, DTXFSTS3, DVBUSDIS,
|
||||
DVBUSPULSE,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_FS_DEVICE peripheral instance
|
||||
pub mod OTG_FS_DEVICE {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x50000800,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_FS_DEVICE
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
DCFG: 0x02200000,
|
||||
DCTL: 0x00000000,
|
||||
DSTS: 0x00000010,
|
||||
DIEPMSK: 0x00000000,
|
||||
DOEPMSK: 0x00000000,
|
||||
DAINT: 0x00000000,
|
||||
DAINTMSK: 0x00000000,
|
||||
DVBUSDIS: 0x000017D7,
|
||||
DVBUSPULSE: 0x000005B8,
|
||||
DIEPEMPMSK: 0x00000000,
|
||||
DIEPCTL0: 0x00000000,
|
||||
DIEPCTL1: 0x00000000,
|
||||
DIEPCTL2: 0x00000000,
|
||||
DIEPCTL3: 0x00000000,
|
||||
DOEPCTL0: 0x00008000,
|
||||
DOEPCTL1: 0x00000000,
|
||||
DOEPCTL2: 0x00000000,
|
||||
DOEPCTL3: 0x00000000,
|
||||
DIEPINT0: 0x00000080,
|
||||
DIEPINT1: 0x00000080,
|
||||
DIEPINT2: 0x00000080,
|
||||
DIEPINT3: 0x00000080,
|
||||
DOEPINT0: 0x00000080,
|
||||
DOEPINT1: 0x00000080,
|
||||
DOEPINT2: 0x00000080,
|
||||
DOEPINT3: 0x00000080,
|
||||
DIEPTSIZ0: 0x00000000,
|
||||
DOEPTSIZ0: 0x00000000,
|
||||
DIEPTSIZ1: 0x00000000,
|
||||
DIEPTSIZ2: 0x00000000,
|
||||
DIEPTSIZ3: 0x00000000,
|
||||
DTXFSTS0: 0x00000000,
|
||||
DTXFSTS1: 0x00000000,
|
||||
DTXFSTS2: 0x00000000,
|
||||
DTXFSTS3: 0x00000000,
|
||||
DOEPTSIZ1: 0x00000000,
|
||||
DOEPTSIZ2: 0x00000000,
|
||||
DOEPTSIZ3: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_FS_DEVICE
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_FS_DEVICE: *const RegisterBlock = 0x50000800 as *const _;
|
||||
Vendored
+60
@@ -0,0 +1,60 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go full speed
|
||||
//!
|
||||
//! Used by: stm32f401, stm32f405, stm32f407, stm32f411, stm32f427, stm32f429
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_fs_global_v1::Instance;
|
||||
pub use super::super::peripherals::otg_fs_global_v1::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_fs_global_v1::{
|
||||
CID, DIEPTXF0, DIEPTXF1, DIEPTXF2, DIEPTXF3, GAHBCFG, GCCFG, GINTMSK, GINTSTS, GNPTXSTS,
|
||||
GOTGCTL, GOTGINT, GRSTCTL, GRXFSIZ, GRXSTSP, GRXSTSR, GUSBCFG, HPTXFSIZ,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_FS_GLOBAL peripheral instance
|
||||
pub mod OTG_FS_GLOBAL {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x50000000,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_FS_GLOBAL
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
GOTGCTL: 0x00000800,
|
||||
GOTGINT: 0x00000000,
|
||||
GAHBCFG: 0x00000000,
|
||||
GUSBCFG: 0x00000A00,
|
||||
GRSTCTL: 0x20000000,
|
||||
GINTSTS: 0x04000020,
|
||||
GINTMSK: 0x00000000,
|
||||
GRXSTSR: 0x00000000,
|
||||
GRXFSIZ: 0x00000200,
|
||||
DIEPTXF0: 0x00000200,
|
||||
GNPTXSTS: 0x00080200,
|
||||
GCCFG: 0x00000000,
|
||||
CID: 0x00001000,
|
||||
HPTXFSIZ: 0x02000600,
|
||||
DIEPTXF1: 0x02000400,
|
||||
DIEPTXF2: 0x02000400,
|
||||
DIEPTXF3: 0x02000400,
|
||||
GRXSTSP: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_FS_GLOBAL
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_FS_GLOBAL: *const RegisterBlock = 0x50000000 as *const _;
|
||||
@@ -0,0 +1,83 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go full speed
|
||||
//!
|
||||
//! Used by: stm32f401, stm32f405, stm32f407, stm32f411, stm32f412, stm32f413, stm32f427, stm32f429, stm32f446, stm32f469
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_fs_host::Instance;
|
||||
pub use super::super::peripherals::otg_fs_host::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_fs_host::{
|
||||
HAINT, HAINTMSK, HCCHAR0, HCCHAR1, HCCHAR2, HCCHAR3, HCCHAR4, HCCHAR5, HCCHAR6, HCCHAR7, HCFG,
|
||||
HCINT0, HCINT1, HCINT2, HCINT3, HCINT4, HCINT5, HCINT6, HCINT7, HCINTMSK0, HCINTMSK1,
|
||||
HCINTMSK2, HCINTMSK3, HCINTMSK4, HCINTMSK5, HCINTMSK6, HCINTMSK7, HCTSIZ0, HCTSIZ1, HCTSIZ2,
|
||||
HCTSIZ3, HCTSIZ4, HCTSIZ5, HCTSIZ6, HCTSIZ7, HFIR, HFNUM, HPRT, HPTXSTS,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_FS_HOST peripheral instance
|
||||
pub mod OTG_FS_HOST {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x50000400,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_FS_HOST
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
HCFG: 0x00000000,
|
||||
HFIR: 0x0000EA60,
|
||||
HFNUM: 0x00003FFF,
|
||||
HPTXSTS: 0x00080100,
|
||||
HAINT: 0x00000000,
|
||||
HAINTMSK: 0x00000000,
|
||||
HPRT: 0x00000000,
|
||||
HCCHAR0: 0x00000000,
|
||||
HCCHAR1: 0x00000000,
|
||||
HCCHAR2: 0x00000000,
|
||||
HCCHAR3: 0x00000000,
|
||||
HCCHAR4: 0x00000000,
|
||||
HCCHAR5: 0x00000000,
|
||||
HCCHAR6: 0x00000000,
|
||||
HCCHAR7: 0x00000000,
|
||||
HCINT0: 0x00000000,
|
||||
HCINT1: 0x00000000,
|
||||
HCINT2: 0x00000000,
|
||||
HCINT3: 0x00000000,
|
||||
HCINT4: 0x00000000,
|
||||
HCINT5: 0x00000000,
|
||||
HCINT6: 0x00000000,
|
||||
HCINT7: 0x00000000,
|
||||
HCINTMSK0: 0x00000000,
|
||||
HCINTMSK1: 0x00000000,
|
||||
HCINTMSK2: 0x00000000,
|
||||
HCINTMSK3: 0x00000000,
|
||||
HCINTMSK4: 0x00000000,
|
||||
HCINTMSK5: 0x00000000,
|
||||
HCINTMSK6: 0x00000000,
|
||||
HCINTMSK7: 0x00000000,
|
||||
HCTSIZ0: 0x00000000,
|
||||
HCTSIZ1: 0x00000000,
|
||||
HCTSIZ2: 0x00000000,
|
||||
HCTSIZ3: 0x00000000,
|
||||
HCTSIZ4: 0x00000000,
|
||||
HCTSIZ5: 0x00000000,
|
||||
HCTSIZ6: 0x00000000,
|
||||
HCTSIZ7: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_FS_HOST
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_FS_HOST: *const RegisterBlock = 0x50000400 as *const _;
|
||||
@@ -0,0 +1,109 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go high speed
|
||||
//!
|
||||
//! Used by: stm32f405, stm32f407, stm32f427, stm32f429, stm32f446
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_hs_device::Instance;
|
||||
pub use super::super::peripherals::otg_hs_device::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_hs_device::{
|
||||
DAINT, DAINTMSK, DCFG, DCTL, DEACHINT, DEACHINTMSK, DIEPCTL0, DIEPCTL1, DIEPCTL2, DIEPCTL3,
|
||||
DIEPCTL4, DIEPCTL5, DIEPDMA1, DIEPDMA2, DIEPDMA3, DIEPDMA4, DIEPDMA5, DIEPEACHMSK1, DIEPEMPMSK,
|
||||
DIEPINT0, DIEPINT1, DIEPINT2, DIEPINT3, DIEPINT4, DIEPINT5, DIEPMSK, DIEPTSIZ0, DIEPTSIZ1,
|
||||
DIEPTSIZ2, DIEPTSIZ3, DIEPTSIZ4, DIEPTSIZ5, DOEPCTL0, DOEPCTL1, DOEPCTL2, DOEPCTL3, DOEPCTL4,
|
||||
DOEPCTL5, DOEPEACHMSK1, DOEPINT0, DOEPINT1, DOEPINT2, DOEPINT3, DOEPINT4, DOEPINT5, DOEPMSK,
|
||||
DOEPTSIZ0, DOEPTSIZ1, DOEPTSIZ2, DOEPTSIZ3, DOEPTSIZ4, DOEPTSIZ5, DSTS, DTHRCTL, DTXFSTS0,
|
||||
DTXFSTS1, DTXFSTS2, DTXFSTS3, DTXFSTS4, DTXFSTS5, DVBUSDIS, DVBUSPULSE,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_HS_DEVICE peripheral instance
|
||||
pub mod OTG_HS_DEVICE {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x40040800,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_HS_DEVICE
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
DCFG: 0x02200000,
|
||||
DCTL: 0x00000000,
|
||||
DSTS: 0x00000010,
|
||||
DIEPMSK: 0x00000000,
|
||||
DOEPMSK: 0x00000000,
|
||||
DAINT: 0x00000000,
|
||||
DAINTMSK: 0x00000000,
|
||||
DVBUSDIS: 0x000017D7,
|
||||
DVBUSPULSE: 0x000005B8,
|
||||
DTHRCTL: 0x00000000,
|
||||
DIEPEMPMSK: 0x00000000,
|
||||
DEACHINT: 0x00000000,
|
||||
DEACHINTMSK: 0x00000000,
|
||||
DIEPEACHMSK1: 0x00000000,
|
||||
DOEPEACHMSK1: 0x00000000,
|
||||
DIEPCTL0: 0x00000000,
|
||||
DIEPCTL1: 0x00000000,
|
||||
DIEPCTL2: 0x00000000,
|
||||
DIEPCTL3: 0x00000000,
|
||||
DIEPCTL4: 0x00000000,
|
||||
DIEPCTL5: 0x00000000,
|
||||
DIEPINT0: 0x00000080,
|
||||
DIEPINT1: 0x00000080,
|
||||
DIEPINT2: 0x00000080,
|
||||
DIEPINT3: 0x00000080,
|
||||
DIEPINT4: 0x00000080,
|
||||
DIEPINT5: 0x00000080,
|
||||
DIEPTSIZ0: 0x00000000,
|
||||
DIEPDMA1: 0x00000000,
|
||||
DIEPDMA2: 0x00000000,
|
||||
DIEPDMA3: 0x00000000,
|
||||
DIEPDMA4: 0x00000000,
|
||||
DIEPDMA5: 0x00000000,
|
||||
DTXFSTS0: 0x00000000,
|
||||
DTXFSTS1: 0x00000000,
|
||||
DTXFSTS2: 0x00000000,
|
||||
DTXFSTS3: 0x00000000,
|
||||
DTXFSTS4: 0x00000000,
|
||||
DTXFSTS5: 0x00000000,
|
||||
DIEPTSIZ1: 0x00000000,
|
||||
DIEPTSIZ2: 0x00000000,
|
||||
DIEPTSIZ3: 0x00000000,
|
||||
DIEPTSIZ4: 0x00000000,
|
||||
DIEPTSIZ5: 0x00000000,
|
||||
DOEPCTL0: 0x00008000,
|
||||
DOEPCTL1: 0x00000000,
|
||||
DOEPCTL2: 0x00000000,
|
||||
DOEPCTL3: 0x00000000,
|
||||
DOEPCTL4: 0x00000000,
|
||||
DOEPCTL5: 0x00000000,
|
||||
DOEPINT0: 0x00000080,
|
||||
DOEPINT1: 0x00000080,
|
||||
DOEPINT2: 0x00000080,
|
||||
DOEPINT3: 0x00000080,
|
||||
DOEPINT4: 0x00000080,
|
||||
DOEPINT5: 0x00000080,
|
||||
DOEPTSIZ0: 0x00000000,
|
||||
DOEPTSIZ1: 0x00000000,
|
||||
DOEPTSIZ2: 0x00000000,
|
||||
DOEPTSIZ3: 0x00000000,
|
||||
DOEPTSIZ4: 0x00000000,
|
||||
DOEPTSIZ5: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_HS_DEVICE
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_HS_DEVICE: *const RegisterBlock = 0x40040800 as *const _;
|
||||
@@ -0,0 +1,64 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go high speed
|
||||
//!
|
||||
//! Used by: stm32f405, stm32f407, stm32f427, stm32f429, stm32f446
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_hs_global::Instance;
|
||||
pub use super::super::peripherals::otg_hs_global::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_hs_global::{
|
||||
CID, DIEPTXF1, DIEPTXF2, DIEPTXF3, DIEPTXF4, DIEPTXF5, GAHBCFG, GCCFG, GINTMSK, GINTSTS,
|
||||
GNPTXFSIZ, GNPTXSTS, GOTGCTL, GOTGINT, GRSTCTL, GRXFSIZ, GRXSTSP, GRXSTSR, GUSBCFG, HPTXFSIZ,
|
||||
PHYCR,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_HS_GLOBAL peripheral instance
|
||||
pub mod OTG_HS_GLOBAL {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x40040000,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_HS_GLOBAL
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
GOTGCTL: 0x00000800,
|
||||
GOTGINT: 0x00000000,
|
||||
GAHBCFG: 0x00000000,
|
||||
GUSBCFG: 0x00000A00,
|
||||
GRSTCTL: 0x20000000,
|
||||
GINTSTS: 0x04000020,
|
||||
GINTMSK: 0x00000000,
|
||||
GRXSTSR: 0x00000000,
|
||||
GRXSTSP: 0x00000000,
|
||||
GRXFSIZ: 0x00000200,
|
||||
GNPTXFSIZ: 0x00000200,
|
||||
GNPTXSTS: 0x00080200,
|
||||
PHYCR: 0x00000000,
|
||||
GCCFG: 0x00000000,
|
||||
CID: 0x00001200,
|
||||
HPTXFSIZ: 0x02000600,
|
||||
DIEPTXF1: 0x02000400,
|
||||
DIEPTXF2: 0x02000400,
|
||||
DIEPTXF3: 0x02000400,
|
||||
DIEPTXF4: 0x02000400,
|
||||
DIEPTXF5: 0x02000400,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_HS_GLOBAL
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_HS_GLOBAL: *const RegisterBlock = 0x40040000 as *const _;
|
||||
@@ -0,0 +1,127 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go high speed
|
||||
//!
|
||||
//! Used by: stm32f405, stm32f407, stm32f427, stm32f429, stm32f446, stm32f469
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_hs_host::Instance;
|
||||
pub use super::super::peripherals::otg_hs_host::{RegisterBlock, ResetValues};
|
||||
pub use super::super::peripherals::otg_hs_host::{
|
||||
HAINT, HAINTMSK, HCCHAR0, HCCHAR1, HCCHAR10, HCCHAR11, HCCHAR2, HCCHAR3, HCCHAR4, HCCHAR5,
|
||||
HCCHAR6, HCCHAR7, HCCHAR8, HCCHAR9, HCDMA0, HCDMA1, HCDMA10, HCDMA11, HCDMA2, HCDMA3, HCDMA4,
|
||||
HCDMA5, HCDMA6, HCDMA7, HCDMA8, HCDMA9, HCFG, HCINT0, HCINT1, HCINT10, HCINT11, HCINT2, HCINT3,
|
||||
HCINT4, HCINT5, HCINT6, HCINT7, HCINT8, HCINT9, HCINTMSK0, HCINTMSK1, HCINTMSK10, HCINTMSK11,
|
||||
HCINTMSK2, HCINTMSK3, HCINTMSK4, HCINTMSK5, HCINTMSK6, HCINTMSK7, HCINTMSK8, HCINTMSK9,
|
||||
HCSPLT0, HCSPLT1, HCSPLT10, HCSPLT11, HCSPLT2, HCSPLT3, HCSPLT4, HCSPLT5, HCSPLT6, HCSPLT7,
|
||||
HCSPLT8, HCSPLT9, HCTSIZ0, HCTSIZ1, HCTSIZ10, HCTSIZ11, HCTSIZ2, HCTSIZ3, HCTSIZ4, HCTSIZ5,
|
||||
HCTSIZ6, HCTSIZ7, HCTSIZ8, HCTSIZ9, HFIR, HFNUM, HPRT, HPTXSTS,
|
||||
};
|
||||
|
||||
/// Access functions for the OTG_HS_HOST peripheral instance
|
||||
pub mod OTG_HS_HOST {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x40040400,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_HS_HOST
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
HCFG: 0x00000000,
|
||||
HFIR: 0x0000EA60,
|
||||
HFNUM: 0x00003FFF,
|
||||
HPTXSTS: 0x00080100,
|
||||
HAINT: 0x00000000,
|
||||
HAINTMSK: 0x00000000,
|
||||
HPRT: 0x00000000,
|
||||
HCCHAR0: 0x00000000,
|
||||
HCCHAR1: 0x00000000,
|
||||
HCCHAR2: 0x00000000,
|
||||
HCCHAR3: 0x00000000,
|
||||
HCCHAR4: 0x00000000,
|
||||
HCCHAR5: 0x00000000,
|
||||
HCCHAR6: 0x00000000,
|
||||
HCCHAR7: 0x00000000,
|
||||
HCCHAR8: 0x00000000,
|
||||
HCCHAR9: 0x00000000,
|
||||
HCCHAR10: 0x00000000,
|
||||
HCCHAR11: 0x00000000,
|
||||
HCSPLT0: 0x00000000,
|
||||
HCSPLT1: 0x00000000,
|
||||
HCSPLT2: 0x00000000,
|
||||
HCSPLT3: 0x00000000,
|
||||
HCSPLT4: 0x00000000,
|
||||
HCSPLT5: 0x00000000,
|
||||
HCSPLT6: 0x00000000,
|
||||
HCSPLT7: 0x00000000,
|
||||
HCSPLT8: 0x00000000,
|
||||
HCSPLT9: 0x00000000,
|
||||
HCSPLT10: 0x00000000,
|
||||
HCSPLT11: 0x00000000,
|
||||
HCINT0: 0x00000000,
|
||||
HCINT1: 0x00000000,
|
||||
HCINT2: 0x00000000,
|
||||
HCINT3: 0x00000000,
|
||||
HCINT4: 0x00000000,
|
||||
HCINT5: 0x00000000,
|
||||
HCINT6: 0x00000000,
|
||||
HCINT7: 0x00000000,
|
||||
HCINT8: 0x00000000,
|
||||
HCINT9: 0x00000000,
|
||||
HCINT10: 0x00000000,
|
||||
HCINT11: 0x00000000,
|
||||
HCINTMSK0: 0x00000000,
|
||||
HCINTMSK1: 0x00000000,
|
||||
HCINTMSK2: 0x00000000,
|
||||
HCINTMSK3: 0x00000000,
|
||||
HCINTMSK4: 0x00000000,
|
||||
HCINTMSK5: 0x00000000,
|
||||
HCINTMSK6: 0x00000000,
|
||||
HCINTMSK7: 0x00000000,
|
||||
HCINTMSK8: 0x00000000,
|
||||
HCINTMSK9: 0x00000000,
|
||||
HCINTMSK10: 0x00000000,
|
||||
HCINTMSK11: 0x00000000,
|
||||
HCTSIZ0: 0x00000000,
|
||||
HCTSIZ1: 0x00000000,
|
||||
HCTSIZ2: 0x00000000,
|
||||
HCTSIZ3: 0x00000000,
|
||||
HCTSIZ4: 0x00000000,
|
||||
HCTSIZ5: 0x00000000,
|
||||
HCTSIZ6: 0x00000000,
|
||||
HCTSIZ7: 0x00000000,
|
||||
HCTSIZ8: 0x00000000,
|
||||
HCTSIZ9: 0x00000000,
|
||||
HCTSIZ10: 0x00000000,
|
||||
HCTSIZ11: 0x00000000,
|
||||
HCDMA0: 0x00000000,
|
||||
HCDMA1: 0x00000000,
|
||||
HCDMA2: 0x00000000,
|
||||
HCDMA3: 0x00000000,
|
||||
HCDMA4: 0x00000000,
|
||||
HCDMA5: 0x00000000,
|
||||
HCDMA6: 0x00000000,
|
||||
HCDMA7: 0x00000000,
|
||||
HCDMA8: 0x00000000,
|
||||
HCDMA9: 0x00000000,
|
||||
HCDMA10: 0x00000000,
|
||||
HCDMA11: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_HS_HOST
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_HS_HOST: *const RegisterBlock = 0x40040400 as *const _;
|
||||
@@ -0,0 +1,70 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go full speed
|
||||
//!
|
||||
//! Used by: stm32f405, stm32f407, stm32f427, stm32f429, stm32f446
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub use super::super::peripherals::otg_fs_pwrclk::Instance;
|
||||
pub use super::super::peripherals::otg_fs_pwrclk::PCGCCTL;
|
||||
pub use super::super::peripherals::otg_fs_pwrclk::{RegisterBlock, ResetValues};
|
||||
|
||||
/// Access functions for the OTG_FS_PWRCLK peripheral instance
|
||||
pub mod OTG_FS_PWRCLK {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x50000e00,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_FS_PWRCLK
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
PCGCCTL: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_FS_PWRCLK
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_FS_PWRCLK: *const RegisterBlock = 0x50000e00 as *const _;
|
||||
|
||||
/// Access functions for the OTG_HS_PWRCLK peripheral instance
|
||||
pub mod OTG_HS_PWRCLK {
|
||||
use super::ResetValues;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use super::Instance;
|
||||
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
const INSTANCE: Instance = Instance {
|
||||
addr: 0x40040e00,
|
||||
_marker: ::core::marker::PhantomData,
|
||||
};
|
||||
|
||||
/// Reset values for each field in OTG_HS_PWRCLK
|
||||
pub const reset: ResetValues = ResetValues {
|
||||
PCGCCTL: 0x00000000,
|
||||
};
|
||||
}
|
||||
|
||||
/// Raw pointer to OTG_HS_PWRCLK
|
||||
///
|
||||
/// Dereferencing this is unsafe because you are not ensured unique
|
||||
/// access to the peripheral, so you may encounter data races with
|
||||
/// other users of this peripheral. It is up to you to ensure you
|
||||
/// will not cause data races.
|
||||
///
|
||||
/// This constant is provided for ease of use in unsafe code: you can
|
||||
/// simply call for example `write_reg!(gpio, GPIOA, ODR, 1);`.
|
||||
pub const OTG_HS_PWRCLK: *const RegisterBlock = 0x40040e00 as *const _;
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
pub mod instances;
|
||||
pub mod peripherals;
|
||||
pub mod stm32f429;
|
||||
|
||||
pub use ral_registers as register;
|
||||
pub use ral_registers::{modify_reg, read_reg, write_reg};
|
||||
|
||||
pub mod otg_global {
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_global::OTG_FS_GLOBAL as OTG_GLOBAL;
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_global::*;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_global::OTG_HS_GLOBAL as OTG_GLOBAL;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_global::*;
|
||||
}
|
||||
|
||||
pub mod otg_device {
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_device::OTG_FS_DEVICE as OTG_DEVICE;
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_device::*;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_device::OTG_HS_DEVICE as OTG_DEVICE;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_device::*;
|
||||
}
|
||||
|
||||
pub mod otg_pwrclk {
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_s_pwrclk::OTG_FS_PWRCLK as OTG_PWRCLK;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_s_pwrclk::OTG_HS_PWRCLK as OTG_PWRCLK;
|
||||
pub use super::stm32f429::otg_s_pwrclk::*;
|
||||
}
|
||||
|
||||
pub mod otg_global_dieptxfx {
|
||||
use super::register::RWRegister;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_global::DIEPTXF1 as DIEPTXFx;
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_fs_global::DIEPTXF1 as DIEPTXFx;
|
||||
|
||||
pub struct RegisterBlock {
|
||||
pub DIEPTXFx: RWRegister<u32>,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod endpoint_in {
|
||||
use super::register::RWRegister;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_device::{
|
||||
DIEPCTL1 as DIEPCTL, DIEPINT1 as DIEPINT, DIEPTSIZ1 as DIEPTSIZ, DTXFSTS1 as DTXFSTS,
|
||||
};
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_device::{
|
||||
DIEPCTL1 as DIEPCTL, DIEPINT1 as DIEPINT, DIEPTSIZ1 as DIEPTSIZ, DTXFSTS1 as DTXFSTS,
|
||||
};
|
||||
|
||||
pub struct RegisterBlock {
|
||||
pub DIEPCTL: RWRegister<u32>,
|
||||
_reserved0: u32,
|
||||
pub DIEPINT: RWRegister<u32>,
|
||||
_reserved1: u32,
|
||||
pub DIEPTSIZ: RWRegister<u32>,
|
||||
_reserved2: u32,
|
||||
pub DTXFSTS: RWRegister<u32>,
|
||||
_reserved3: u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub mod endpoint0_out {
|
||||
use super::register::RWRegister;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_device::{DOEPCTL0, DOEPINT0, DOEPTSIZ0};
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_device::{DOEPCTL0, DOEPINT0, DOEPTSIZ0};
|
||||
|
||||
pub struct RegisterBlock {
|
||||
pub DOEPCTL0: RWRegister<u32>,
|
||||
_reserved0: u32,
|
||||
pub DOEPINT0: RWRegister<u32>,
|
||||
_reserved1: u32,
|
||||
pub DOEPTSIZ0: RWRegister<u32>,
|
||||
_reserved2: [u32; 3],
|
||||
}
|
||||
}
|
||||
|
||||
pub mod endpoint_out {
|
||||
use super::register::RWRegister;
|
||||
|
||||
#[cfg(feature = "fs")]
|
||||
pub use super::stm32f429::otg_fs_device::{
|
||||
DOEPCTL1 as DOEPCTL, DOEPINT1 as DOEPINT, DOEPTSIZ1 as DOEPTSIZ,
|
||||
};
|
||||
|
||||
#[cfg(feature = "hs")]
|
||||
pub use super::stm32f429::otg_hs_device::{
|
||||
DOEPCTL1 as DOEPCTL, DOEPINT1 as DOEPINT, DOEPTSIZ1 as DOEPTSIZ,
|
||||
};
|
||||
|
||||
pub struct RegisterBlock {
|
||||
pub DOEPCTL: RWRegister<u32>,
|
||||
_reserved0: u32,
|
||||
pub DOEPINT: RWRegister<u32>,
|
||||
_reserved1: u32,
|
||||
pub DOEPTSIZ: RWRegister<u32>,
|
||||
_reserved2: [u32; 3],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
#![allow(unused)]
|
||||
pub mod otg_fs_device_v1;
|
||||
pub mod otg_fs_global_v1;
|
||||
pub mod otg_fs_host;
|
||||
pub mod otg_fs_pwrclk;
|
||||
pub mod otg_hs_device;
|
||||
pub mod otg_hs_global;
|
||||
pub mod otg_hs_host;
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,78 @@
|
||||
#![allow(non_snake_case, non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
//! USB on the go full speed
|
||||
//!
|
||||
//! Used by: stm32f401, stm32f405, stm32f407, stm32f411, stm32f412, stm32f413, stm32f427, stm32f429, stm32f446, stm32f469
|
||||
|
||||
use super::super::register::RWRegister;
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
use core::marker::PhantomData;
|
||||
|
||||
/// OTG_FS power and clock gating control register
|
||||
pub mod PCGCCTL {
|
||||
|
||||
/// Stop PHY clock
|
||||
pub mod STPPCLK {
|
||||
/// Offset (0 bits)
|
||||
pub const offset: u32 = 0;
|
||||
/// Mask (1 bit: 1 << 0)
|
||||
pub const mask: u32 = 1 << offset;
|
||||
/// Read-only values (empty)
|
||||
pub mod R {}
|
||||
/// Write-only values (empty)
|
||||
pub mod W {}
|
||||
/// Read-write values (empty)
|
||||
pub mod RW {}
|
||||
}
|
||||
|
||||
/// Gate HCLK
|
||||
pub mod GATEHCLK {
|
||||
/// Offset (1 bits)
|
||||
pub const offset: u32 = 1;
|
||||
/// Mask (1 bit: 1 << 1)
|
||||
pub const mask: u32 = 1 << offset;
|
||||
/// Read-only values (empty)
|
||||
pub mod R {}
|
||||
/// Write-only values (empty)
|
||||
pub mod W {}
|
||||
/// Read-write values (empty)
|
||||
pub mod RW {}
|
||||
}
|
||||
|
||||
/// PHY Suspended
|
||||
pub mod PHYSUSP {
|
||||
/// Offset (4 bits)
|
||||
pub const offset: u32 = 4;
|
||||
/// Mask (1 bit: 1 << 4)
|
||||
pub const mask: u32 = 1 << offset;
|
||||
/// Read-only values (empty)
|
||||
pub mod R {}
|
||||
/// Write-only values (empty)
|
||||
pub mod W {}
|
||||
/// Read-write values (empty)
|
||||
pub mod RW {}
|
||||
}
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct RegisterBlock {
|
||||
/// OTG_FS power and clock gating control register
|
||||
pub PCGCCTL: RWRegister<u32>,
|
||||
}
|
||||
pub struct ResetValues {
|
||||
pub PCGCCTL: u32,
|
||||
}
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
pub struct Instance {
|
||||
pub(crate) addr: u32,
|
||||
pub(crate) _marker: PhantomData<*const RegisterBlock>,
|
||||
}
|
||||
#[cfg(not(feature = "nosync"))]
|
||||
impl ::core::ops::Deref for Instance {
|
||||
type Target = RegisterBlock;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &RegisterBlock {
|
||||
unsafe { &*(self.addr as *const _) }
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "rtfm")]
|
||||
unsafe impl Send for Instance {}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
||||
pub use super::instances::otg_fs_device_f401_f405_f407_f411_f427_f429 as otg_fs_device;
|
||||
pub use super::instances::otg_fs_global_f401_f405_f407_f411_f427_f429 as otg_fs_global;
|
||||
pub use super::instances::otg_fs_host;
|
||||
pub use super::instances::otg_hs_device;
|
||||
pub use super::instances::otg_hs_global;
|
||||
pub use super::instances::otg_hs_host;
|
||||
pub use super::instances::otg_s_pwrclk;
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
//! Target-specific definitions
|
||||
|
||||
use vcell::VolatileCell;
|
||||
|
||||
use crate::ral::register::RWRegister;
|
||||
use crate::ral::{
|
||||
endpoint0_out, endpoint_in, endpoint_out, otg_device, otg_global, otg_global_dieptxfx,
|
||||
otg_pwrclk,
|
||||
};
|
||||
use crate::UsbPeripheral;
|
||||
|
||||
pub fn fifo_write(usb: UsbRegisters, channel: impl Into<usize>, mut buf: &[u8]) {
|
||||
let fifo = usb.fifo(channel.into());
|
||||
|
||||
while buf.len() >= 4 {
|
||||
let mut u32_bytes = [0u8; 4];
|
||||
u32_bytes.copy_from_slice(&buf[..4]);
|
||||
buf = &buf[4..];
|
||||
fifo.write(u32::from_ne_bytes(u32_bytes));
|
||||
}
|
||||
if !buf.is_empty() {
|
||||
let mut u32_bytes = [0u8; 4];
|
||||
u32_bytes[..buf.len()].copy_from_slice(buf);
|
||||
fifo.write(u32::from_ne_bytes(u32_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fifo_read_into(usb: UsbRegisters, buf: &[VolatileCell<u32>]) {
|
||||
let fifo = usb.fifo(0);
|
||||
|
||||
for p in buf {
|
||||
let word = fifo.read();
|
||||
p.set(word);
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around device-specific peripheral that provides unified register interface
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct UsbRegisters(usize);
|
||||
|
||||
impl UsbRegisters {
|
||||
#[inline(always)]
|
||||
pub fn new<USB: UsbPeripheral>() -> Self {
|
||||
Self(USB::REGISTERS as usize)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn global(&self) -> &'static otg_global::RegisterBlock {
|
||||
unsafe { &*(self.0 as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn device(&self) -> &'static otg_device::RegisterBlock {
|
||||
unsafe { &*((self.0 + 0x800) as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn pwrclk(&self) -> &'static otg_pwrclk::RegisterBlock {
|
||||
unsafe { &*((self.0 + 0xe00) as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn fifo(&self, channel: usize) -> &'static RWRegister<u32> {
|
||||
assert!(channel <= 15);
|
||||
let address = self.0 + 0x1000 + channel * 0x1000;
|
||||
unsafe { &*(address as *const RWRegister<u32>) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn dieptxfx(&self, index: usize) -> &'static otg_global_dieptxfx::RegisterBlock {
|
||||
let address = self.0 + 0x100 + 4 * index;
|
||||
unsafe { &*(address as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn endpoint_in(&self, index: usize) -> &'static endpoint_in::RegisterBlock {
|
||||
let address = self.0 + 0x900 + 0x20 * index;
|
||||
unsafe { &*(address as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn endpoint0_out(&self) -> &'static endpoint0_out::RegisterBlock {
|
||||
let address = self.0 + 0xb00;
|
||||
unsafe { &*(address as *const _) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn endpoint_out(&self, index: usize) -> &'static endpoint_out::RegisterBlock {
|
||||
let address = self.0 + 0xb00 + 0x20 * index;
|
||||
unsafe { &*(address as *const _) }
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
use usb_device::endpoint::{EndpointAddress, EndpointType};
|
||||
|
||||
/// USB endpoint descriptor information.
|
||||
pub struct EndpointDescriptor {
|
||||
/// Endpoint address.
|
||||
pub address: EndpointAddress,
|
||||
|
||||
/// Endpoint transfer type.
|
||||
pub ep_type: EndpointType,
|
||||
|
||||
/// Maximum packet size.
|
||||
pub max_packet_size: u16,
|
||||
|
||||
/// Poll interval for interrupt endpoints.
|
||||
pub interval: u8,
|
||||
}
|
||||
|
||||
/// Configuration for an endpoint allocation.
|
||||
pub struct EndpointConfig {
|
||||
/// The transfer type of the endpoint to be allocated.
|
||||
pub ep_type: EndpointType,
|
||||
|
||||
/// Maximum packet size for the endpoint to be allocated.
|
||||
pub max_packet_size: u16,
|
||||
|
||||
/// Poll interval for interrupt endpoints.
|
||||
pub interval: u8,
|
||||
|
||||
/// Requests a specific endpoint number. Allocation shall fail if the number is not available.
|
||||
pub number: Option<u8>,
|
||||
|
||||
/// Specifies that the endpoint is the "pair" of another endpoint.
|
||||
///
|
||||
/// If `ep` is an endpoint in the opposite direction, this means that the endpoint to be
|
||||
/// allocated uses the same transfer type as `ep` but in the opposite direction in all alternate
|
||||
/// settings for the interface.
|
||||
///
|
||||
/// If `ep` is an endpoint in the same direction, this means that the two endpoints belong to
|
||||
/// different alternate settings for the interface and may never be enabled at the same time.
|
||||
pub pair_of: Option<EndpointAddress>,
|
||||
}
|
||||
Reference in new issue
Block a user