diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-05-30 21:54:32 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-30 21:54:32 +0000 |
| commit | 3f90620343a5413423da8b84008cf468ea957d54 (patch) | |
| tree | 2b456e04f83b22cad28632034680e0227292a7f8 | |
| parent | f5d0d28ac3cfcb74eaa59bbe984b7969a0743724 (diff) | |
| parent | 3f35a8876ee65d030e32ab2444bc0abb29d88382 (diff) | |
Merge pull request #1512 from embassy-rs/cyw43
Merge cyw43 into main repo
| -rwxr-xr-x | ci.sh | 11 | ||||
| -rwxr-xr-x | cyw43-firmware/43439A0.bin | bin | 0 -> 224190 bytes | |||
| -rwxr-xr-x | cyw43-firmware/43439A0_clm.bin | bin | 0 -> 4752 bytes | |||
| -rw-r--r-- | cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt | 49 | ||||
| -rw-r--r-- | cyw43-firmware/README.md | 5 | ||||
| -rw-r--r-- | cyw43-pio/Cargo.toml | 17 | ||||
| -rw-r--r-- | cyw43-pio/src/lib.rs | 229 | ||||
| -rw-r--r-- | cyw43/Cargo.toml | 28 | ||||
| -rw-r--r-- | cyw43/README.md | 57 | ||||
| -rw-r--r-- | cyw43/src/bus.rs | 328 | ||||
| -rw-r--r-- | cyw43/src/consts.rs | 318 | ||||
| -rw-r--r-- | cyw43/src/control.rs | 457 | ||||
| -rw-r--r-- | cyw43/src/countries.rs | 481 | ||||
| -rw-r--r-- | cyw43/src/events.rs | 400 | ||||
| -rw-r--r-- | cyw43/src/fmt.rs | 257 | ||||
| -rw-r--r-- | cyw43/src/ioctl.rs | 126 | ||||
| -rw-r--r-- | cyw43/src/lib.rs | 236 | ||||
| -rw-r--r-- | cyw43/src/nvram.rs | 54 | ||||
| -rw-r--r-- | cyw43/src/runner.rs | 575 | ||||
| -rw-r--r-- | cyw43/src/structs.rs | 496 | ||||
| -rw-r--r-- | examples/rp/Cargo.toml | 3 | ||||
| -rw-r--r-- | examples/rp/src/bin/wifi_ap_tcp_server.rs | 139 | ||||
| -rw-r--r-- | examples/rp/src/bin/wifi_scan.rs | 75 | ||||
| -rw-r--r-- | examples/rp/src/bin/wifi_tcp_server.rs | 146 |
24 files changed, 4487 insertions, 0 deletions
| @@ -5,6 +5,10 @@ set -euo pipefail | |||
| 5 | export RUSTFLAGS=-Dwarnings | 5 | export RUSTFLAGS=-Dwarnings |
| 6 | export DEFMT_LOG=trace | 6 | export DEFMT_LOG=trace |
| 7 | 7 | ||
| 8 | # needed by wifi examples | ||
| 9 | export WIFI_NETWORK=x | ||
| 10 | export WIFI_PASSWORD=x | ||
| 11 | |||
| 8 | TARGET=$(rustc -vV | sed -n 's|host: ||p') | 12 | TARGET=$(rustc -vV | sed -n 's|host: ||p') |
| 9 | 13 | ||
| 10 | BUILD_EXTRA="" | 14 | BUILD_EXTRA="" |
| @@ -82,6 +86,13 @@ cargo batch \ | |||
| 82 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ | 86 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \ |
| 83 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ | 87 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \ |
| 84 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \ | 88 | --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \ |
| 89 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ | ||
| 90 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ | ||
| 91 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ | ||
| 92 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log,firmware-logs' \ | ||
| 93 | --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt,firmware-logs' \ | ||
| 94 | --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features '' \ | ||
| 95 | --- build --release --manifest-path cyw43-pio/Cargo.toml --target thumbv6m-none-eabi --features 'overclock' \ | ||
| 85 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \ | 96 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,nightly \ |
| 86 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \ | 97 | --- build --release --manifest-path embassy-boot/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,nightly \ |
| 87 | --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ | 98 | --- build --release --manifest-path embassy-boot/rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \ |
diff --git a/cyw43-firmware/43439A0.bin b/cyw43-firmware/43439A0.bin new file mode 100755 index 000000000..b46b3beff --- /dev/null +++ b/cyw43-firmware/43439A0.bin | |||
| Binary files differ | |||
diff --git a/cyw43-firmware/43439A0_clm.bin b/cyw43-firmware/43439A0_clm.bin new file mode 100755 index 000000000..6e3ba786b --- /dev/null +++ b/cyw43-firmware/43439A0_clm.bin | |||
| Binary files differ | |||
diff --git a/cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt b/cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt new file mode 100644 index 000000000..cbb51f9c9 --- /dev/null +++ b/cyw43-firmware/LICENSE-permissive-binary-license-1.0.txt | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | Permissive Binary License | ||
| 2 | |||
| 3 | Version 1.0, July 2019 | ||
| 4 | |||
| 5 | Redistribution. Redistribution and use in binary form, without | ||
| 6 | modification, are permitted provided that the following conditions are | ||
| 7 | met: | ||
| 8 | |||
| 9 | 1) Redistributions must reproduce the above copyright notice and the | ||
| 10 | following disclaimer in the documentation and/or other materials | ||
| 11 | provided with the distribution. | ||
| 12 | |||
| 13 | 2) Unless to the extent explicitly permitted by law, no reverse | ||
| 14 | engineering, decompilation, or disassembly of this software is | ||
| 15 | permitted. | ||
| 16 | |||
| 17 | 3) Redistribution as part of a software development kit must include the | ||
| 18 | accompanying file named �DEPENDENCIES� and any dependencies listed in | ||
| 19 | that file. | ||
| 20 | |||
| 21 | 4) Neither the name of the copyright holder nor the names of its | ||
| 22 | contributors may be used to endorse or promote products derived from | ||
| 23 | this software without specific prior written permission. | ||
| 24 | |||
| 25 | Limited patent license. The copyright holders (and contributors) grant a | ||
| 26 | worldwide, non-exclusive, no-charge, royalty-free patent license to | ||
| 27 | make, have made, use, offer to sell, sell, import, and otherwise | ||
| 28 | transfer this software, where such license applies only to those patent | ||
| 29 | claims licensable by the copyright holders (and contributors) that are | ||
| 30 | necessarily infringed by this software. This patent license shall not | ||
| 31 | apply to any combinations that include this software. No hardware is | ||
| 32 | licensed hereunder. | ||
| 33 | |||
| 34 | If you institute patent litigation against any entity (including a | ||
| 35 | cross-claim or counterclaim in a lawsuit) alleging that the software | ||
| 36 | itself infringes your patent(s), then your rights granted under this | ||
| 37 | license shall terminate as of the date such litigation is filed. | ||
| 38 | |||
| 39 | DISCLAIMER. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND | ||
| 40 | CONTRIBUTORS "AS IS." ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT | ||
| 41 | NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
| 42 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 43 | HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 44 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| 45 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| 46 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| 47 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| 48 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| 49 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file | ||
diff --git a/cyw43-firmware/README.md b/cyw43-firmware/README.md new file mode 100644 index 000000000..7381fdc56 --- /dev/null +++ b/cyw43-firmware/README.md | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # WiFi firmware | ||
| 2 | |||
| 3 | Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439 | ||
| 4 | |||
| 5 | Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) \ No newline at end of file | ||
diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml new file mode 100644 index 000000000..6e9e784a0 --- /dev/null +++ b/cyw43-pio/Cargo.toml | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | [package] | ||
| 2 | name = "cyw43-pio" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | |||
| 6 | [features] | ||
| 7 | # If disabled, SPI runs at 31.25MHz | ||
| 8 | # If enabled, SPI runs at 62.5MHz, which is 25% higher than 50Mhz which is the maximum according to the CYW43439 datasheet. | ||
| 9 | overclock = [] | ||
| 10 | |||
| 11 | [dependencies] | ||
| 12 | cyw43 = { version = "0.1.0", path = "../cyw43" } | ||
| 13 | embassy-rp = { version = "0.1.0", path = "../embassy-rp" } | ||
| 14 | pio-proc = "0.2" | ||
| 15 | pio = "0.2.1" | ||
| 16 | fixed = "1.23.1" | ||
| 17 | defmt = { version = "0.3", optional = true } | ||
diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs new file mode 100644 index 000000000..dca30c74d --- /dev/null +++ b/cyw43-pio/src/lib.rs | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![allow(incomplete_features)] | ||
| 3 | #![feature(async_fn_in_trait)] | ||
| 4 | |||
| 5 | use core::slice; | ||
| 6 | |||
| 7 | use cyw43::SpiBusCyw43; | ||
| 8 | use embassy_rp::dma::Channel; | ||
| 9 | use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; | ||
| 10 | use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; | ||
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; | ||
| 13 | use fixed::FixedU32; | ||
| 14 | use pio_proc::pio_asm; | ||
| 15 | |||
| 16 | pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> { | ||
| 17 | cs: Output<'d, CS>, | ||
| 18 | sm: StateMachine<'d, PIO, SM>, | ||
| 19 | irq: Irq<'d, PIO, 0>, | ||
| 20 | dma: PeripheralRef<'d, DMA>, | ||
| 21 | wrap_target: u8, | ||
| 22 | } | ||
| 23 | |||
| 24 | impl<'d, CS, PIO, const SM: usize, DMA> PioSpi<'d, CS, PIO, SM, DMA> | ||
| 25 | where | ||
| 26 | DMA: Channel, | ||
| 27 | CS: Pin, | ||
| 28 | PIO: Instance, | ||
| 29 | { | ||
| 30 | pub fn new<DIO, CLK>( | ||
| 31 | common: &mut Common<'d, PIO>, | ||
| 32 | mut sm: StateMachine<'d, PIO, SM>, | ||
| 33 | irq: Irq<'d, PIO, 0>, | ||
| 34 | cs: Output<'d, CS>, | ||
| 35 | dio: DIO, | ||
| 36 | clk: CLK, | ||
| 37 | dma: impl Peripheral<P = DMA> + 'd, | ||
| 38 | ) -> Self | ||
| 39 | where | ||
| 40 | DIO: PioPin, | ||
| 41 | CLK: PioPin, | ||
| 42 | { | ||
| 43 | #[cfg(feature = "overclock")] | ||
| 44 | let program = pio_asm!( | ||
| 45 | ".side_set 1" | ||
| 46 | |||
| 47 | ".wrap_target" | ||
| 48 | // write out x-1 bits | ||
| 49 | "lp:" | ||
| 50 | "out pins, 1 side 0" | ||
| 51 | "jmp x-- lp side 1" | ||
| 52 | // switch directions | ||
| 53 | "set pindirs, 0 side 0" | ||
| 54 | "nop side 1" // necessary for clkdiv=1. | ||
| 55 | "nop side 0" | ||
| 56 | // read in y-1 bits | ||
| 57 | "lp2:" | ||
| 58 | "in pins, 1 side 1" | ||
| 59 | "jmp y-- lp2 side 0" | ||
| 60 | |||
| 61 | // wait for event and irq host | ||
| 62 | "wait 1 pin 0 side 0" | ||
| 63 | "irq 0 side 0" | ||
| 64 | |||
| 65 | ".wrap" | ||
| 66 | ); | ||
| 67 | #[cfg(not(feature = "overclock"))] | ||
| 68 | let program = pio_asm!( | ||
| 69 | ".side_set 1" | ||
| 70 | |||
| 71 | ".wrap_target" | ||
| 72 | // write out x-1 bits | ||
| 73 | "lp:" | ||
| 74 | "out pins, 1 side 0" | ||
| 75 | "jmp x-- lp side 1" | ||
| 76 | // switch directions | ||
| 77 | "set pindirs, 0 side 0" | ||
| 78 | "nop side 0" | ||
| 79 | // read in y-1 bits | ||
| 80 | "lp2:" | ||
| 81 | "in pins, 1 side 1" | ||
| 82 | "jmp y-- lp2 side 0" | ||
| 83 | |||
| 84 | // wait for event and irq host | ||
| 85 | "wait 1 pin 0 side 0" | ||
| 86 | "irq 0 side 0" | ||
| 87 | |||
| 88 | ".wrap" | ||
| 89 | ); | ||
| 90 | |||
| 91 | let relocated = RelocatedProgram::new(&program.program); | ||
| 92 | |||
| 93 | let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio); | ||
| 94 | pin_io.set_pull(Pull::None); | ||
| 95 | pin_io.set_schmitt(true); | ||
| 96 | pin_io.set_input_sync_bypass(true); | ||
| 97 | pin_io.set_drive_strength(Drive::_12mA); | ||
| 98 | pin_io.set_slew_rate(SlewRate::Fast); | ||
| 99 | |||
| 100 | let mut pin_clk = common.make_pio_pin(clk); | ||
| 101 | pin_clk.set_drive_strength(Drive::_12mA); | ||
| 102 | pin_clk.set_slew_rate(SlewRate::Fast); | ||
| 103 | |||
| 104 | let mut cfg = Config::default(); | ||
| 105 | cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); | ||
| 106 | cfg.set_out_pins(&[&pin_io]); | ||
| 107 | cfg.set_in_pins(&[&pin_io]); | ||
| 108 | cfg.set_set_pins(&[&pin_io]); | ||
| 109 | cfg.shift_out.direction = ShiftDirection::Left; | ||
| 110 | cfg.shift_out.auto_fill = true; | ||
| 111 | //cfg.shift_out.threshold = 32; | ||
| 112 | cfg.shift_in.direction = ShiftDirection::Left; | ||
| 113 | cfg.shift_in.auto_fill = true; | ||
| 114 | //cfg.shift_in.threshold = 32; | ||
| 115 | |||
| 116 | #[cfg(feature = "overclock")] | ||
| 117 | { | ||
| 118 | // 125mhz Pio => 62.5Mhz SPI Freq. 25% higher than theoretical maximum according to | ||
| 119 | // data sheet, but seems to work fine. | ||
| 120 | cfg.clock_divider = FixedU32::from_bits(0x0100); | ||
| 121 | } | ||
| 122 | |||
| 123 | #[cfg(not(feature = "overclock"))] | ||
| 124 | { | ||
| 125 | // same speed as pico-sdk, 62.5Mhz | ||
| 126 | // This is actually the fastest we can go without overclocking. | ||
| 127 | // According to data sheet, the theoretical maximum is 100Mhz Pio => 50Mhz SPI Freq. | ||
| 128 | // However, the PIO uses a fractional divider, which works by introducing jitter when | ||
| 129 | // the divider is not an integer. It does some clocks at 125mhz and others at 62.5mhz | ||
| 130 | // so that it averages out to the desired frequency of 100mhz. The 125mhz clock cycles | ||
| 131 | // violate the maximum from the data sheet. | ||
| 132 | cfg.clock_divider = FixedU32::from_bits(0x0200); | ||
| 133 | } | ||
| 134 | |||
| 135 | sm.set_config(&cfg); | ||
| 136 | |||
| 137 | sm.set_pin_dirs(Direction::Out, &[&pin_clk, &pin_io]); | ||
| 138 | sm.set_pins(Level::Low, &[&pin_clk, &pin_io]); | ||
| 139 | |||
| 140 | Self { | ||
| 141 | cs, | ||
| 142 | sm, | ||
| 143 | irq, | ||
| 144 | dma: dma.into_ref(), | ||
| 145 | wrap_target: relocated.wrap().target, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | pub async fn write(&mut self, write: &[u32]) -> u32 { | ||
| 150 | self.sm.set_enable(false); | ||
| 151 | let write_bits = write.len() * 32 - 1; | ||
| 152 | let read_bits = 31; | ||
| 153 | |||
| 154 | #[cfg(feature = "defmt")] | ||
| 155 | defmt::trace!("write={} read={}", write_bits, read_bits); | ||
| 156 | |||
| 157 | unsafe { | ||
| 158 | pio_instr_util::set_x(&mut self.sm, write_bits as u32); | ||
| 159 | pio_instr_util::set_y(&mut self.sm, read_bits as u32); | ||
| 160 | pio_instr_util::set_pindir(&mut self.sm, 0b1); | ||
| 161 | pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); | ||
| 162 | } | ||
| 163 | |||
| 164 | self.sm.set_enable(true); | ||
| 165 | |||
| 166 | self.sm.tx().dma_push(self.dma.reborrow(), write).await; | ||
| 167 | |||
| 168 | let mut status = 0; | ||
| 169 | self.sm | ||
| 170 | .rx() | ||
| 171 | .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status)) | ||
| 172 | .await; | ||
| 173 | status | ||
| 174 | } | ||
| 175 | |||
| 176 | pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 { | ||
| 177 | self.sm.set_enable(false); | ||
| 178 | let write_bits = 31; | ||
| 179 | let read_bits = read.len() * 32 + 32 - 1; | ||
| 180 | |||
| 181 | #[cfg(feature = "defmt")] | ||
| 182 | defmt::trace!("write={} read={}", write_bits, read_bits); | ||
| 183 | |||
| 184 | unsafe { | ||
| 185 | pio_instr_util::set_y(&mut self.sm, read_bits as u32); | ||
| 186 | pio_instr_util::set_x(&mut self.sm, write_bits as u32); | ||
| 187 | pio_instr_util::set_pindir(&mut self.sm, 0b1); | ||
| 188 | pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target); | ||
| 189 | } | ||
| 190 | |||
| 191 | // self.cs.set_low(); | ||
| 192 | self.sm.set_enable(true); | ||
| 193 | |||
| 194 | self.sm.tx().dma_push(self.dma.reborrow(), slice::from_ref(&cmd)).await; | ||
| 195 | self.sm.rx().dma_pull(self.dma.reborrow(), read).await; | ||
| 196 | |||
| 197 | let mut status = 0; | ||
| 198 | self.sm | ||
| 199 | .rx() | ||
| 200 | .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status)) | ||
| 201 | .await; | ||
| 202 | status | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | impl<'d, CS, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, CS, PIO, SM, DMA> | ||
| 207 | where | ||
| 208 | CS: Pin, | ||
| 209 | PIO: Instance, | ||
| 210 | DMA: Channel, | ||
| 211 | { | ||
| 212 | async fn cmd_write(&mut self, write: &[u32]) -> u32 { | ||
| 213 | self.cs.set_low(); | ||
| 214 | let status = self.write(write).await; | ||
| 215 | self.cs.set_high(); | ||
| 216 | status | ||
| 217 | } | ||
| 218 | |||
| 219 | async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 { | ||
| 220 | self.cs.set_low(); | ||
| 221 | let status = self.cmd_read(write, read).await; | ||
| 222 | self.cs.set_high(); | ||
| 223 | status | ||
| 224 | } | ||
| 225 | |||
| 226 | async fn wait_for_event(&mut self) { | ||
| 227 | self.irq.wait().await; | ||
| 228 | } | ||
| 229 | } | ||
diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml new file mode 100644 index 000000000..c7f8816f5 --- /dev/null +++ b/cyw43/Cargo.toml | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | [package] | ||
| 2 | name = "cyw43" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2021" | ||
| 5 | |||
| 6 | [features] | ||
| 7 | defmt = ["dep:defmt"] | ||
| 8 | log = ["dep:log"] | ||
| 9 | |||
| 10 | # Fetch console logs from the WiFi firmware and forward them to `log` or `defmt`. | ||
| 11 | firmware-logs = [] | ||
| 12 | |||
| 13 | [dependencies] | ||
| 14 | embassy-time = { version = "0.1.0", path = "../embassy-time"} | ||
| 15 | embassy-sync = { version = "0.2.0", path = "../embassy-sync"} | ||
| 16 | embassy-futures = { version = "0.1.0", path = "../embassy-futures"} | ||
| 17 | embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"} | ||
| 18 | atomic-polyfill = "0.1.5" | ||
| 19 | |||
| 20 | defmt = { version = "0.3", optional = true } | ||
| 21 | log = { version = "0.4.17", optional = true } | ||
| 22 | |||
| 23 | cortex-m = "0.7.6" | ||
| 24 | cortex-m-rt = "0.7.0" | ||
| 25 | futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] } | ||
| 26 | |||
| 27 | embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" } | ||
| 28 | num_enum = { version = "0.5.7", default-features = false } | ||
diff --git a/cyw43/README.md b/cyw43/README.md new file mode 100644 index 000000000..defea489f --- /dev/null +++ b/cyw43/README.md | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | # cyw43 | ||
| 2 | |||
| 3 | WIP driver for the CYW43439 wifi chip, used in the Raspberry Pi Pico W. Implementation based on [Infineon/wifi-host-driver](https://github.com/Infineon/wifi-host-driver). | ||
| 4 | |||
| 5 | ## Current status | ||
| 6 | |||
| 7 | Working: | ||
| 8 | |||
| 9 | - Station mode (joining an AP). | ||
| 10 | - AP mode (creating an AP) | ||
| 11 | - Scanning | ||
| 12 | - Sending and receiving Ethernet frames. | ||
| 13 | - Using the default MAC address. | ||
| 14 | - [`embassy-net`](https://embassy.dev) integration. | ||
| 15 | - RP2040 PIO driver for the nonstandard half-duplex SPI used in the Pico W. | ||
| 16 | - Using IRQ for device events | ||
| 17 | - GPIO support (for LED on the Pico W) | ||
| 18 | |||
| 19 | TODO: | ||
| 20 | |||
| 21 | - Setting a custom MAC address. | ||
| 22 | - Bus sleep (unclear what the benefit is. Is it needed for IRQs? or is it just power consumption optimization?) | ||
| 23 | |||
| 24 | ## Running the examples | ||
| 25 | |||
| 26 | - `cargo install probe-rs-cli` | ||
| 27 | - `cd examples/rpi-pico-w` | ||
| 28 | ### Example 1: Scan the wifi stations | ||
| 29 | - `cargo run --release --bin wifi_scan` | ||
| 30 | ### Example 2: Create an access point (IP and credentials in the code) | ||
| 31 | - `cargo run --release --bin tcp_server_ap` | ||
| 32 | ### Example 3: Connect to an existing network and create a server | ||
| 33 | - `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release` | ||
| 34 | |||
| 35 | After a few seconds, you should see that DHCP picks up an IP address like this | ||
| 36 | ``` | ||
| 37 | 11.944489 DEBUG Acquired IP configuration: | ||
| 38 | 11.944517 DEBUG IP address: 192.168.0.250/24 | ||
| 39 | 11.944620 DEBUG Default gateway: 192.168.0.33 | ||
| 40 | 11.944722 DEBUG DNS server 0: 192.168.0.33 | ||
| 41 | ``` | ||
| 42 | This example implements a TCP echo server on port 1234. You can try connecting to it with: | ||
| 43 | ``` | ||
| 44 | nc 192.168.0.250 1234 | ||
| 45 | ``` | ||
| 46 | Send it some data, you should see it echoed back and printed in the firmware's logs. | ||
| 47 | |||
| 48 | ## License | ||
| 49 | |||
| 50 | This work is licensed under either of | ||
| 51 | |||
| 52 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or | ||
| 53 | <http://www.apache.org/licenses/LICENSE-2.0>) | ||
| 54 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>) | ||
| 55 | |||
| 56 | at your option. | ||
| 57 | |||
diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs new file mode 100644 index 000000000..e26f11120 --- /dev/null +++ b/cyw43/src/bus.rs | |||
| @@ -0,0 +1,328 @@ | |||
| 1 | use embassy_futures::yield_now; | ||
| 2 | use embassy_time::{Duration, Timer}; | ||
| 3 | use embedded_hal_1::digital::OutputPin; | ||
| 4 | use futures::FutureExt; | ||
| 5 | |||
| 6 | use crate::consts::*; | ||
| 7 | use crate::slice8_mut; | ||
| 8 | |||
| 9 | /// Custom Spi Trait that _only_ supports the bus operation of the cyw43 | ||
| 10 | /// Implementors are expected to hold the CS pin low during an operation. | ||
| 11 | pub trait SpiBusCyw43 { | ||
| 12 | /// Issues a write command on the bus | ||
| 13 | /// First 32 bits of `word` are expected to be a cmd word | ||
| 14 | async fn cmd_write(&mut self, write: &[u32]) -> u32; | ||
| 15 | |||
| 16 | /// Issues a read command on the bus | ||
| 17 | /// `write` is expected to be a 32 bit cmd word | ||
| 18 | /// `read` will contain the response of the device | ||
| 19 | /// Backplane reads have a response delay that produces one extra unspecified word at the beginning of `read`. | ||
| 20 | /// Callers that want to read `n` word from the backplane, have to provide a slice that is `n+1` words long. | ||
| 21 | async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32; | ||
| 22 | |||
| 23 | /// Wait for events from the Device. A typical implementation would wait for the IRQ pin to be high. | ||
| 24 | /// The default implementation always reports ready, resulting in active polling of the device. | ||
| 25 | async fn wait_for_event(&mut self) { | ||
| 26 | yield_now().await; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | pub(crate) struct Bus<PWR, SPI> { | ||
| 31 | backplane_window: u32, | ||
| 32 | pwr: PWR, | ||
| 33 | spi: SPI, | ||
| 34 | status: u32, | ||
| 35 | } | ||
| 36 | |||
| 37 | impl<PWR, SPI> Bus<PWR, SPI> | ||
| 38 | where | ||
| 39 | PWR: OutputPin, | ||
| 40 | SPI: SpiBusCyw43, | ||
| 41 | { | ||
| 42 | pub(crate) fn new(pwr: PWR, spi: SPI) -> Self { | ||
| 43 | Self { | ||
| 44 | backplane_window: 0xAAAA_AAAA, | ||
| 45 | pwr, | ||
| 46 | spi, | ||
| 47 | status: 0, | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | pub async fn init(&mut self) { | ||
| 52 | // Reset | ||
| 53 | self.pwr.set_low().unwrap(); | ||
| 54 | Timer::after(Duration::from_millis(20)).await; | ||
| 55 | self.pwr.set_high().unwrap(); | ||
| 56 | Timer::after(Duration::from_millis(250)).await; | ||
| 57 | |||
| 58 | while self | ||
| 59 | .read32_swapped(REG_BUS_TEST_RO) | ||
| 60 | .inspect(|v| trace!("{:#x}", v)) | ||
| 61 | .await | ||
| 62 | != FEEDBEAD | ||
| 63 | {} | ||
| 64 | |||
| 65 | self.write32_swapped(REG_BUS_TEST_RW, TEST_PATTERN).await; | ||
| 66 | let val = self.read32_swapped(REG_BUS_TEST_RW).await; | ||
| 67 | trace!("{:#x}", val); | ||
| 68 | assert_eq!(val, TEST_PATTERN); | ||
| 69 | |||
| 70 | let val = self.read32_swapped(REG_BUS_CTRL).await; | ||
| 71 | trace!("{:#010b}", (val & 0xff)); | ||
| 72 | |||
| 73 | // 32-bit word length, little endian (which is the default endianess). | ||
| 74 | self.write32_swapped( | ||
| 75 | REG_BUS_CTRL, | ||
| 76 | WORD_LENGTH_32 | HIGH_SPEED | INTERRUPT_HIGH | WAKE_UP | STATUS_ENABLE | INTERRUPT_WITH_STATUS, | ||
| 77 | ) | ||
| 78 | .await; | ||
| 79 | |||
| 80 | let val = self.read8(FUNC_BUS, REG_BUS_CTRL).await; | ||
| 81 | trace!("{:#b}", val); | ||
| 82 | |||
| 83 | let val = self.read32(FUNC_BUS, REG_BUS_TEST_RO).await; | ||
| 84 | trace!("{:#x}", val); | ||
| 85 | assert_eq!(val, FEEDBEAD); | ||
| 86 | let val = self.read32(FUNC_BUS, REG_BUS_TEST_RW).await; | ||
| 87 | trace!("{:#x}", val); | ||
| 88 | assert_eq!(val, TEST_PATTERN); | ||
| 89 | } | ||
| 90 | |||
| 91 | pub async fn wlan_read(&mut self, buf: &mut [u32], len_in_u8: u32) { | ||
| 92 | let cmd = cmd_word(READ, INC_ADDR, FUNC_WLAN, 0, len_in_u8); | ||
| 93 | let len_in_u32 = (len_in_u8 as usize + 3) / 4; | ||
| 94 | |||
| 95 | self.status = self.spi.cmd_read(cmd, &mut buf[..len_in_u32]).await; | ||
| 96 | } | ||
| 97 | |||
| 98 | pub async fn wlan_write(&mut self, buf: &[u32]) { | ||
| 99 | let cmd = cmd_word(WRITE, INC_ADDR, FUNC_WLAN, 0, buf.len() as u32 * 4); | ||
| 100 | //TODO try to remove copy? | ||
| 101 | let mut cmd_buf = [0_u32; 513]; | ||
| 102 | cmd_buf[0] = cmd; | ||
| 103 | cmd_buf[1..][..buf.len()].copy_from_slice(buf); | ||
| 104 | |||
| 105 | self.status = self.spi.cmd_write(&cmd_buf).await; | ||
| 106 | } | ||
| 107 | |||
| 108 | #[allow(unused)] | ||
| 109 | pub async fn bp_read(&mut self, mut addr: u32, mut data: &mut [u8]) { | ||
| 110 | // It seems the HW force-aligns the addr | ||
| 111 | // to 2 if data.len() >= 2 | ||
| 112 | // to 4 if data.len() >= 4 | ||
| 113 | // To simplify, enforce 4-align for now. | ||
| 114 | assert!(addr % 4 == 0); | ||
| 115 | |||
| 116 | // Backplane read buffer has one extra word for the response delay. | ||
| 117 | let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1]; | ||
| 118 | |||
| 119 | while !data.is_empty() { | ||
| 120 | // Ensure transfer doesn't cross a window boundary. | ||
| 121 | let window_offs = addr & BACKPLANE_ADDRESS_MASK; | ||
| 122 | let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; | ||
| 123 | |||
| 124 | let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); | ||
| 125 | |||
| 126 | self.backplane_set_window(addr).await; | ||
| 127 | |||
| 128 | let cmd = cmd_word(READ, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); | ||
| 129 | |||
| 130 | // round `buf` to word boundary, add one extra word for the response delay | ||
| 131 | self.status = self.spi.cmd_read(cmd, &mut buf[..(len + 3) / 4 + 1]).await; | ||
| 132 | |||
| 133 | // when writing out the data, we skip the response-delay byte | ||
| 134 | data[..len].copy_from_slice(&slice8_mut(&mut buf[1..])[..len]); | ||
| 135 | |||
| 136 | // Advance ptr. | ||
| 137 | addr += len as u32; | ||
| 138 | data = &mut data[len..]; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | pub async fn bp_write(&mut self, mut addr: u32, mut data: &[u8]) { | ||
| 143 | // It seems the HW force-aligns the addr | ||
| 144 | // to 2 if data.len() >= 2 | ||
| 145 | // to 4 if data.len() >= 4 | ||
| 146 | // To simplify, enforce 4-align for now. | ||
| 147 | assert!(addr % 4 == 0); | ||
| 148 | |||
| 149 | let mut buf = [0u32; BACKPLANE_MAX_TRANSFER_SIZE / 4 + 1]; | ||
| 150 | |||
| 151 | while !data.is_empty() { | ||
| 152 | // Ensure transfer doesn't cross a window boundary. | ||
| 153 | let window_offs = addr & BACKPLANE_ADDRESS_MASK; | ||
| 154 | let window_remaining = BACKPLANE_WINDOW_SIZE - window_offs as usize; | ||
| 155 | |||
| 156 | let len = data.len().min(BACKPLANE_MAX_TRANSFER_SIZE).min(window_remaining); | ||
| 157 | slice8_mut(&mut buf[1..])[..len].copy_from_slice(&data[..len]); | ||
| 158 | |||
| 159 | self.backplane_set_window(addr).await; | ||
| 160 | |||
| 161 | let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BACKPLANE, window_offs, len as u32); | ||
| 162 | buf[0] = cmd; | ||
| 163 | |||
| 164 | self.status = self.spi.cmd_write(&buf[..(len + 3) / 4 + 1]).await; | ||
| 165 | |||
| 166 | // Advance ptr. | ||
| 167 | addr += len as u32; | ||
| 168 | data = &data[len..]; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | pub async fn bp_read8(&mut self, addr: u32) -> u8 { | ||
| 173 | self.backplane_readn(addr, 1).await as u8 | ||
| 174 | } | ||
| 175 | |||
| 176 | pub async fn bp_write8(&mut self, addr: u32, val: u8) { | ||
| 177 | self.backplane_writen(addr, val as u32, 1).await | ||
| 178 | } | ||
| 179 | |||
| 180 | pub async fn bp_read16(&mut self, addr: u32) -> u16 { | ||
| 181 | self.backplane_readn(addr, 2).await as u16 | ||
| 182 | } | ||
| 183 | |||
| 184 | #[allow(unused)] | ||
| 185 | pub async fn bp_write16(&mut self, addr: u32, val: u16) { | ||
| 186 | self.backplane_writen(addr, val as u32, 2).await | ||
| 187 | } | ||
| 188 | |||
| 189 | #[allow(unused)] | ||
| 190 | pub async fn bp_read32(&mut self, addr: u32) -> u32 { | ||
| 191 | self.backplane_readn(addr, 4).await | ||
| 192 | } | ||
| 193 | |||
| 194 | pub async fn bp_write32(&mut self, addr: u32, val: u32) { | ||
| 195 | self.backplane_writen(addr, val, 4).await | ||
| 196 | } | ||
| 197 | |||
| 198 | async fn backplane_readn(&mut self, addr: u32, len: u32) -> u32 { | ||
| 199 | self.backplane_set_window(addr).await; | ||
| 200 | |||
| 201 | let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; | ||
| 202 | if len == 4 { | ||
| 203 | bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG | ||
| 204 | } | ||
| 205 | self.readn(FUNC_BACKPLANE, bus_addr, len).await | ||
| 206 | } | ||
| 207 | |||
| 208 | async fn backplane_writen(&mut self, addr: u32, val: u32, len: u32) { | ||
| 209 | self.backplane_set_window(addr).await; | ||
| 210 | |||
| 211 | let mut bus_addr = addr & BACKPLANE_ADDRESS_MASK; | ||
| 212 | if len == 4 { | ||
| 213 | bus_addr |= BACKPLANE_ADDRESS_32BIT_FLAG | ||
| 214 | } | ||
| 215 | self.writen(FUNC_BACKPLANE, bus_addr, val, len).await | ||
| 216 | } | ||
| 217 | |||
| 218 | async fn backplane_set_window(&mut self, addr: u32) { | ||
| 219 | let new_window = addr & !BACKPLANE_ADDRESS_MASK; | ||
| 220 | |||
| 221 | if (new_window >> 24) as u8 != (self.backplane_window >> 24) as u8 { | ||
| 222 | self.write8( | ||
| 223 | FUNC_BACKPLANE, | ||
| 224 | REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH, | ||
| 225 | (new_window >> 24) as u8, | ||
| 226 | ) | ||
| 227 | .await; | ||
| 228 | } | ||
| 229 | if (new_window >> 16) as u8 != (self.backplane_window >> 16) as u8 { | ||
| 230 | self.write8( | ||
| 231 | FUNC_BACKPLANE, | ||
| 232 | REG_BACKPLANE_BACKPLANE_ADDRESS_MID, | ||
| 233 | (new_window >> 16) as u8, | ||
| 234 | ) | ||
| 235 | .await; | ||
| 236 | } | ||
| 237 | if (new_window >> 8) as u8 != (self.backplane_window >> 8) as u8 { | ||
| 238 | self.write8( | ||
| 239 | FUNC_BACKPLANE, | ||
| 240 | REG_BACKPLANE_BACKPLANE_ADDRESS_LOW, | ||
| 241 | (new_window >> 8) as u8, | ||
| 242 | ) | ||
| 243 | .await; | ||
| 244 | } | ||
| 245 | self.backplane_window = new_window; | ||
| 246 | } | ||
| 247 | |||
| 248 | pub async fn read8(&mut self, func: u32, addr: u32) -> u8 { | ||
| 249 | self.readn(func, addr, 1).await as u8 | ||
| 250 | } | ||
| 251 | |||
| 252 | pub async fn write8(&mut self, func: u32, addr: u32, val: u8) { | ||
| 253 | self.writen(func, addr, val as u32, 1).await | ||
| 254 | } | ||
| 255 | |||
| 256 | pub async fn read16(&mut self, func: u32, addr: u32) -> u16 { | ||
| 257 | self.readn(func, addr, 2).await as u16 | ||
| 258 | } | ||
| 259 | |||
| 260 | #[allow(unused)] | ||
| 261 | pub async fn write16(&mut self, func: u32, addr: u32, val: u16) { | ||
| 262 | self.writen(func, addr, val as u32, 2).await | ||
| 263 | } | ||
| 264 | |||
| 265 | pub async fn read32(&mut self, func: u32, addr: u32) -> u32 { | ||
| 266 | self.readn(func, addr, 4).await | ||
| 267 | } | ||
| 268 | |||
| 269 | #[allow(unused)] | ||
| 270 | pub async fn write32(&mut self, func: u32, addr: u32, val: u32) { | ||
| 271 | self.writen(func, addr, val, 4).await | ||
| 272 | } | ||
| 273 | |||
| 274 | async fn readn(&mut self, func: u32, addr: u32, len: u32) -> u32 { | ||
| 275 | let cmd = cmd_word(READ, INC_ADDR, func, addr, len); | ||
| 276 | let mut buf = [0; 2]; | ||
| 277 | // if we are reading from the backplane, we need an extra word for the response delay | ||
| 278 | let len = if func == FUNC_BACKPLANE { 2 } else { 1 }; | ||
| 279 | |||
| 280 | self.status = self.spi.cmd_read(cmd, &mut buf[..len]).await; | ||
| 281 | |||
| 282 | // if we read from the backplane, the result is in the second word, after the response delay | ||
| 283 | if func == FUNC_BACKPLANE { | ||
| 284 | buf[1] | ||
| 285 | } else { | ||
| 286 | buf[0] | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { | ||
| 291 | let cmd = cmd_word(WRITE, INC_ADDR, func, addr, len); | ||
| 292 | |||
| 293 | self.status = self.spi.cmd_write(&[cmd, val]).await; | ||
| 294 | } | ||
| 295 | |||
| 296 | async fn read32_swapped(&mut self, addr: u32) -> u32 { | ||
| 297 | let cmd = cmd_word(READ, INC_ADDR, FUNC_BUS, addr, 4); | ||
| 298 | let cmd = swap16(cmd); | ||
| 299 | let mut buf = [0; 1]; | ||
| 300 | |||
| 301 | self.status = self.spi.cmd_read(cmd, &mut buf).await; | ||
| 302 | |||
| 303 | swap16(buf[0]) | ||
| 304 | } | ||
| 305 | |||
| 306 | async fn write32_swapped(&mut self, addr: u32, val: u32) { | ||
| 307 | let cmd = cmd_word(WRITE, INC_ADDR, FUNC_BUS, addr, 4); | ||
| 308 | let buf = [swap16(cmd), swap16(val)]; | ||
| 309 | |||
| 310 | self.status = self.spi.cmd_write(&buf).await; | ||
| 311 | } | ||
| 312 | |||
| 313 | pub async fn wait_for_event(&mut self) { | ||
| 314 | self.spi.wait_for_event().await; | ||
| 315 | } | ||
| 316 | |||
| 317 | pub fn status(&self) -> u32 { | ||
| 318 | self.status | ||
| 319 | } | ||
| 320 | } | ||
| 321 | |||
| 322 | fn swap16(x: u32) -> u32 { | ||
| 323 | x.rotate_left(16) | ||
| 324 | } | ||
| 325 | |||
| 326 | fn cmd_word(write: bool, incr: bool, func: u32, addr: u32, len: u32) -> u32 { | ||
| 327 | (write as u32) << 31 | (incr as u32) << 30 | (func & 0b11) << 28 | (addr & 0x1FFFF) << 11 | (len & 0x7FF) | ||
| 328 | } | ||
diff --git a/cyw43/src/consts.rs b/cyw43/src/consts.rs new file mode 100644 index 000000000..1f6551589 --- /dev/null +++ b/cyw43/src/consts.rs | |||
| @@ -0,0 +1,318 @@ | |||
| 1 | #![allow(unused)] | ||
| 2 | |||
| 3 | pub(crate) const FUNC_BUS: u32 = 0; | ||
| 4 | pub(crate) const FUNC_BACKPLANE: u32 = 1; | ||
| 5 | pub(crate) const FUNC_WLAN: u32 = 2; | ||
| 6 | pub(crate) const FUNC_BT: u32 = 3; | ||
| 7 | |||
| 8 | pub(crate) const REG_BUS_CTRL: u32 = 0x0; | ||
| 9 | pub(crate) const REG_BUS_INTERRUPT: u32 = 0x04; // 16 bits - Interrupt status | ||
| 10 | pub(crate) const REG_BUS_INTERRUPT_ENABLE: u32 = 0x06; // 16 bits - Interrupt mask | ||
| 11 | pub(crate) const REG_BUS_STATUS: u32 = 0x8; | ||
| 12 | pub(crate) const REG_BUS_TEST_RO: u32 = 0x14; | ||
| 13 | pub(crate) const REG_BUS_TEST_RW: u32 = 0x18; | ||
| 14 | pub(crate) const REG_BUS_RESP_DELAY: u32 = 0x1c; | ||
| 15 | pub(crate) const WORD_LENGTH_32: u32 = 0x1; | ||
| 16 | pub(crate) const HIGH_SPEED: u32 = 0x10; | ||
| 17 | pub(crate) const INTERRUPT_HIGH: u32 = 1 << 5; | ||
| 18 | pub(crate) const WAKE_UP: u32 = 1 << 7; | ||
| 19 | pub(crate) const STATUS_ENABLE: u32 = 1 << 16; | ||
| 20 | pub(crate) const INTERRUPT_WITH_STATUS: u32 = 1 << 17; | ||
| 21 | |||
| 22 | // SPI_STATUS_REGISTER bits | ||
| 23 | pub(crate) const STATUS_DATA_NOT_AVAILABLE: u32 = 0x00000001; | ||
| 24 | pub(crate) const STATUS_UNDERFLOW: u32 = 0x00000002; | ||
| 25 | pub(crate) const STATUS_OVERFLOW: u32 = 0x00000004; | ||
| 26 | pub(crate) const STATUS_F2_INTR: u32 = 0x00000008; | ||
| 27 | pub(crate) const STATUS_F3_INTR: u32 = 0x00000010; | ||
| 28 | pub(crate) const STATUS_F2_RX_READY: u32 = 0x00000020; | ||
| 29 | pub(crate) const STATUS_F3_RX_READY: u32 = 0x00000040; | ||
| 30 | pub(crate) const STATUS_HOST_CMD_DATA_ERR: u32 = 0x00000080; | ||
| 31 | pub(crate) const STATUS_F2_PKT_AVAILABLE: u32 = 0x00000100; | ||
| 32 | pub(crate) const STATUS_F2_PKT_LEN_MASK: u32 = 0x000FFE00; | ||
| 33 | pub(crate) const STATUS_F2_PKT_LEN_SHIFT: u32 = 9; | ||
| 34 | pub(crate) const STATUS_F3_PKT_AVAILABLE: u32 = 0x00100000; | ||
| 35 | pub(crate) const STATUS_F3_PKT_LEN_MASK: u32 = 0xFFE00000; | ||
| 36 | pub(crate) const STATUS_F3_PKT_LEN_SHIFT: u32 = 21; | ||
| 37 | |||
| 38 | pub(crate) const REG_BACKPLANE_GPIO_SELECT: u32 = 0x10005; | ||
| 39 | pub(crate) const REG_BACKPLANE_GPIO_OUTPUT: u32 = 0x10006; | ||
| 40 | pub(crate) const REG_BACKPLANE_GPIO_ENABLE: u32 = 0x10007; | ||
| 41 | pub(crate) const REG_BACKPLANE_FUNCTION2_WATERMARK: u32 = 0x10008; | ||
| 42 | pub(crate) const REG_BACKPLANE_DEVICE_CONTROL: u32 = 0x10009; | ||
| 43 | pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_LOW: u32 = 0x1000A; | ||
| 44 | pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_MID: u32 = 0x1000B; | ||
| 45 | pub(crate) const REG_BACKPLANE_BACKPLANE_ADDRESS_HIGH: u32 = 0x1000C; | ||
| 46 | pub(crate) const REG_BACKPLANE_FRAME_CONTROL: u32 = 0x1000D; | ||
| 47 | pub(crate) const REG_BACKPLANE_CHIP_CLOCK_CSR: u32 = 0x1000E; | ||
| 48 | pub(crate) const REG_BACKPLANE_PULL_UP: u32 = 0x1000F; | ||
| 49 | pub(crate) const REG_BACKPLANE_READ_FRAME_BC_LOW: u32 = 0x1001B; | ||
| 50 | pub(crate) const REG_BACKPLANE_READ_FRAME_BC_HIGH: u32 = 0x1001C; | ||
| 51 | pub(crate) const REG_BACKPLANE_WAKEUP_CTRL: u32 = 0x1001E; | ||
| 52 | pub(crate) const REG_BACKPLANE_SLEEP_CSR: u32 = 0x1001F; | ||
| 53 | |||
| 54 | pub(crate) const BACKPLANE_WINDOW_SIZE: usize = 0x8000; | ||
| 55 | pub(crate) const BACKPLANE_ADDRESS_MASK: u32 = 0x7FFF; | ||
| 56 | pub(crate) const BACKPLANE_ADDRESS_32BIT_FLAG: u32 = 0x08000; | ||
| 57 | pub(crate) const BACKPLANE_MAX_TRANSFER_SIZE: usize = 64; | ||
| 58 | // Active Low Power (ALP) clock constants | ||
| 59 | pub(crate) const BACKPLANE_ALP_AVAIL_REQ: u8 = 0x08; | ||
| 60 | pub(crate) const BACKPLANE_ALP_AVAIL: u8 = 0x40; | ||
| 61 | |||
| 62 | // Broadcom AMBA (Advanced Microcontroller Bus Architecture) Interconnect | ||
| 63 | // (AI) pub (crate) constants | ||
| 64 | pub(crate) const AI_IOCTRL_OFFSET: u32 = 0x408; | ||
| 65 | pub(crate) const AI_IOCTRL_BIT_FGC: u8 = 0x0002; | ||
| 66 | pub(crate) const AI_IOCTRL_BIT_CLOCK_EN: u8 = 0x0001; | ||
| 67 | pub(crate) const AI_IOCTRL_BIT_CPUHALT: u8 = 0x0020; | ||
| 68 | |||
| 69 | pub(crate) const AI_RESETCTRL_OFFSET: u32 = 0x800; | ||
| 70 | pub(crate) const AI_RESETCTRL_BIT_RESET: u8 = 1; | ||
| 71 | |||
| 72 | pub(crate) const AI_RESETSTATUS_OFFSET: u32 = 0x804; | ||
| 73 | |||
| 74 | pub(crate) const TEST_PATTERN: u32 = 0x12345678; | ||
| 75 | pub(crate) const FEEDBEAD: u32 = 0xFEEDBEAD; | ||
| 76 | |||
| 77 | // SPI_INTERRUPT_REGISTER and SPI_INTERRUPT_ENABLE_REGISTER Bits | ||
| 78 | pub(crate) const IRQ_DATA_UNAVAILABLE: u16 = 0x0001; // Requested data not available; Clear by writing a "1" | ||
| 79 | pub(crate) const IRQ_F2_F3_FIFO_RD_UNDERFLOW: u16 = 0x0002; | ||
| 80 | pub(crate) const IRQ_F2_F3_FIFO_WR_OVERFLOW: u16 = 0x0004; | ||
| 81 | pub(crate) const IRQ_COMMAND_ERROR: u16 = 0x0008; // Cleared by writing 1 | ||
| 82 | pub(crate) const IRQ_DATA_ERROR: u16 = 0x0010; // Cleared by writing 1 | ||
| 83 | pub(crate) const IRQ_F2_PACKET_AVAILABLE: u16 = 0x0020; | ||
| 84 | pub(crate) const IRQ_F3_PACKET_AVAILABLE: u16 = 0x0040; | ||
| 85 | pub(crate) const IRQ_F1_OVERFLOW: u16 = 0x0080; // Due to last write. Bkplane has pending write requests | ||
| 86 | pub(crate) const IRQ_MISC_INTR0: u16 = 0x0100; | ||
| 87 | pub(crate) const IRQ_MISC_INTR1: u16 = 0x0200; | ||
| 88 | pub(crate) const IRQ_MISC_INTR2: u16 = 0x0400; | ||
| 89 | pub(crate) const IRQ_MISC_INTR3: u16 = 0x0800; | ||
| 90 | pub(crate) const IRQ_MISC_INTR4: u16 = 0x1000; | ||
| 91 | pub(crate) const IRQ_F1_INTR: u16 = 0x2000; | ||
| 92 | pub(crate) const IRQ_F2_INTR: u16 = 0x4000; | ||
| 93 | pub(crate) const IRQ_F3_INTR: u16 = 0x8000; | ||
| 94 | |||
| 95 | pub(crate) const IOCTL_CMD_UP: u32 = 2; | ||
| 96 | pub(crate) const IOCTL_CMD_DOWN: u32 = 3; | ||
| 97 | pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26; | ||
| 98 | pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30; | ||
| 99 | pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64; | ||
| 100 | pub(crate) const IOCTL_CMD_SET_AP: u32 = 118; | ||
| 101 | pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263; | ||
| 102 | pub(crate) const IOCTL_CMD_GET_VAR: u32 = 262; | ||
| 103 | pub(crate) const IOCTL_CMD_SET_PASSPHRASE: u32 = 268; | ||
| 104 | |||
| 105 | pub(crate) const CHANNEL_TYPE_CONTROL: u8 = 0; | ||
| 106 | pub(crate) const CHANNEL_TYPE_EVENT: u8 = 1; | ||
| 107 | pub(crate) const CHANNEL_TYPE_DATA: u8 = 2; | ||
| 108 | |||
| 109 | // CYW_SPID command structure constants. | ||
| 110 | pub(crate) const WRITE: bool = true; | ||
| 111 | pub(crate) const READ: bool = false; | ||
| 112 | pub(crate) const INC_ADDR: bool = true; | ||
| 113 | pub(crate) const FIXED_ADDR: bool = false; | ||
| 114 | |||
| 115 | pub(crate) const AES_ENABLED: u32 = 0x0004; | ||
| 116 | pub(crate) const WPA2_SECURITY: u32 = 0x00400000; | ||
| 117 | |||
| 118 | pub(crate) const MIN_PSK_LEN: usize = 8; | ||
| 119 | pub(crate) const MAX_PSK_LEN: usize = 64; | ||
| 120 | |||
| 121 | // Security type (authentication and encryption types are combined using bit mask) | ||
| 122 | #[allow(non_camel_case_types)] | ||
| 123 | #[derive(Copy, Clone, PartialEq)] | ||
| 124 | #[repr(u32)] | ||
| 125 | pub(crate) enum Security { | ||
| 126 | OPEN = 0, | ||
| 127 | WPA2_AES_PSK = WPA2_SECURITY | AES_ENABLED, | ||
| 128 | } | ||
| 129 | |||
| 130 | #[allow(non_camel_case_types)] | ||
| 131 | #[derive(Copy, Clone)] | ||
| 132 | #[repr(u8)] | ||
| 133 | pub enum EStatus { | ||
| 134 | /// operation was successful | ||
| 135 | SUCCESS = 0, | ||
| 136 | /// operation failed | ||
| 137 | FAIL = 1, | ||
| 138 | /// operation timed out | ||
| 139 | TIMEOUT = 2, | ||
| 140 | /// failed due to no matching network found | ||
| 141 | NO_NETWORKS = 3, | ||
| 142 | /// operation was aborted | ||
| 143 | ABORT = 4, | ||
| 144 | /// protocol failure: packet not ack'd | ||
| 145 | NO_ACK = 5, | ||
| 146 | /// AUTH or ASSOC packet was unsolicited | ||
| 147 | UNSOLICITED = 6, | ||
| 148 | /// attempt to assoc to an auto auth configuration | ||
| 149 | ATTEMPT = 7, | ||
| 150 | /// scan results are incomplete | ||
| 151 | PARTIAL = 8, | ||
| 152 | /// scan aborted by another scan | ||
| 153 | NEWSCAN = 9, | ||
| 154 | /// scan aborted due to assoc in progress | ||
| 155 | NEWASSOC = 10, | ||
| 156 | /// 802.11h quiet period started | ||
| 157 | _11HQUIET = 11, | ||
| 158 | /// user disabled scanning (WLC_SET_SCANSUPPRESS) | ||
| 159 | SUPPRESS = 12, | ||
| 160 | /// no allowable channels to scan | ||
| 161 | NOCHANS = 13, | ||
| 162 | /// scan aborted due to CCX fast roam | ||
| 163 | CCXFASTRM = 14, | ||
| 164 | /// abort channel select | ||
| 165 | CS_ABORT = 15, | ||
| 166 | } | ||
| 167 | |||
| 168 | impl PartialEq<EStatus> for u32 { | ||
| 169 | fn eq(&self, other: &EStatus) -> bool { | ||
| 170 | *self == *other as Self | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | #[allow(dead_code)] | ||
| 175 | pub(crate) struct FormatStatus(pub u32); | ||
| 176 | |||
| 177 | #[cfg(feature = "defmt")] | ||
| 178 | impl defmt::Format for FormatStatus { | ||
| 179 | fn format(&self, fmt: defmt::Formatter) { | ||
| 180 | macro_rules! implm { | ||
| 181 | ($($name:ident),*) => { | ||
| 182 | $( | ||
| 183 | if self.0 & $name > 0 { | ||
| 184 | defmt::write!(fmt, " | {}", &stringify!($name)[7..]); | ||
| 185 | } | ||
| 186 | )* | ||
| 187 | }; | ||
| 188 | } | ||
| 189 | |||
| 190 | implm!( | ||
| 191 | STATUS_DATA_NOT_AVAILABLE, | ||
| 192 | STATUS_UNDERFLOW, | ||
| 193 | STATUS_OVERFLOW, | ||
| 194 | STATUS_F2_INTR, | ||
| 195 | STATUS_F3_INTR, | ||
| 196 | STATUS_F2_RX_READY, | ||
| 197 | STATUS_F3_RX_READY, | ||
| 198 | STATUS_HOST_CMD_DATA_ERR, | ||
| 199 | STATUS_F2_PKT_AVAILABLE, | ||
| 200 | STATUS_F3_PKT_AVAILABLE | ||
| 201 | ); | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | #[cfg(feature = "log")] | ||
| 206 | impl core::fmt::Debug for FormatStatus { | ||
| 207 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
| 208 | macro_rules! implm { | ||
| 209 | ($($name:ident),*) => { | ||
| 210 | $( | ||
| 211 | if self.0 & $name > 0 { | ||
| 212 | core::write!(fmt, " | {}", &stringify!($name)[7..])?; | ||
| 213 | } | ||
| 214 | )* | ||
| 215 | }; | ||
| 216 | } | ||
| 217 | |||
| 218 | implm!( | ||
| 219 | STATUS_DATA_NOT_AVAILABLE, | ||
| 220 | STATUS_UNDERFLOW, | ||
| 221 | STATUS_OVERFLOW, | ||
| 222 | STATUS_F2_INTR, | ||
| 223 | STATUS_F3_INTR, | ||
| 224 | STATUS_F2_RX_READY, | ||
| 225 | STATUS_F3_RX_READY, | ||
| 226 | STATUS_HOST_CMD_DATA_ERR, | ||
| 227 | STATUS_F2_PKT_AVAILABLE, | ||
| 228 | STATUS_F3_PKT_AVAILABLE | ||
| 229 | ); | ||
| 230 | Ok(()) | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | #[cfg(feature = "log")] | ||
| 235 | impl core::fmt::Display for FormatStatus { | ||
| 236 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 237 | core::fmt::Debug::fmt(self, f) | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 241 | #[allow(dead_code)] | ||
| 242 | pub(crate) struct FormatInterrupt(pub u16); | ||
| 243 | |||
| 244 | #[cfg(feature = "defmt")] | ||
| 245 | impl defmt::Format for FormatInterrupt { | ||
| 246 | fn format(&self, fmt: defmt::Formatter) { | ||
| 247 | macro_rules! implm { | ||
| 248 | ($($name:ident),*) => { | ||
| 249 | $( | ||
| 250 | if self.0 & $name > 0 { | ||
| 251 | defmt::write!(fmt, " | {}", &stringify!($name)[4..]); | ||
| 252 | } | ||
| 253 | )* | ||
| 254 | }; | ||
| 255 | } | ||
| 256 | |||
| 257 | implm!( | ||
| 258 | IRQ_DATA_UNAVAILABLE, | ||
| 259 | IRQ_F2_F3_FIFO_RD_UNDERFLOW, | ||
| 260 | IRQ_F2_F3_FIFO_WR_OVERFLOW, | ||
| 261 | IRQ_COMMAND_ERROR, | ||
| 262 | IRQ_DATA_ERROR, | ||
| 263 | IRQ_F2_PACKET_AVAILABLE, | ||
| 264 | IRQ_F3_PACKET_AVAILABLE, | ||
| 265 | IRQ_F1_OVERFLOW, | ||
| 266 | IRQ_MISC_INTR0, | ||
| 267 | IRQ_MISC_INTR1, | ||
| 268 | IRQ_MISC_INTR2, | ||
| 269 | IRQ_MISC_INTR3, | ||
| 270 | IRQ_MISC_INTR4, | ||
| 271 | IRQ_F1_INTR, | ||
| 272 | IRQ_F2_INTR, | ||
| 273 | IRQ_F3_INTR | ||
| 274 | ); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | #[cfg(feature = "log")] | ||
| 279 | impl core::fmt::Debug for FormatInterrupt { | ||
| 280 | fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
| 281 | macro_rules! implm { | ||
| 282 | ($($name:ident),*) => { | ||
| 283 | $( | ||
| 284 | if self.0 & $name > 0 { | ||
| 285 | core::write!(fmt, " | {}", &stringify!($name)[7..])?; | ||
| 286 | } | ||
| 287 | )* | ||
| 288 | }; | ||
| 289 | } | ||
| 290 | |||
| 291 | implm!( | ||
| 292 | IRQ_DATA_UNAVAILABLE, | ||
| 293 | IRQ_F2_F3_FIFO_RD_UNDERFLOW, | ||
| 294 | IRQ_F2_F3_FIFO_WR_OVERFLOW, | ||
| 295 | IRQ_COMMAND_ERROR, | ||
| 296 | IRQ_DATA_ERROR, | ||
| 297 | IRQ_F2_PACKET_AVAILABLE, | ||
| 298 | IRQ_F3_PACKET_AVAILABLE, | ||
| 299 | IRQ_F1_OVERFLOW, | ||
| 300 | IRQ_MISC_INTR0, | ||
| 301 | IRQ_MISC_INTR1, | ||
| 302 | IRQ_MISC_INTR2, | ||
| 303 | IRQ_MISC_INTR3, | ||
| 304 | IRQ_MISC_INTR4, | ||
| 305 | IRQ_F1_INTR, | ||
| 306 | IRQ_F2_INTR, | ||
| 307 | IRQ_F3_INTR | ||
| 308 | ); | ||
| 309 | Ok(()) | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | #[cfg(feature = "log")] | ||
| 314 | impl core::fmt::Display for FormatInterrupt { | ||
| 315 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 316 | core::fmt::Debug::fmt(self, f) | ||
| 317 | } | ||
| 318 | } | ||
diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs new file mode 100644 index 000000000..6919d569e --- /dev/null +++ b/cyw43/src/control.rs | |||
| @@ -0,0 +1,457 @@ | |||
| 1 | use core::cmp::{max, min}; | ||
| 2 | |||
| 3 | use ch::driver::LinkState; | ||
| 4 | use embassy_net_driver_channel as ch; | ||
| 5 | use embassy_time::{Duration, Timer}; | ||
| 6 | |||
| 7 | pub use crate::bus::SpiBusCyw43; | ||
| 8 | use crate::consts::*; | ||
| 9 | use crate::events::{Event, EventSubscriber, Events}; | ||
| 10 | use crate::fmt::Bytes; | ||
| 11 | use crate::ioctl::{IoctlState, IoctlType}; | ||
| 12 | use crate::structs::*; | ||
| 13 | use crate::{countries, events, PowerManagementMode}; | ||
| 14 | |||
| 15 | #[derive(Debug)] | ||
| 16 | pub struct Error { | ||
| 17 | pub status: u32, | ||
| 18 | } | ||
| 19 | |||
| 20 | pub struct Control<'a> { | ||
| 21 | state_ch: ch::StateRunner<'a>, | ||
| 22 | events: &'a Events, | ||
| 23 | ioctl_state: &'a IoctlState, | ||
| 24 | } | ||
| 25 | |||
| 26 | impl<'a> Control<'a> { | ||
| 27 | pub(crate) fn new(state_ch: ch::StateRunner<'a>, event_sub: &'a Events, ioctl_state: &'a IoctlState) -> Self { | ||
| 28 | Self { | ||
| 29 | state_ch, | ||
| 30 | events: event_sub, | ||
| 31 | ioctl_state, | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | pub async fn init(&mut self, clm: &[u8]) { | ||
| 36 | const CHUNK_SIZE: usize = 1024; | ||
| 37 | |||
| 38 | debug!("Downloading CLM..."); | ||
| 39 | |||
| 40 | let mut offs = 0; | ||
| 41 | for chunk in clm.chunks(CHUNK_SIZE) { | ||
| 42 | let mut flag = DOWNLOAD_FLAG_HANDLER_VER; | ||
| 43 | if offs == 0 { | ||
| 44 | flag |= DOWNLOAD_FLAG_BEGIN; | ||
| 45 | } | ||
| 46 | offs += chunk.len(); | ||
| 47 | if offs == clm.len() { | ||
| 48 | flag |= DOWNLOAD_FLAG_END; | ||
| 49 | } | ||
| 50 | |||
| 51 | let header = DownloadHeader { | ||
| 52 | flag, | ||
| 53 | dload_type: DOWNLOAD_TYPE_CLM, | ||
| 54 | len: chunk.len() as _, | ||
| 55 | crc: 0, | ||
| 56 | }; | ||
| 57 | let mut buf = [0; 8 + 12 + CHUNK_SIZE]; | ||
| 58 | buf[0..8].copy_from_slice(b"clmload\x00"); | ||
| 59 | buf[8..20].copy_from_slice(&header.to_bytes()); | ||
| 60 | buf[20..][..chunk.len()].copy_from_slice(&chunk); | ||
| 61 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..8 + 12 + chunk.len()]) | ||
| 62 | .await; | ||
| 63 | } | ||
| 64 | |||
| 65 | // check clmload ok | ||
| 66 | assert_eq!(self.get_iovar_u32("clmload_status").await, 0); | ||
| 67 | |||
| 68 | debug!("Configuring misc stuff..."); | ||
| 69 | |||
| 70 | // Disable tx gloming which transfers multiple packets in one request. | ||
| 71 | // 'glom' is short for "conglomerate" which means "gather together into | ||
| 72 | // a compact mass". | ||
| 73 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 74 | self.set_iovar_u32("apsta", 1).await; | ||
| 75 | |||
| 76 | // read MAC addr. | ||
| 77 | let mut mac_addr = [0; 6]; | ||
| 78 | assert_eq!(self.get_iovar("cur_etheraddr", &mut mac_addr).await, 6); | ||
| 79 | debug!("mac addr: {:02x}", Bytes(&mac_addr)); | ||
| 80 | |||
| 81 | let country = countries::WORLD_WIDE_XX; | ||
| 82 | let country_info = CountryInfo { | ||
| 83 | country_abbrev: [country.code[0], country.code[1], 0, 0], | ||
| 84 | country_code: [country.code[0], country.code[1], 0, 0], | ||
| 85 | rev: if country.rev == 0 { -1 } else { country.rev as _ }, | ||
| 86 | }; | ||
| 87 | self.set_iovar("country", &country_info.to_bytes()).await; | ||
| 88 | |||
| 89 | // set country takes some time, next ioctls fail if we don't wait. | ||
| 90 | Timer::after(Duration::from_millis(100)).await; | ||
| 91 | |||
| 92 | // Set antenna to chip antenna | ||
| 93 | self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await; | ||
| 94 | |||
| 95 | self.set_iovar_u32("bus:txglom", 0).await; | ||
| 96 | Timer::after(Duration::from_millis(100)).await; | ||
| 97 | //self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...?? | ||
| 98 | //Timer::after(Duration::from_millis(100)).await; | ||
| 99 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 100 | Timer::after(Duration::from_millis(100)).await; | ||
| 101 | self.set_iovar_u32("ampdu_mpdu", 4).await; | ||
| 102 | Timer::after(Duration::from_millis(100)).await; | ||
| 103 | //self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes | ||
| 104 | |||
| 105 | //Timer::after(Duration::from_millis(100)).await; | ||
| 106 | |||
| 107 | // evts | ||
| 108 | let mut evts = EventMask { | ||
| 109 | iface: 0, | ||
| 110 | events: [0xFF; 24], | ||
| 111 | }; | ||
| 112 | |||
| 113 | // Disable spammy uninteresting events. | ||
| 114 | evts.unset(Event::RADIO); | ||
| 115 | evts.unset(Event::IF); | ||
| 116 | evts.unset(Event::PROBREQ_MSG); | ||
| 117 | evts.unset(Event::PROBREQ_MSG_RX); | ||
| 118 | evts.unset(Event::PROBRESP_MSG); | ||
| 119 | evts.unset(Event::PROBRESP_MSG); | ||
| 120 | evts.unset(Event::ROAM); | ||
| 121 | |||
| 122 | self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await; | ||
| 123 | |||
| 124 | Timer::after(Duration::from_millis(100)).await; | ||
| 125 | |||
| 126 | // set wifi up | ||
| 127 | self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||
| 128 | |||
| 129 | Timer::after(Duration::from_millis(100)).await; | ||
| 130 | |||
| 131 | self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto | ||
| 132 | self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any | ||
| 133 | |||
| 134 | Timer::after(Duration::from_millis(100)).await; | ||
| 135 | |||
| 136 | self.state_ch.set_ethernet_address(mac_addr); | ||
| 137 | |||
| 138 | debug!("INIT DONE"); | ||
| 139 | } | ||
| 140 | |||
| 141 | pub async fn set_power_management(&mut self, mode: PowerManagementMode) { | ||
| 142 | // power save mode | ||
| 143 | let mode_num = mode.mode(); | ||
| 144 | if mode_num == 2 { | ||
| 145 | self.set_iovar_u32("pm2_sleep_ret", mode.sleep_ret_ms() as u32).await; | ||
| 146 | self.set_iovar_u32("bcn_li_bcn", mode.beacon_period() as u32).await; | ||
| 147 | self.set_iovar_u32("bcn_li_dtim", mode.dtim_period() as u32).await; | ||
| 148 | self.set_iovar_u32("assoc_listen", mode.assoc() as u32).await; | ||
| 149 | } | ||
| 150 | self.ioctl_set_u32(86, 0, mode_num).await; | ||
| 151 | } | ||
| 152 | |||
| 153 | pub async fn join_open(&mut self, ssid: &str) -> Result<(), Error> { | ||
| 154 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 155 | |||
| 156 | self.ioctl_set_u32(134, 0, 0).await; // wsec = open | ||
| 157 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 0).await; | ||
| 158 | self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 | ||
| 159 | self.ioctl_set_u32(22, 0, 0).await; // set_auth = open (0) | ||
| 160 | |||
| 161 | let mut i = SsidInfo { | ||
| 162 | len: ssid.len() as _, | ||
| 163 | ssid: [0; 32], | ||
| 164 | }; | ||
| 165 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | ||
| 166 | |||
| 167 | self.wait_for_join(i).await | ||
| 168 | } | ||
| 169 | |||
| 170 | pub async fn join_wpa2(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> { | ||
| 171 | self.set_iovar_u32("ampdu_ba_wsize", 8).await; | ||
| 172 | |||
| 173 | self.ioctl_set_u32(134, 0, 4).await; // wsec = wpa2 | ||
| 174 | self.set_iovar_u32x2("bsscfg:sup_wpa", 0, 1).await; | ||
| 175 | self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await; | ||
| 176 | self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await; | ||
| 177 | |||
| 178 | Timer::after(Duration::from_millis(100)).await; | ||
| 179 | |||
| 180 | let mut pfi = PassphraseInfo { | ||
| 181 | len: passphrase.len() as _, | ||
| 182 | flags: 1, | ||
| 183 | passphrase: [0; 64], | ||
| 184 | }; | ||
| 185 | pfi.passphrase[..passphrase.len()].copy_from_slice(passphrase.as_bytes()); | ||
| 186 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) | ||
| 187 | .await; // WLC_SET_WSEC_PMK | ||
| 188 | |||
| 189 | self.ioctl_set_u32(20, 0, 1).await; // set_infra = 1 | ||
| 190 | self.ioctl_set_u32(22, 0, 0).await; // set_auth = 0 (open) | ||
| 191 | self.ioctl_set_u32(165, 0, 0x80).await; // set_wpa_auth | ||
| 192 | |||
| 193 | let mut i = SsidInfo { | ||
| 194 | len: ssid.len() as _, | ||
| 195 | ssid: [0; 32], | ||
| 196 | }; | ||
| 197 | i.ssid[..ssid.len()].copy_from_slice(ssid.as_bytes()); | ||
| 198 | |||
| 199 | self.wait_for_join(i).await | ||
| 200 | } | ||
| 201 | |||
| 202 | async fn wait_for_join(&mut self, i: SsidInfo) -> Result<(), Error> { | ||
| 203 | self.events.mask.enable(&[Event::SET_SSID, Event::AUTH]); | ||
| 204 | let mut subscriber = self.events.queue.subscriber().unwrap(); | ||
| 205 | // the actual join operation starts here | ||
| 206 | // we make sure to enable events before so we don't miss any | ||
| 207 | |||
| 208 | // set_ssid | ||
| 209 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_SSID, 0, &mut i.to_bytes()) | ||
| 210 | .await; | ||
| 211 | |||
| 212 | // to complete the join, we wait for a SET_SSID event | ||
| 213 | // we also save the AUTH status for the user, it may be interesting | ||
| 214 | let mut auth_status = 0; | ||
| 215 | let status = loop { | ||
| 216 | let msg = subscriber.next_message_pure().await; | ||
| 217 | if msg.header.event_type == Event::AUTH && msg.header.status != EStatus::SUCCESS { | ||
| 218 | auth_status = msg.header.status; | ||
| 219 | } else if msg.header.event_type == Event::SET_SSID { | ||
| 220 | // join operation ends with SET_SSID event | ||
| 221 | break msg.header.status; | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | |||
| 225 | self.events.mask.disable_all(); | ||
| 226 | if status == EStatus::SUCCESS { | ||
| 227 | // successful join | ||
| 228 | self.state_ch.set_link_state(LinkState::Up); | ||
| 229 | debug!("JOINED"); | ||
| 230 | Ok(()) | ||
| 231 | } else { | ||
| 232 | warn!("JOIN failed with status={} auth={}", status, auth_status); | ||
| 233 | Err(Error { status }) | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | pub async fn gpio_set(&mut self, gpio_n: u8, gpio_en: bool) { | ||
| 238 | assert!(gpio_n < 3); | ||
| 239 | self.set_iovar_u32x2("gpioout", 1 << gpio_n, if gpio_en { 1 << gpio_n } else { 0 }) | ||
| 240 | .await | ||
| 241 | } | ||
| 242 | |||
| 243 | pub async fn start_ap_open(&mut self, ssid: &str, channel: u8) { | ||
| 244 | self.start_ap(ssid, "", Security::OPEN, channel).await; | ||
| 245 | } | ||
| 246 | |||
| 247 | pub async fn start_ap_wpa2(&mut self, ssid: &str, passphrase: &str, channel: u8) { | ||
| 248 | self.start_ap(ssid, passphrase, Security::WPA2_AES_PSK, channel).await; | ||
| 249 | } | ||
| 250 | |||
| 251 | async fn start_ap(&mut self, ssid: &str, passphrase: &str, security: Security, channel: u8) { | ||
| 252 | if security != Security::OPEN | ||
| 253 | && (passphrase.as_bytes().len() < MIN_PSK_LEN || passphrase.as_bytes().len() > MAX_PSK_LEN) | ||
| 254 | { | ||
| 255 | panic!("Passphrase is too short or too long"); | ||
| 256 | } | ||
| 257 | |||
| 258 | // Temporarily set wifi down | ||
| 259 | self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await; | ||
| 260 | |||
| 261 | // Turn off APSTA mode | ||
| 262 | self.set_iovar_u32("apsta", 0).await; | ||
| 263 | |||
| 264 | // Set wifi up again | ||
| 265 | self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await; | ||
| 266 | |||
| 267 | // Turn on AP mode | ||
| 268 | self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await; | ||
| 269 | |||
| 270 | // Set SSID | ||
| 271 | let mut i = SsidInfoWithIndex { | ||
| 272 | index: 0, | ||
| 273 | ssid_info: SsidInfo { | ||
| 274 | len: ssid.as_bytes().len() as _, | ||
| 275 | ssid: [0; 32], | ||
| 276 | }, | ||
| 277 | }; | ||
| 278 | i.ssid_info.ssid[..ssid.as_bytes().len()].copy_from_slice(ssid.as_bytes()); | ||
| 279 | self.set_iovar("bsscfg:ssid", &i.to_bytes()).await; | ||
| 280 | |||
| 281 | // Set channel number | ||
| 282 | self.ioctl_set_u32(IOCTL_CMD_SET_CHANNEL, 0, channel as u32).await; | ||
| 283 | |||
| 284 | // Set security | ||
| 285 | self.set_iovar_u32x2("bsscfg:wsec", 0, (security as u32) & 0xFF).await; | ||
| 286 | |||
| 287 | if security != Security::OPEN { | ||
| 288 | self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK | ||
| 289 | |||
| 290 | Timer::after(Duration::from_millis(100)).await; | ||
| 291 | |||
| 292 | // Set passphrase | ||
| 293 | let mut pfi = PassphraseInfo { | ||
| 294 | len: passphrase.as_bytes().len() as _, | ||
| 295 | flags: 1, // WSEC_PASSPHRASE | ||
| 296 | passphrase: [0; 64], | ||
| 297 | }; | ||
| 298 | pfi.passphrase[..passphrase.as_bytes().len()].copy_from_slice(passphrase.as_bytes()); | ||
| 299 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_PASSPHRASE, 0, &mut pfi.to_bytes()) | ||
| 300 | .await; | ||
| 301 | } | ||
| 302 | |||
| 303 | // Change mutlicast rate from 1 Mbps to 11 Mbps | ||
| 304 | self.set_iovar_u32("2g_mrate", 11000000 / 500000).await; | ||
| 305 | |||
| 306 | // Start AP | ||
| 307 | self.set_iovar_u32x2("bss", 0, 1).await; // bss = BSS_UP | ||
| 308 | } | ||
| 309 | |||
| 310 | async fn set_iovar_u32x2(&mut self, name: &str, val1: u32, val2: u32) { | ||
| 311 | let mut buf = [0; 8]; | ||
| 312 | buf[0..4].copy_from_slice(&val1.to_le_bytes()); | ||
| 313 | buf[4..8].copy_from_slice(&val2.to_le_bytes()); | ||
| 314 | self.set_iovar(name, &buf).await | ||
| 315 | } | ||
| 316 | |||
| 317 | async fn set_iovar_u32(&mut self, name: &str, val: u32) { | ||
| 318 | self.set_iovar(name, &val.to_le_bytes()).await | ||
| 319 | } | ||
| 320 | |||
| 321 | async fn get_iovar_u32(&mut self, name: &str) -> u32 { | ||
| 322 | let mut buf = [0; 4]; | ||
| 323 | let len = self.get_iovar(name, &mut buf).await; | ||
| 324 | assert_eq!(len, 4); | ||
| 325 | u32::from_le_bytes(buf) | ||
| 326 | } | ||
| 327 | |||
| 328 | async fn set_iovar(&mut self, name: &str, val: &[u8]) { | ||
| 329 | self.set_iovar_v::<64>(name, val).await | ||
| 330 | } | ||
| 331 | |||
| 332 | async fn set_iovar_v<const BUFSIZE: usize>(&mut self, name: &str, val: &[u8]) { | ||
| 333 | debug!("set {} = {:02x}", name, Bytes(val)); | ||
| 334 | |||
| 335 | let mut buf = [0; BUFSIZE]; | ||
| 336 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 337 | buf[name.len()] = 0; | ||
| 338 | buf[name.len() + 1..][..val.len()].copy_from_slice(val); | ||
| 339 | |||
| 340 | let total_len = name.len() + 1 + val.len(); | ||
| 341 | self.ioctl(IoctlType::Set, IOCTL_CMD_SET_VAR, 0, &mut buf[..total_len]) | ||
| 342 | .await; | ||
| 343 | } | ||
| 344 | |||
| 345 | // TODO this is not really working, it always returns all zeros. | ||
| 346 | async fn get_iovar(&mut self, name: &str, res: &mut [u8]) -> usize { | ||
| 347 | debug!("get {}", name); | ||
| 348 | |||
| 349 | let mut buf = [0; 64]; | ||
| 350 | buf[..name.len()].copy_from_slice(name.as_bytes()); | ||
| 351 | buf[name.len()] = 0; | ||
| 352 | |||
| 353 | let total_len = max(name.len() + 1, res.len()); | ||
| 354 | let res_len = self | ||
| 355 | .ioctl(IoctlType::Get, IOCTL_CMD_GET_VAR, 0, &mut buf[..total_len]) | ||
| 356 | .await; | ||
| 357 | |||
| 358 | let out_len = min(res.len(), res_len); | ||
| 359 | res[..out_len].copy_from_slice(&buf[..out_len]); | ||
| 360 | out_len | ||
| 361 | } | ||
| 362 | |||
| 363 | async fn ioctl_set_u32(&mut self, cmd: u32, iface: u32, val: u32) { | ||
| 364 | let mut buf = val.to_le_bytes(); | ||
| 365 | self.ioctl(IoctlType::Set, cmd, iface, &mut buf).await; | ||
| 366 | } | ||
| 367 | |||
| 368 | async fn ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { | ||
| 369 | struct CancelOnDrop<'a>(&'a IoctlState); | ||
| 370 | |||
| 371 | impl CancelOnDrop<'_> { | ||
| 372 | fn defuse(self) { | ||
| 373 | core::mem::forget(self); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | impl Drop for CancelOnDrop<'_> { | ||
| 378 | fn drop(&mut self) { | ||
| 379 | self.0.cancel_ioctl(); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | let ioctl = CancelOnDrop(self.ioctl_state); | ||
| 384 | |||
| 385 | ioctl.0.do_ioctl(kind, cmd, iface, buf).await; | ||
| 386 | let resp_len = ioctl.0.wait_complete().await; | ||
| 387 | |||
| 388 | ioctl.defuse(); | ||
| 389 | |||
| 390 | resp_len | ||
| 391 | } | ||
| 392 | |||
| 393 | /// Start a wifi scan | ||
| 394 | /// | ||
| 395 | /// Returns a `Stream` of networks found by the device | ||
| 396 | /// | ||
| 397 | /// # Note | ||
| 398 | /// Device events are currently implemented using a bounded queue. | ||
| 399 | /// To not miss any events, you should make sure to always await the stream. | ||
| 400 | pub async fn scan(&mut self) -> Scanner<'_> { | ||
| 401 | const SCANTYPE_PASSIVE: u8 = 1; | ||
| 402 | |||
| 403 | let scan_params = ScanParams { | ||
| 404 | version: 1, | ||
| 405 | action: 1, | ||
| 406 | sync_id: 1, | ||
| 407 | ssid_len: 0, | ||
| 408 | ssid: [0; 32], | ||
| 409 | bssid: [0xff; 6], | ||
| 410 | bss_type: 2, | ||
| 411 | scan_type: SCANTYPE_PASSIVE, | ||
| 412 | nprobes: !0, | ||
| 413 | active_time: !0, | ||
| 414 | passive_time: !0, | ||
| 415 | home_time: !0, | ||
| 416 | channel_num: 0, | ||
| 417 | channel_list: [0; 1], | ||
| 418 | }; | ||
| 419 | |||
| 420 | self.events.mask.enable(&[Event::ESCAN_RESULT]); | ||
| 421 | let subscriber = self.events.queue.subscriber().unwrap(); | ||
| 422 | self.set_iovar_v::<256>("escan", &scan_params.to_bytes()).await; | ||
| 423 | |||
| 424 | Scanner { | ||
| 425 | subscriber, | ||
| 426 | events: &self.events, | ||
| 427 | } | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | pub struct Scanner<'a> { | ||
| 432 | subscriber: EventSubscriber<'a>, | ||
| 433 | events: &'a Events, | ||
| 434 | } | ||
| 435 | |||
| 436 | impl Scanner<'_> { | ||
| 437 | /// wait for the next found network | ||
| 438 | pub async fn next(&mut self) -> Option<BssInfo> { | ||
| 439 | let event = self.subscriber.next_message_pure().await; | ||
| 440 | if event.header.status != EStatus::PARTIAL { | ||
| 441 | self.events.mask.disable_all(); | ||
| 442 | return None; | ||
| 443 | } | ||
| 444 | |||
| 445 | if let events::Payload::BssInfo(bss) = event.payload { | ||
| 446 | Some(bss) | ||
| 447 | } else { | ||
| 448 | None | ||
| 449 | } | ||
| 450 | } | ||
| 451 | } | ||
| 452 | |||
| 453 | impl Drop for Scanner<'_> { | ||
| 454 | fn drop(&mut self) { | ||
| 455 | self.events.mask.disable_all(); | ||
| 456 | } | ||
| 457 | } | ||
diff --git a/cyw43/src/countries.rs b/cyw43/src/countries.rs new file mode 100644 index 000000000..fa1e8cace --- /dev/null +++ b/cyw43/src/countries.rs | |||
| @@ -0,0 +1,481 @@ | |||
| 1 | #![allow(unused)] | ||
| 2 | |||
| 3 | pub struct Country { | ||
| 4 | pub code: [u8; 2], | ||
| 5 | pub rev: u16, | ||
| 6 | } | ||
| 7 | |||
| 8 | /// AF Afghanistan | ||
| 9 | pub const AFGHANISTAN: Country = Country { code: *b"AF", rev: 0 }; | ||
| 10 | /// AL Albania | ||
| 11 | pub const ALBANIA: Country = Country { code: *b"AL", rev: 0 }; | ||
| 12 | /// DZ Algeria | ||
| 13 | pub const ALGERIA: Country = Country { code: *b"DZ", rev: 0 }; | ||
| 14 | /// AS American_Samoa | ||
| 15 | pub const AMERICAN_SAMOA: Country = Country { code: *b"AS", rev: 0 }; | ||
| 16 | /// AO Angola | ||
| 17 | pub const ANGOLA: Country = Country { code: *b"AO", rev: 0 }; | ||
| 18 | /// AI Anguilla | ||
| 19 | pub const ANGUILLA: Country = Country { code: *b"AI", rev: 0 }; | ||
| 20 | /// AG Antigua_and_Barbuda | ||
| 21 | pub const ANTIGUA_AND_BARBUDA: Country = Country { code: *b"AG", rev: 0 }; | ||
| 22 | /// AR Argentina | ||
| 23 | pub const ARGENTINA: Country = Country { code: *b"AR", rev: 0 }; | ||
| 24 | /// AM Armenia | ||
| 25 | pub const ARMENIA: Country = Country { code: *b"AM", rev: 0 }; | ||
| 26 | /// AW Aruba | ||
| 27 | pub const ARUBA: Country = Country { code: *b"AW", rev: 0 }; | ||
| 28 | /// AU Australia | ||
| 29 | pub const AUSTRALIA: Country = Country { code: *b"AU", rev: 0 }; | ||
| 30 | /// AT Austria | ||
| 31 | pub const AUSTRIA: Country = Country { code: *b"AT", rev: 0 }; | ||
| 32 | /// AZ Azerbaijan | ||
| 33 | pub const AZERBAIJAN: Country = Country { code: *b"AZ", rev: 0 }; | ||
| 34 | /// BS Bahamas | ||
| 35 | pub const BAHAMAS: Country = Country { code: *b"BS", rev: 0 }; | ||
| 36 | /// BH Bahrain | ||
| 37 | pub const BAHRAIN: Country = Country { code: *b"BH", rev: 0 }; | ||
| 38 | /// 0B Baker_Island | ||
| 39 | pub const BAKER_ISLAND: Country = Country { code: *b"0B", rev: 0 }; | ||
| 40 | /// BD Bangladesh | ||
| 41 | pub const BANGLADESH: Country = Country { code: *b"BD", rev: 0 }; | ||
| 42 | /// BB Barbados | ||
| 43 | pub const BARBADOS: Country = Country { code: *b"BB", rev: 0 }; | ||
| 44 | /// BY Belarus | ||
| 45 | pub const BELARUS: Country = Country { code: *b"BY", rev: 0 }; | ||
| 46 | /// BE Belgium | ||
| 47 | pub const BELGIUM: Country = Country { code: *b"BE", rev: 0 }; | ||
| 48 | /// BZ Belize | ||
| 49 | pub const BELIZE: Country = Country { code: *b"BZ", rev: 0 }; | ||
| 50 | /// BJ Benin | ||
| 51 | pub const BENIN: Country = Country { code: *b"BJ", rev: 0 }; | ||
| 52 | /// BM Bermuda | ||
| 53 | pub const BERMUDA: Country = Country { code: *b"BM", rev: 0 }; | ||
| 54 | /// BT Bhutan | ||
| 55 | pub const BHUTAN: Country = Country { code: *b"BT", rev: 0 }; | ||
| 56 | /// BO Bolivia | ||
| 57 | pub const BOLIVIA: Country = Country { code: *b"BO", rev: 0 }; | ||
| 58 | /// BA Bosnia_and_Herzegovina | ||
| 59 | pub const BOSNIA_AND_HERZEGOVINA: Country = Country { code: *b"BA", rev: 0 }; | ||
| 60 | /// BW Botswana | ||
| 61 | pub const BOTSWANA: Country = Country { code: *b"BW", rev: 0 }; | ||
| 62 | /// BR Brazil | ||
| 63 | pub const BRAZIL: Country = Country { code: *b"BR", rev: 0 }; | ||
| 64 | /// IO British_Indian_Ocean_Territory | ||
| 65 | pub const BRITISH_INDIAN_OCEAN_TERRITORY: Country = Country { code: *b"IO", rev: 0 }; | ||
| 66 | /// BN Brunei_Darussalam | ||
| 67 | pub const BRUNEI_DARUSSALAM: Country = Country { code: *b"BN", rev: 0 }; | ||
| 68 | /// BG Bulgaria | ||
| 69 | pub const BULGARIA: Country = Country { code: *b"BG", rev: 0 }; | ||
| 70 | /// BF Burkina_Faso | ||
| 71 | pub const BURKINA_FASO: Country = Country { code: *b"BF", rev: 0 }; | ||
| 72 | /// BI Burundi | ||
| 73 | pub const BURUNDI: Country = Country { code: *b"BI", rev: 0 }; | ||
| 74 | /// KH Cambodia | ||
| 75 | pub const CAMBODIA: Country = Country { code: *b"KH", rev: 0 }; | ||
| 76 | /// CM Cameroon | ||
| 77 | pub const CAMEROON: Country = Country { code: *b"CM", rev: 0 }; | ||
| 78 | /// CA Canada | ||
| 79 | pub const CANADA: Country = Country { code: *b"CA", rev: 0 }; | ||
| 80 | /// CA Canada Revision 950 | ||
| 81 | pub const CANADA_REV950: Country = Country { code: *b"CA", rev: 950 }; | ||
| 82 | /// CV Cape_Verde | ||
| 83 | pub const CAPE_VERDE: Country = Country { code: *b"CV", rev: 0 }; | ||
| 84 | /// KY Cayman_Islands | ||
| 85 | pub const CAYMAN_ISLANDS: Country = Country { code: *b"KY", rev: 0 }; | ||
| 86 | /// CF Central_African_Republic | ||
| 87 | pub const CENTRAL_AFRICAN_REPUBLIC: Country = Country { code: *b"CF", rev: 0 }; | ||
| 88 | /// TD Chad | ||
| 89 | pub const CHAD: Country = Country { code: *b"TD", rev: 0 }; | ||
| 90 | /// CL Chile | ||
| 91 | pub const CHILE: Country = Country { code: *b"CL", rev: 0 }; | ||
| 92 | /// CN China | ||
| 93 | pub const CHINA: Country = Country { code: *b"CN", rev: 0 }; | ||
| 94 | /// CX Christmas_Island | ||
| 95 | pub const CHRISTMAS_ISLAND: Country = Country { code: *b"CX", rev: 0 }; | ||
| 96 | /// CO Colombia | ||
| 97 | pub const COLOMBIA: Country = Country { code: *b"CO", rev: 0 }; | ||
| 98 | /// KM Comoros | ||
| 99 | pub const COMOROS: Country = Country { code: *b"KM", rev: 0 }; | ||
| 100 | /// CG Congo | ||
| 101 | pub const CONGO: Country = Country { code: *b"CG", rev: 0 }; | ||
| 102 | /// CD Congo,_The_Democratic_Republic_Of_The | ||
| 103 | pub const CONGO_THE_DEMOCRATIC_REPUBLIC_OF_THE: Country = Country { code: *b"CD", rev: 0 }; | ||
| 104 | /// CR Costa_Rica | ||
| 105 | pub const COSTA_RICA: Country = Country { code: *b"CR", rev: 0 }; | ||
| 106 | /// CI Cote_D'ivoire | ||
| 107 | pub const COTE_DIVOIRE: Country = Country { code: *b"CI", rev: 0 }; | ||
| 108 | /// HR Croatia | ||
| 109 | pub const CROATIA: Country = Country { code: *b"HR", rev: 0 }; | ||
| 110 | /// CU Cuba | ||
| 111 | pub const CUBA: Country = Country { code: *b"CU", rev: 0 }; | ||
| 112 | /// CY Cyprus | ||
| 113 | pub const CYPRUS: Country = Country { code: *b"CY", rev: 0 }; | ||
| 114 | /// CZ Czech_Republic | ||
| 115 | pub const CZECH_REPUBLIC: Country = Country { code: *b"CZ", rev: 0 }; | ||
| 116 | /// DK Denmark | ||
| 117 | pub const DENMARK: Country = Country { code: *b"DK", rev: 0 }; | ||
| 118 | /// DJ Djibouti | ||
| 119 | pub const DJIBOUTI: Country = Country { code: *b"DJ", rev: 0 }; | ||
| 120 | /// DM Dominica | ||
| 121 | pub const DOMINICA: Country = Country { code: *b"DM", rev: 0 }; | ||
| 122 | /// DO Dominican_Republic | ||
| 123 | pub const DOMINICAN_REPUBLIC: Country = Country { code: *b"DO", rev: 0 }; | ||
| 124 | /// AU G'Day mate! | ||
| 125 | pub const DOWN_UNDER: Country = Country { code: *b"AU", rev: 0 }; | ||
| 126 | /// EC Ecuador | ||
| 127 | pub const ECUADOR: Country = Country { code: *b"EC", rev: 0 }; | ||
| 128 | /// EG Egypt | ||
| 129 | pub const EGYPT: Country = Country { code: *b"EG", rev: 0 }; | ||
| 130 | /// SV El_Salvador | ||
| 131 | pub const EL_SALVADOR: Country = Country { code: *b"SV", rev: 0 }; | ||
| 132 | /// GQ Equatorial_Guinea | ||
| 133 | pub const EQUATORIAL_GUINEA: Country = Country { code: *b"GQ", rev: 0 }; | ||
| 134 | /// ER Eritrea | ||
| 135 | pub const ERITREA: Country = Country { code: *b"ER", rev: 0 }; | ||
| 136 | /// EE Estonia | ||
| 137 | pub const ESTONIA: Country = Country { code: *b"EE", rev: 0 }; | ||
| 138 | /// ET Ethiopia | ||
| 139 | pub const ETHIOPIA: Country = Country { code: *b"ET", rev: 0 }; | ||
| 140 | /// FK Falkland_Islands_(Malvinas) | ||
| 141 | pub const FALKLAND_ISLANDS_MALVINAS: Country = Country { code: *b"FK", rev: 0 }; | ||
| 142 | /// FO Faroe_Islands | ||
| 143 | pub const FAROE_ISLANDS: Country = Country { code: *b"FO", rev: 0 }; | ||
| 144 | /// FJ Fiji | ||
| 145 | pub const FIJI: Country = Country { code: *b"FJ", rev: 0 }; | ||
| 146 | /// FI Finland | ||
| 147 | pub const FINLAND: Country = Country { code: *b"FI", rev: 0 }; | ||
| 148 | /// FR France | ||
| 149 | pub const FRANCE: Country = Country { code: *b"FR", rev: 0 }; | ||
| 150 | /// GF French_Guina | ||
| 151 | pub const FRENCH_GUINA: Country = Country { code: *b"GF", rev: 0 }; | ||
| 152 | /// PF French_Polynesia | ||
| 153 | pub const FRENCH_POLYNESIA: Country = Country { code: *b"PF", rev: 0 }; | ||
| 154 | /// TF French_Southern_Territories | ||
| 155 | pub const FRENCH_SOUTHERN_TERRITORIES: Country = Country { code: *b"TF", rev: 0 }; | ||
| 156 | /// GA Gabon | ||
| 157 | pub const GABON: Country = Country { code: *b"GA", rev: 0 }; | ||
| 158 | /// GM Gambia | ||
| 159 | pub const GAMBIA: Country = Country { code: *b"GM", rev: 0 }; | ||
| 160 | /// GE Georgia | ||
| 161 | pub const GEORGIA: Country = Country { code: *b"GE", rev: 0 }; | ||
| 162 | /// DE Germany | ||
| 163 | pub const GERMANY: Country = Country { code: *b"DE", rev: 0 }; | ||
| 164 | /// E0 European_Wide Revision 895 | ||
| 165 | pub const EUROPEAN_WIDE_REV895: Country = Country { code: *b"E0", rev: 895 }; | ||
| 166 | /// GH Ghana | ||
| 167 | pub const GHANA: Country = Country { code: *b"GH", rev: 0 }; | ||
| 168 | /// GI Gibraltar | ||
| 169 | pub const GIBRALTAR: Country = Country { code: *b"GI", rev: 0 }; | ||
| 170 | /// GR Greece | ||
| 171 | pub const GREECE: Country = Country { code: *b"GR", rev: 0 }; | ||
| 172 | /// GD Grenada | ||
| 173 | pub const GRENADA: Country = Country { code: *b"GD", rev: 0 }; | ||
| 174 | /// GP Guadeloupe | ||
| 175 | pub const GUADELOUPE: Country = Country { code: *b"GP", rev: 0 }; | ||
| 176 | /// GU Guam | ||
| 177 | pub const GUAM: Country = Country { code: *b"GU", rev: 0 }; | ||
| 178 | /// GT Guatemala | ||
| 179 | pub const GUATEMALA: Country = Country { code: *b"GT", rev: 0 }; | ||
| 180 | /// GG Guernsey | ||
| 181 | pub const GUERNSEY: Country = Country { code: *b"GG", rev: 0 }; | ||
| 182 | /// GN Guinea | ||
| 183 | pub const GUINEA: Country = Country { code: *b"GN", rev: 0 }; | ||
| 184 | /// GW Guinea-bissau | ||
| 185 | pub const GUINEA_BISSAU: Country = Country { code: *b"GW", rev: 0 }; | ||
| 186 | /// GY Guyana | ||
| 187 | pub const GUYANA: Country = Country { code: *b"GY", rev: 0 }; | ||
| 188 | /// HT Haiti | ||
| 189 | pub const HAITI: Country = Country { code: *b"HT", rev: 0 }; | ||
| 190 | /// VA Holy_See_(Vatican_City_State) | ||
| 191 | pub const HOLY_SEE_VATICAN_CITY_STATE: Country = Country { code: *b"VA", rev: 0 }; | ||
| 192 | /// HN Honduras | ||
| 193 | pub const HONDURAS: Country = Country { code: *b"HN", rev: 0 }; | ||
| 194 | /// HK Hong_Kong | ||
| 195 | pub const HONG_KONG: Country = Country { code: *b"HK", rev: 0 }; | ||
| 196 | /// HU Hungary | ||
| 197 | pub const HUNGARY: Country = Country { code: *b"HU", rev: 0 }; | ||
| 198 | /// IS Iceland | ||
| 199 | pub const ICELAND: Country = Country { code: *b"IS", rev: 0 }; | ||
| 200 | /// IN India | ||
| 201 | pub const INDIA: Country = Country { code: *b"IN", rev: 0 }; | ||
| 202 | /// ID Indonesia | ||
| 203 | pub const INDONESIA: Country = Country { code: *b"ID", rev: 0 }; | ||
| 204 | /// IR Iran,_Islamic_Republic_Of | ||
| 205 | pub const IRAN_ISLAMIC_REPUBLIC_OF: Country = Country { code: *b"IR", rev: 0 }; | ||
| 206 | /// IQ Iraq | ||
| 207 | pub const IRAQ: Country = Country { code: *b"IQ", rev: 0 }; | ||
| 208 | /// IE Ireland | ||
| 209 | pub const IRELAND: Country = Country { code: *b"IE", rev: 0 }; | ||
| 210 | /// IL Israel | ||
| 211 | pub const ISRAEL: Country = Country { code: *b"IL", rev: 0 }; | ||
| 212 | /// IT Italy | ||
| 213 | pub const ITALY: Country = Country { code: *b"IT", rev: 0 }; | ||
| 214 | /// JM Jamaica | ||
| 215 | pub const JAMAICA: Country = Country { code: *b"JM", rev: 0 }; | ||
| 216 | /// JP Japan | ||
| 217 | pub const JAPAN: Country = Country { code: *b"JP", rev: 0 }; | ||
| 218 | /// JE Jersey | ||
| 219 | pub const JERSEY: Country = Country { code: *b"JE", rev: 0 }; | ||
| 220 | /// JO Jordan | ||
| 221 | pub const JORDAN: Country = Country { code: *b"JO", rev: 0 }; | ||
| 222 | /// KZ Kazakhstan | ||
| 223 | pub const KAZAKHSTAN: Country = Country { code: *b"KZ", rev: 0 }; | ||
| 224 | /// KE Kenya | ||
| 225 | pub const KENYA: Country = Country { code: *b"KE", rev: 0 }; | ||
| 226 | /// KI Kiribati | ||
| 227 | pub const KIRIBATI: Country = Country { code: *b"KI", rev: 0 }; | ||
| 228 | /// KR Korea,_Republic_Of | ||
| 229 | pub const KOREA_REPUBLIC_OF: Country = Country { code: *b"KR", rev: 1 }; | ||
| 230 | /// 0A Kosovo | ||
| 231 | pub const KOSOVO: Country = Country { code: *b"0A", rev: 0 }; | ||
| 232 | /// KW Kuwait | ||
| 233 | pub const KUWAIT: Country = Country { code: *b"KW", rev: 0 }; | ||
| 234 | /// KG Kyrgyzstan | ||
| 235 | pub const KYRGYZSTAN: Country = Country { code: *b"KG", rev: 0 }; | ||
| 236 | /// LA Lao_People's_Democratic_Repubic | ||
| 237 | pub const LAO_PEOPLES_DEMOCRATIC_REPUBIC: Country = Country { code: *b"LA", rev: 0 }; | ||
| 238 | /// LV Latvia | ||
| 239 | pub const LATVIA: Country = Country { code: *b"LV", rev: 0 }; | ||
| 240 | /// LB Lebanon | ||
| 241 | pub const LEBANON: Country = Country { code: *b"LB", rev: 0 }; | ||
| 242 | /// LS Lesotho | ||
| 243 | pub const LESOTHO: Country = Country { code: *b"LS", rev: 0 }; | ||
| 244 | /// LR Liberia | ||
| 245 | pub const LIBERIA: Country = Country { code: *b"LR", rev: 0 }; | ||
| 246 | /// LY Libyan_Arab_Jamahiriya | ||
| 247 | pub const LIBYAN_ARAB_JAMAHIRIYA: Country = Country { code: *b"LY", rev: 0 }; | ||
| 248 | /// LI Liechtenstein | ||
| 249 | pub const LIECHTENSTEIN: Country = Country { code: *b"LI", rev: 0 }; | ||
| 250 | /// LT Lithuania | ||
| 251 | pub const LITHUANIA: Country = Country { code: *b"LT", rev: 0 }; | ||
| 252 | /// LU Luxembourg | ||
| 253 | pub const LUXEMBOURG: Country = Country { code: *b"LU", rev: 0 }; | ||
| 254 | /// MO Macao | ||
| 255 | pub const MACAO: Country = Country { code: *b"MO", rev: 0 }; | ||
| 256 | /// MK Macedonia,_Former_Yugoslav_Republic_Of | ||
| 257 | pub const MACEDONIA_FORMER_YUGOSLAV_REPUBLIC_OF: Country = Country { code: *b"MK", rev: 0 }; | ||
| 258 | /// MG Madagascar | ||
| 259 | pub const MADAGASCAR: Country = Country { code: *b"MG", rev: 0 }; | ||
| 260 | /// MW Malawi | ||
| 261 | pub const MALAWI: Country = Country { code: *b"MW", rev: 0 }; | ||
| 262 | /// MY Malaysia | ||
| 263 | pub const MALAYSIA: Country = Country { code: *b"MY", rev: 0 }; | ||
| 264 | /// MV Maldives | ||
| 265 | pub const MALDIVES: Country = Country { code: *b"MV", rev: 0 }; | ||
| 266 | /// ML Mali | ||
| 267 | pub const MALI: Country = Country { code: *b"ML", rev: 0 }; | ||
| 268 | /// MT Malta | ||
| 269 | pub const MALTA: Country = Country { code: *b"MT", rev: 0 }; | ||
| 270 | /// IM Man,_Isle_Of | ||
| 271 | pub const MAN_ISLE_OF: Country = Country { code: *b"IM", rev: 0 }; | ||
| 272 | /// MQ Martinique | ||
| 273 | pub const MARTINIQUE: Country = Country { code: *b"MQ", rev: 0 }; | ||
| 274 | /// MR Mauritania | ||
| 275 | pub const MAURITANIA: Country = Country { code: *b"MR", rev: 0 }; | ||
| 276 | /// MU Mauritius | ||
| 277 | pub const MAURITIUS: Country = Country { code: *b"MU", rev: 0 }; | ||
| 278 | /// YT Mayotte | ||
| 279 | pub const MAYOTTE: Country = Country { code: *b"YT", rev: 0 }; | ||
| 280 | /// MX Mexico | ||
| 281 | pub const MEXICO: Country = Country { code: *b"MX", rev: 0 }; | ||
| 282 | /// FM Micronesia,_Federated_States_Of | ||
| 283 | pub const MICRONESIA_FEDERATED_STATES_OF: Country = Country { code: *b"FM", rev: 0 }; | ||
| 284 | /// MD Moldova,_Republic_Of | ||
| 285 | pub const MOLDOVA_REPUBLIC_OF: Country = Country { code: *b"MD", rev: 0 }; | ||
| 286 | /// MC Monaco | ||
| 287 | pub const MONACO: Country = Country { code: *b"MC", rev: 0 }; | ||
| 288 | /// MN Mongolia | ||
| 289 | pub const MONGOLIA: Country = Country { code: *b"MN", rev: 0 }; | ||
| 290 | /// ME Montenegro | ||
| 291 | pub const MONTENEGRO: Country = Country { code: *b"ME", rev: 0 }; | ||
| 292 | /// MS Montserrat | ||
| 293 | pub const MONTSERRAT: Country = Country { code: *b"MS", rev: 0 }; | ||
| 294 | /// MA Morocco | ||
| 295 | pub const MOROCCO: Country = Country { code: *b"MA", rev: 0 }; | ||
| 296 | /// MZ Mozambique | ||
| 297 | pub const MOZAMBIQUE: Country = Country { code: *b"MZ", rev: 0 }; | ||
| 298 | /// MM Myanmar | ||
| 299 | pub const MYANMAR: Country = Country { code: *b"MM", rev: 0 }; | ||
| 300 | /// NA Namibia | ||
| 301 | pub const NAMIBIA: Country = Country { code: *b"NA", rev: 0 }; | ||
| 302 | /// NR Nauru | ||
| 303 | pub const NAURU: Country = Country { code: *b"NR", rev: 0 }; | ||
| 304 | /// NP Nepal | ||
| 305 | pub const NEPAL: Country = Country { code: *b"NP", rev: 0 }; | ||
| 306 | /// NL Netherlands | ||
| 307 | pub const NETHERLANDS: Country = Country { code: *b"NL", rev: 0 }; | ||
| 308 | /// AN Netherlands_Antilles | ||
| 309 | pub const NETHERLANDS_ANTILLES: Country = Country { code: *b"AN", rev: 0 }; | ||
| 310 | /// NC New_Caledonia | ||
| 311 | pub const NEW_CALEDONIA: Country = Country { code: *b"NC", rev: 0 }; | ||
| 312 | /// NZ New_Zealand | ||
| 313 | pub const NEW_ZEALAND: Country = Country { code: *b"NZ", rev: 0 }; | ||
| 314 | /// NI Nicaragua | ||
| 315 | pub const NICARAGUA: Country = Country { code: *b"NI", rev: 0 }; | ||
| 316 | /// NE Niger | ||
| 317 | pub const NIGER: Country = Country { code: *b"NE", rev: 0 }; | ||
| 318 | /// NG Nigeria | ||
| 319 | pub const NIGERIA: Country = Country { code: *b"NG", rev: 0 }; | ||
| 320 | /// NF Norfolk_Island | ||
| 321 | pub const NORFOLK_ISLAND: Country = Country { code: *b"NF", rev: 0 }; | ||
| 322 | /// MP Northern_Mariana_Islands | ||
| 323 | pub const NORTHERN_MARIANA_ISLANDS: Country = Country { code: *b"MP", rev: 0 }; | ||
| 324 | /// NO Norway | ||
| 325 | pub const NORWAY: Country = Country { code: *b"NO", rev: 0 }; | ||
| 326 | /// OM Oman | ||
| 327 | pub const OMAN: Country = Country { code: *b"OM", rev: 0 }; | ||
| 328 | /// PK Pakistan | ||
| 329 | pub const PAKISTAN: Country = Country { code: *b"PK", rev: 0 }; | ||
| 330 | /// PW Palau | ||
| 331 | pub const PALAU: Country = Country { code: *b"PW", rev: 0 }; | ||
| 332 | /// PA Panama | ||
| 333 | pub const PANAMA: Country = Country { code: *b"PA", rev: 0 }; | ||
| 334 | /// PG Papua_New_Guinea | ||
| 335 | pub const PAPUA_NEW_GUINEA: Country = Country { code: *b"PG", rev: 0 }; | ||
| 336 | /// PY Paraguay | ||
| 337 | pub const PARAGUAY: Country = Country { code: *b"PY", rev: 0 }; | ||
| 338 | /// PE Peru | ||
| 339 | pub const PERU: Country = Country { code: *b"PE", rev: 0 }; | ||
| 340 | /// PH Philippines | ||
| 341 | pub const PHILIPPINES: Country = Country { code: *b"PH", rev: 0 }; | ||
| 342 | /// PL Poland | ||
| 343 | pub const POLAND: Country = Country { code: *b"PL", rev: 0 }; | ||
| 344 | /// PT Portugal | ||
| 345 | pub const PORTUGAL: Country = Country { code: *b"PT", rev: 0 }; | ||
| 346 | /// PR Pueto_Rico | ||
| 347 | pub const PUETO_RICO: Country = Country { code: *b"PR", rev: 0 }; | ||
| 348 | /// QA Qatar | ||
| 349 | pub const QATAR: Country = Country { code: *b"QA", rev: 0 }; | ||
| 350 | /// RE Reunion | ||
| 351 | pub const REUNION: Country = Country { code: *b"RE", rev: 0 }; | ||
| 352 | /// RO Romania | ||
| 353 | pub const ROMANIA: Country = Country { code: *b"RO", rev: 0 }; | ||
| 354 | /// RU Russian_Federation | ||
| 355 | pub const RUSSIAN_FEDERATION: Country = Country { code: *b"RU", rev: 0 }; | ||
| 356 | /// RW Rwanda | ||
| 357 | pub const RWANDA: Country = Country { code: *b"RW", rev: 0 }; | ||
| 358 | /// KN Saint_Kitts_and_Nevis | ||
| 359 | pub const SAINT_KITTS_AND_NEVIS: Country = Country { code: *b"KN", rev: 0 }; | ||
| 360 | /// LC Saint_Lucia | ||
| 361 | pub const SAINT_LUCIA: Country = Country { code: *b"LC", rev: 0 }; | ||
| 362 | /// PM Saint_Pierre_and_Miquelon | ||
| 363 | pub const SAINT_PIERRE_AND_MIQUELON: Country = Country { code: *b"PM", rev: 0 }; | ||
| 364 | /// VC Saint_Vincent_and_The_Grenadines | ||
| 365 | pub const SAINT_VINCENT_AND_THE_GRENADINES: Country = Country { code: *b"VC", rev: 0 }; | ||
| 366 | /// WS Samoa | ||
| 367 | pub const SAMOA: Country = Country { code: *b"WS", rev: 0 }; | ||
| 368 | /// MF Sanit_Martin_/_Sint_Marteen | ||
| 369 | pub const SANIT_MARTIN_SINT_MARTEEN: Country = Country { code: *b"MF", rev: 0 }; | ||
| 370 | /// ST Sao_Tome_and_Principe | ||
| 371 | pub const SAO_TOME_AND_PRINCIPE: Country = Country { code: *b"ST", rev: 0 }; | ||
| 372 | /// SA Saudi_Arabia | ||
| 373 | pub const SAUDI_ARABIA: Country = Country { code: *b"SA", rev: 0 }; | ||
| 374 | /// SN Senegal | ||
| 375 | pub const SENEGAL: Country = Country { code: *b"SN", rev: 0 }; | ||
| 376 | /// RS Serbia | ||
| 377 | pub const SERBIA: Country = Country { code: *b"RS", rev: 0 }; | ||
| 378 | /// SC Seychelles | ||
| 379 | pub const SEYCHELLES: Country = Country { code: *b"SC", rev: 0 }; | ||
| 380 | /// SL Sierra_Leone | ||
| 381 | pub const SIERRA_LEONE: Country = Country { code: *b"SL", rev: 0 }; | ||
| 382 | /// SG Singapore | ||
| 383 | pub const SINGAPORE: Country = Country { code: *b"SG", rev: 0 }; | ||
| 384 | /// SK Slovakia | ||
| 385 | pub const SLOVAKIA: Country = Country { code: *b"SK", rev: 0 }; | ||
| 386 | /// SI Slovenia | ||
| 387 | pub const SLOVENIA: Country = Country { code: *b"SI", rev: 0 }; | ||
| 388 | /// SB Solomon_Islands | ||
| 389 | pub const SOLOMON_ISLANDS: Country = Country { code: *b"SB", rev: 0 }; | ||
| 390 | /// SO Somalia | ||
| 391 | pub const SOMALIA: Country = Country { code: *b"SO", rev: 0 }; | ||
| 392 | /// ZA South_Africa | ||
| 393 | pub const SOUTH_AFRICA: Country = Country { code: *b"ZA", rev: 0 }; | ||
| 394 | /// ES Spain | ||
| 395 | pub const SPAIN: Country = Country { code: *b"ES", rev: 0 }; | ||
| 396 | /// LK Sri_Lanka | ||
| 397 | pub const SRI_LANKA: Country = Country { code: *b"LK", rev: 0 }; | ||
| 398 | /// SR Suriname | ||
| 399 | pub const SURINAME: Country = Country { code: *b"SR", rev: 0 }; | ||
| 400 | /// SZ Swaziland | ||
| 401 | pub const SWAZILAND: Country = Country { code: *b"SZ", rev: 0 }; | ||
| 402 | /// SE Sweden | ||
| 403 | pub const SWEDEN: Country = Country { code: *b"SE", rev: 0 }; | ||
| 404 | /// CH Switzerland | ||
| 405 | pub const SWITZERLAND: Country = Country { code: *b"CH", rev: 0 }; | ||
| 406 | /// SY Syrian_Arab_Republic | ||
| 407 | pub const SYRIAN_ARAB_REPUBLIC: Country = Country { code: *b"SY", rev: 0 }; | ||
| 408 | /// TW Taiwan,_Province_Of_China | ||
| 409 | pub const TAIWAN_PROVINCE_OF_CHINA: Country = Country { code: *b"TW", rev: 0 }; | ||
| 410 | /// TJ Tajikistan | ||
| 411 | pub const TAJIKISTAN: Country = Country { code: *b"TJ", rev: 0 }; | ||
| 412 | /// TZ Tanzania,_United_Republic_Of | ||
| 413 | pub const TANZANIA_UNITED_REPUBLIC_OF: Country = Country { code: *b"TZ", rev: 0 }; | ||
| 414 | /// TH Thailand | ||
| 415 | pub const THAILAND: Country = Country { code: *b"TH", rev: 0 }; | ||
| 416 | /// TG Togo | ||
| 417 | pub const TOGO: Country = Country { code: *b"TG", rev: 0 }; | ||
| 418 | /// TO Tonga | ||
| 419 | pub const TONGA: Country = Country { code: *b"TO", rev: 0 }; | ||
| 420 | /// TT Trinidad_and_Tobago | ||
| 421 | pub const TRINIDAD_AND_TOBAGO: Country = Country { code: *b"TT", rev: 0 }; | ||
| 422 | /// TN Tunisia | ||
| 423 | pub const TUNISIA: Country = Country { code: *b"TN", rev: 0 }; | ||
| 424 | /// TR Turkey | ||
| 425 | pub const TURKEY: Country = Country { code: *b"TR", rev: 0 }; | ||
| 426 | /// TM Turkmenistan | ||
| 427 | pub const TURKMENISTAN: Country = Country { code: *b"TM", rev: 0 }; | ||
| 428 | /// TC Turks_and_Caicos_Islands | ||
| 429 | pub const TURKS_AND_CAICOS_ISLANDS: Country = Country { code: *b"TC", rev: 0 }; | ||
| 430 | /// TV Tuvalu | ||
| 431 | pub const TUVALU: Country = Country { code: *b"TV", rev: 0 }; | ||
| 432 | /// UG Uganda | ||
| 433 | pub const UGANDA: Country = Country { code: *b"UG", rev: 0 }; | ||
| 434 | /// UA Ukraine | ||
| 435 | pub const UKRAINE: Country = Country { code: *b"UA", rev: 0 }; | ||
| 436 | /// AE United_Arab_Emirates | ||
| 437 | pub const UNITED_ARAB_EMIRATES: Country = Country { code: *b"AE", rev: 0 }; | ||
| 438 | /// GB United_Kingdom | ||
| 439 | pub const UNITED_KINGDOM: Country = Country { code: *b"GB", rev: 0 }; | ||
| 440 | /// US United_States | ||
| 441 | pub const UNITED_STATES: Country = Country { code: *b"US", rev: 0 }; | ||
| 442 | /// US United_States Revision 4 | ||
| 443 | pub const UNITED_STATES_REV4: Country = Country { code: *b"US", rev: 4 }; | ||
| 444 | /// Q1 United_States Revision 931 | ||
| 445 | pub const UNITED_STATES_REV931: Country = Country { code: *b"Q1", rev: 931 }; | ||
| 446 | /// Q2 United_States_(No_DFS) | ||
| 447 | pub const UNITED_STATES_NO_DFS: Country = Country { code: *b"Q2", rev: 0 }; | ||
| 448 | /// UM United_States_Minor_Outlying_Islands | ||
| 449 | pub const UNITED_STATES_MINOR_OUTLYING_ISLANDS: Country = Country { code: *b"UM", rev: 0 }; | ||
| 450 | /// UY Uruguay | ||
| 451 | pub const URUGUAY: Country = Country { code: *b"UY", rev: 0 }; | ||
| 452 | /// UZ Uzbekistan | ||
| 453 | pub const UZBEKISTAN: Country = Country { code: *b"UZ", rev: 0 }; | ||
| 454 | /// VU Vanuatu | ||
| 455 | pub const VANUATU: Country = Country { code: *b"VU", rev: 0 }; | ||
| 456 | /// VE Venezuela | ||
| 457 | pub const VENEZUELA: Country = Country { code: *b"VE", rev: 0 }; | ||
| 458 | /// VN Viet_Nam | ||
| 459 | pub const VIET_NAM: Country = Country { code: *b"VN", rev: 0 }; | ||
| 460 | /// VG Virgin_Islands,_British | ||
| 461 | pub const VIRGIN_ISLANDS_BRITISH: Country = Country { code: *b"VG", rev: 0 }; | ||
| 462 | /// VI Virgin_Islands,_U.S. | ||
| 463 | pub const VIRGIN_ISLANDS_US: Country = Country { code: *b"VI", rev: 0 }; | ||
| 464 | /// WF Wallis_and_Futuna | ||
| 465 | pub const WALLIS_AND_FUTUNA: Country = Country { code: *b"WF", rev: 0 }; | ||
| 466 | /// 0C West_Bank | ||
| 467 | pub const WEST_BANK: Country = Country { code: *b"0C", rev: 0 }; | ||
| 468 | /// EH Western_Sahara | ||
| 469 | pub const WESTERN_SAHARA: Country = Country { code: *b"EH", rev: 0 }; | ||
| 470 | /// Worldwide Locale Revision 983 | ||
| 471 | pub const WORLD_WIDE_XV_REV983: Country = Country { code: *b"XV", rev: 983 }; | ||
| 472 | /// Worldwide Locale (passive Ch12-14) | ||
| 473 | pub const WORLD_WIDE_XX: Country = Country { code: *b"XX", rev: 0 }; | ||
| 474 | /// Worldwide Locale (passive Ch12-14) Revision 17 | ||
| 475 | pub const WORLD_WIDE_XX_REV17: Country = Country { code: *b"XX", rev: 17 }; | ||
| 476 | /// YE Yemen | ||
| 477 | pub const YEMEN: Country = Country { code: *b"YE", rev: 0 }; | ||
| 478 | /// ZM Zambia | ||
| 479 | pub const ZAMBIA: Country = Country { code: *b"ZM", rev: 0 }; | ||
| 480 | /// ZW Zimbabwe | ||
| 481 | pub const ZIMBABWE: Country = Country { code: *b"ZW", rev: 0 }; | ||
diff --git a/cyw43/src/events.rs b/cyw43/src/events.rs new file mode 100644 index 000000000..a94c49a0c --- /dev/null +++ b/cyw43/src/events.rs | |||
| @@ -0,0 +1,400 @@ | |||
| 1 | #![allow(dead_code)] | ||
| 2 | #![allow(non_camel_case_types)] | ||
| 3 | |||
| 4 | use core::cell::RefCell; | ||
| 5 | |||
| 6 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 7 | use embassy_sync::pubsub::{PubSubChannel, Subscriber}; | ||
| 8 | |||
| 9 | use crate::structs::BssInfo; | ||
| 10 | |||
| 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)] | ||
| 12 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 13 | #[repr(u8)] | ||
| 14 | pub enum Event { | ||
| 15 | #[num_enum(default)] | ||
| 16 | Unknown = 0xFF, | ||
| 17 | /// indicates status of set SSID | ||
| 18 | SET_SSID = 0, | ||
| 19 | /// differentiates join IBSS from found (START) IBSS | ||
| 20 | JOIN = 1, | ||
| 21 | /// STA founded an IBSS or AP started a BSS | ||
| 22 | START = 2, | ||
| 23 | /// 802.11 AUTH request | ||
| 24 | AUTH = 3, | ||
| 25 | /// 802.11 AUTH indication | ||
| 26 | AUTH_IND = 4, | ||
| 27 | /// 802.11 DEAUTH request | ||
| 28 | DEAUTH = 5, | ||
| 29 | /// 802.11 DEAUTH indication | ||
| 30 | DEAUTH_IND = 6, | ||
| 31 | /// 802.11 ASSOC request | ||
| 32 | ASSOC = 7, | ||
| 33 | /// 802.11 ASSOC indication | ||
| 34 | ASSOC_IND = 8, | ||
| 35 | /// 802.11 REASSOC request | ||
| 36 | REASSOC = 9, | ||
| 37 | /// 802.11 REASSOC indication | ||
| 38 | REASSOC_IND = 10, | ||
| 39 | /// 802.11 DISASSOC request | ||
| 40 | DISASSOC = 11, | ||
| 41 | /// 802.11 DISASSOC indication | ||
| 42 | DISASSOC_IND = 12, | ||
| 43 | /// 802.11h Quiet period started | ||
| 44 | QUIET_START = 13, | ||
| 45 | /// 802.11h Quiet period ended | ||
| 46 | QUIET_END = 14, | ||
| 47 | /// BEACONS received/lost indication | ||
| 48 | BEACON_RX = 15, | ||
| 49 | /// generic link indication | ||
| 50 | LINK = 16, | ||
| 51 | /// TKIP MIC error occurred | ||
| 52 | MIC_ERROR = 17, | ||
| 53 | /// NDIS style link indication | ||
| 54 | NDIS_LINK = 18, | ||
| 55 | /// roam attempt occurred: indicate status & reason | ||
| 56 | ROAM = 19, | ||
| 57 | /// change in dot11FailedCount (txfail) | ||
| 58 | TXFAIL = 20, | ||
| 59 | /// WPA2 pmkid cache indication | ||
| 60 | PMKID_CACHE = 21, | ||
| 61 | /// current AP's TSF value went backward | ||
| 62 | RETROGRADE_TSF = 22, | ||
| 63 | /// AP was pruned from join list for reason | ||
| 64 | PRUNE = 23, | ||
| 65 | /// report AutoAuth table entry match for join attempt | ||
| 66 | AUTOAUTH = 24, | ||
| 67 | /// Event encapsulating an EAPOL message | ||
| 68 | EAPOL_MSG = 25, | ||
| 69 | /// Scan results are ready or scan was aborted | ||
| 70 | SCAN_COMPLETE = 26, | ||
| 71 | /// indicate to host addts fail/success | ||
| 72 | ADDTS_IND = 27, | ||
| 73 | /// indicate to host delts fail/success | ||
| 74 | DELTS_IND = 28, | ||
| 75 | /// indicate to host of beacon transmit | ||
| 76 | BCNSENT_IND = 29, | ||
| 77 | /// Send the received beacon up to the host | ||
| 78 | BCNRX_MSG = 30, | ||
| 79 | /// indicate to host loss of beacon | ||
| 80 | BCNLOST_MSG = 31, | ||
| 81 | /// before attempting to roam | ||
| 82 | ROAM_PREP = 32, | ||
| 83 | /// PFN network found event | ||
| 84 | PFN_NET_FOUND = 33, | ||
| 85 | /// PFN network lost event | ||
| 86 | PFN_NET_LOST = 34, | ||
| 87 | RESET_COMPLETE = 35, | ||
| 88 | JOIN_START = 36, | ||
| 89 | ROAM_START = 37, | ||
| 90 | ASSOC_START = 38, | ||
| 91 | IBSS_ASSOC = 39, | ||
| 92 | RADIO = 40, | ||
| 93 | /// PSM microcode watchdog fired | ||
| 94 | PSM_WATCHDOG = 41, | ||
| 95 | /// CCX association start | ||
| 96 | CCX_ASSOC_START = 42, | ||
| 97 | /// CCX association abort | ||
| 98 | CCX_ASSOC_ABORT = 43, | ||
| 99 | /// probe request received | ||
| 100 | PROBREQ_MSG = 44, | ||
| 101 | SCAN_CONFIRM_IND = 45, | ||
| 102 | /// WPA Handshake | ||
| 103 | PSK_SUP = 46, | ||
| 104 | COUNTRY_CODE_CHANGED = 47, | ||
| 105 | /// WMMAC excedded medium time | ||
| 106 | EXCEEDED_MEDIUM_TIME = 48, | ||
| 107 | /// WEP ICV error occurred | ||
| 108 | ICV_ERROR = 49, | ||
| 109 | /// Unsupported unicast encrypted frame | ||
| 110 | UNICAST_DECODE_ERROR = 50, | ||
| 111 | /// Unsupported multicast encrypted frame | ||
| 112 | MULTICAST_DECODE_ERROR = 51, | ||
| 113 | TRACE = 52, | ||
| 114 | /// BT-AMP HCI event | ||
| 115 | BTA_HCI_EVENT = 53, | ||
| 116 | /// I/F change (for wlan host notification) | ||
| 117 | IF = 54, | ||
| 118 | /// P2P Discovery listen state expires | ||
| 119 | P2P_DISC_LISTEN_COMPLETE = 55, | ||
| 120 | /// indicate RSSI change based on configured levels | ||
| 121 | RSSI = 56, | ||
| 122 | /// PFN best network batching event | ||
| 123 | PFN_BEST_BATCHING = 57, | ||
| 124 | EXTLOG_MSG = 58, | ||
| 125 | /// Action frame reception | ||
| 126 | ACTION_FRAME = 59, | ||
| 127 | /// Action frame Tx complete | ||
| 128 | ACTION_FRAME_COMPLETE = 60, | ||
| 129 | /// assoc request received | ||
| 130 | PRE_ASSOC_IND = 61, | ||
| 131 | /// re-assoc request received | ||
| 132 | PRE_REASSOC_IND = 62, | ||
| 133 | /// channel adopted (xxx: obsoleted) | ||
| 134 | CHANNEL_ADOPTED = 63, | ||
| 135 | /// AP started | ||
| 136 | AP_STARTED = 64, | ||
| 137 | /// AP stopped due to DFS | ||
| 138 | DFS_AP_STOP = 65, | ||
| 139 | /// AP resumed due to DFS | ||
| 140 | DFS_AP_RESUME = 66, | ||
| 141 | /// WAI stations event | ||
| 142 | WAI_STA_EVENT = 67, | ||
| 143 | /// event encapsulating an WAI message | ||
| 144 | WAI_MSG = 68, | ||
| 145 | /// escan result event | ||
| 146 | ESCAN_RESULT = 69, | ||
| 147 | /// action frame off channel complete | ||
| 148 | ACTION_FRAME_OFF_CHAN_COMPLETE = 70, | ||
| 149 | /// probe response received | ||
| 150 | PROBRESP_MSG = 71, | ||
| 151 | /// P2P Probe request received | ||
| 152 | P2P_PROBREQ_MSG = 72, | ||
| 153 | DCS_REQUEST = 73, | ||
| 154 | /// credits for D11 FIFOs. [AC0,AC1,AC2,AC3,BC_MC,ATIM] | ||
| 155 | FIFO_CREDIT_MAP = 74, | ||
| 156 | /// Received action frame event WITH wl_event_rx_frame_data_t header | ||
| 157 | ACTION_FRAME_RX = 75, | ||
| 158 | /// Wake Event timer fired, used for wake WLAN test mode | ||
| 159 | WAKE_EVENT = 76, | ||
| 160 | /// Radio measurement complete | ||
| 161 | RM_COMPLETE = 77, | ||
| 162 | /// Synchronize TSF with the host | ||
| 163 | HTSFSYNC = 78, | ||
| 164 | /// request an overlay IOCTL/iovar from the host | ||
| 165 | OVERLAY_REQ = 79, | ||
| 166 | CSA_COMPLETE_IND = 80, | ||
| 167 | /// excess PM Wake Event to inform host | ||
| 168 | EXCESS_PM_WAKE_EVENT = 81, | ||
| 169 | /// no PFN networks around | ||
| 170 | PFN_SCAN_NONE = 82, | ||
| 171 | /// last found PFN network gets lost | ||
| 172 | PFN_SCAN_ALLGONE = 83, | ||
| 173 | GTK_PLUMBED = 84, | ||
| 174 | /// 802.11 ASSOC indication for NDIS only | ||
| 175 | ASSOC_IND_NDIS = 85, | ||
| 176 | /// 802.11 REASSOC indication for NDIS only | ||
| 177 | REASSOC_IND_NDIS = 86, | ||
| 178 | ASSOC_REQ_IE = 87, | ||
| 179 | ASSOC_RESP_IE = 88, | ||
| 180 | /// association recreated on resume | ||
| 181 | ASSOC_RECREATED = 89, | ||
| 182 | /// rx action frame event for NDIS only | ||
| 183 | ACTION_FRAME_RX_NDIS = 90, | ||
| 184 | /// authentication request received | ||
| 185 | AUTH_REQ = 91, | ||
| 186 | /// fast assoc recreation failed | ||
| 187 | SPEEDY_RECREATE_FAIL = 93, | ||
| 188 | /// port-specific event and payload (e.g. NDIS) | ||
| 189 | NATIVE = 94, | ||
| 190 | /// event for tx pkt delay suddently jump | ||
| 191 | PKTDELAY_IND = 95, | ||
| 192 | /// AWDL AW period starts | ||
| 193 | AWDL_AW = 96, | ||
| 194 | /// AWDL Master/Slave/NE master role event | ||
| 195 | AWDL_ROLE = 97, | ||
| 196 | /// Generic AWDL event | ||
| 197 | AWDL_EVENT = 98, | ||
| 198 | /// NIC AF txstatus | ||
| 199 | NIC_AF_TXS = 99, | ||
| 200 | /// NAN event | ||
| 201 | NAN = 100, | ||
| 202 | BEACON_FRAME_RX = 101, | ||
| 203 | /// desired service found | ||
| 204 | SERVICE_FOUND = 102, | ||
| 205 | /// GAS fragment received | ||
| 206 | GAS_FRAGMENT_RX = 103, | ||
| 207 | /// GAS sessions all complete | ||
| 208 | GAS_COMPLETE = 104, | ||
| 209 | /// New device found by p2p offload | ||
| 210 | P2PO_ADD_DEVICE = 105, | ||
| 211 | /// device has been removed by p2p offload | ||
| 212 | P2PO_DEL_DEVICE = 106, | ||
| 213 | /// WNM event to notify STA enter sleep mode | ||
| 214 | WNM_STA_SLEEP = 107, | ||
| 215 | /// Indication of MAC tx failures (exhaustion of 802.11 retries) exceeding threshold(s) | ||
| 216 | TXFAIL_THRESH = 108, | ||
| 217 | /// Proximity Detection event | ||
| 218 | PROXD = 109, | ||
| 219 | /// AWDL RX Probe response | ||
| 220 | AWDL_RX_PRB_RESP = 111, | ||
| 221 | /// AWDL RX Action Frames | ||
| 222 | AWDL_RX_ACT_FRAME = 112, | ||
| 223 | /// AWDL Wowl nulls | ||
| 224 | AWDL_WOWL_NULLPKT = 113, | ||
| 225 | /// AWDL Phycal status | ||
| 226 | AWDL_PHYCAL_STATUS = 114, | ||
| 227 | /// AWDL OOB AF status | ||
| 228 | AWDL_OOB_AF_STATUS = 115, | ||
| 229 | /// Interleaved Scan status | ||
| 230 | AWDL_SCAN_STATUS = 116, | ||
| 231 | /// AWDL AW Start | ||
| 232 | AWDL_AW_START = 117, | ||
| 233 | /// AWDL AW End | ||
| 234 | AWDL_AW_END = 118, | ||
| 235 | /// AWDL AW Extensions | ||
| 236 | AWDL_AW_EXT = 119, | ||
| 237 | AWDL_PEER_CACHE_CONTROL = 120, | ||
| 238 | CSA_START_IND = 121, | ||
| 239 | CSA_DONE_IND = 122, | ||
| 240 | CSA_FAILURE_IND = 123, | ||
| 241 | /// CCA based channel quality report | ||
| 242 | CCA_CHAN_QUAL = 124, | ||
| 243 | /// to report change in BSSID while roaming | ||
| 244 | BSSID = 125, | ||
| 245 | /// tx error indication | ||
| 246 | TX_STAT_ERROR = 126, | ||
| 247 | /// credit check for BCMC supported | ||
| 248 | BCMC_CREDIT_SUPPORT = 127, | ||
| 249 | /// psta primary interface indication | ||
| 250 | PSTA_PRIMARY_INTF_IND = 128, | ||
| 251 | /// Handover Request Initiated | ||
| 252 | BT_WIFI_HANDOVER_REQ = 130, | ||
| 253 | /// Southpaw TxInhibit notification | ||
| 254 | SPW_TXINHIBIT = 131, | ||
| 255 | /// FBT Authentication Request Indication | ||
| 256 | FBT_AUTH_REQ_IND = 132, | ||
| 257 | /// Enhancement addition for RSSI | ||
| 258 | RSSI_LQM = 133, | ||
| 259 | /// Full probe/beacon (IEs etc) results | ||
| 260 | PFN_GSCAN_FULL_RESULT = 134, | ||
| 261 | /// Significant change in rssi of bssids being tracked | ||
| 262 | PFN_SWC = 135, | ||
| 263 | /// a STA been authroized for traffic | ||
| 264 | AUTHORIZED = 136, | ||
| 265 | /// probe req with wl_event_rx_frame_data_t header | ||
| 266 | PROBREQ_MSG_RX = 137, | ||
| 267 | /// PFN completed scan of network list | ||
| 268 | PFN_SCAN_COMPLETE = 138, | ||
| 269 | /// RMC Event | ||
| 270 | RMC_EVENT = 139, | ||
| 271 | /// DPSTA interface indication | ||
| 272 | DPSTA_INTF_IND = 140, | ||
| 273 | /// RRM Event | ||
| 274 | RRM = 141, | ||
| 275 | /// ULP entry event | ||
| 276 | ULP = 146, | ||
| 277 | /// TCP Keep Alive Offload Event | ||
| 278 | TKO = 151, | ||
| 279 | /// authentication request received | ||
| 280 | EXT_AUTH_REQ = 187, | ||
| 281 | /// authentication request received | ||
| 282 | EXT_AUTH_FRAME_RX = 188, | ||
| 283 | /// mgmt frame Tx complete | ||
| 284 | MGMT_FRAME_TXSTATUS = 189, | ||
| 285 | /// highest val + 1 for range checking | ||
| 286 | LAST = 190, | ||
| 287 | } | ||
| 288 | |||
| 289 | // TODO this PubSub can probably be replaced with shared memory to make it a bit more efficient. | ||
| 290 | pub type EventQueue = PubSubChannel<NoopRawMutex, Message, 2, 1, 1>; | ||
| 291 | pub type EventSubscriber<'a> = Subscriber<'a, NoopRawMutex, Message, 2, 1, 1>; | ||
| 292 | |||
| 293 | pub struct Events { | ||
| 294 | pub queue: EventQueue, | ||
| 295 | pub mask: SharedEventMask, | ||
| 296 | } | ||
| 297 | |||
| 298 | impl Events { | ||
| 299 | pub fn new() -> Self { | ||
| 300 | Self { | ||
| 301 | queue: EventQueue::new(), | ||
| 302 | mask: SharedEventMask::default(), | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } | ||
| 306 | |||
| 307 | #[derive(Clone, Copy)] | ||
| 308 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 309 | pub struct Status { | ||
| 310 | pub event_type: Event, | ||
| 311 | pub status: u32, | ||
| 312 | } | ||
| 313 | |||
| 314 | #[derive(Clone, Copy)] | ||
| 315 | pub enum Payload { | ||
| 316 | None, | ||
| 317 | BssInfo(BssInfo), | ||
| 318 | } | ||
| 319 | |||
| 320 | #[derive(Clone, Copy)] | ||
| 321 | |||
| 322 | pub struct Message { | ||
| 323 | pub header: Status, | ||
| 324 | pub payload: Payload, | ||
| 325 | } | ||
| 326 | |||
| 327 | impl Message { | ||
| 328 | pub fn new(status: Status, payload: Payload) -> Self { | ||
| 329 | Self { | ||
| 330 | header: status, | ||
| 331 | payload, | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | #[derive(Default)] | ||
| 337 | struct EventMask { | ||
| 338 | mask: [u32; Self::WORD_COUNT], | ||
| 339 | } | ||
| 340 | |||
| 341 | impl EventMask { | ||
| 342 | const WORD_COUNT: usize = ((Event::LAST as u32 + (u32::BITS - 1)) / u32::BITS) as usize; | ||
| 343 | |||
| 344 | fn enable(&mut self, event: Event) { | ||
| 345 | let n = event as u32; | ||
| 346 | let word = n / u32::BITS; | ||
| 347 | let bit = n % u32::BITS; | ||
| 348 | |||
| 349 | self.mask[word as usize] |= 1 << bit; | ||
| 350 | } | ||
| 351 | |||
| 352 | fn disable(&mut self, event: Event) { | ||
| 353 | let n = event as u32; | ||
| 354 | let word = n / u32::BITS; | ||
| 355 | let bit = n % u32::BITS; | ||
| 356 | |||
| 357 | self.mask[word as usize] &= !(1 << bit); | ||
| 358 | } | ||
| 359 | |||
| 360 | fn is_enabled(&self, event: Event) -> bool { | ||
| 361 | let n = event as u32; | ||
| 362 | let word = n / u32::BITS; | ||
| 363 | let bit = n % u32::BITS; | ||
| 364 | |||
| 365 | self.mask[word as usize] & (1 << bit) > 0 | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 369 | #[derive(Default)] | ||
| 370 | |||
| 371 | pub struct SharedEventMask { | ||
| 372 | mask: RefCell<EventMask>, | ||
| 373 | } | ||
| 374 | |||
| 375 | impl SharedEventMask { | ||
| 376 | pub fn enable(&self, events: &[Event]) { | ||
| 377 | let mut mask = self.mask.borrow_mut(); | ||
| 378 | for event in events { | ||
| 379 | mask.enable(*event); | ||
| 380 | } | ||
| 381 | } | ||
| 382 | |||
| 383 | #[allow(dead_code)] | ||
| 384 | pub fn disable(&self, events: &[Event]) { | ||
| 385 | let mut mask = self.mask.borrow_mut(); | ||
| 386 | for event in events { | ||
| 387 | mask.disable(*event); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | pub fn disable_all(&self) { | ||
| 392 | let mut mask = self.mask.borrow_mut(); | ||
| 393 | mask.mask = Default::default(); | ||
| 394 | } | ||
| 395 | |||
| 396 | pub fn is_enabled(&self, event: Event) -> bool { | ||
| 397 | let mask = self.mask.borrow(); | ||
| 398 | mask.is_enabled(event) | ||
| 399 | } | ||
| 400 | } | ||
diff --git a/cyw43/src/fmt.rs b/cyw43/src/fmt.rs new file mode 100644 index 000000000..5730447b3 --- /dev/null +++ b/cyw43/src/fmt.rs | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | #![macro_use] | ||
| 2 | #![allow(unused_macros)] | ||
| 3 | |||
| 4 | use core::fmt::{Debug, Display, LowerHex}; | ||
| 5 | |||
| 6 | #[cfg(all(feature = "defmt", feature = "log"))] | ||
| 7 | compile_error!("You may not enable both `defmt` and `log` features."); | ||
| 8 | |||
| 9 | macro_rules! assert { | ||
| 10 | ($($x:tt)*) => { | ||
| 11 | { | ||
| 12 | #[cfg(not(feature = "defmt"))] | ||
| 13 | ::core::assert!($($x)*); | ||
| 14 | #[cfg(feature = "defmt")] | ||
| 15 | ::defmt::assert!($($x)*); | ||
| 16 | } | ||
| 17 | }; | ||
| 18 | } | ||
| 19 | |||
| 20 | macro_rules! assert_eq { | ||
| 21 | ($($x:tt)*) => { | ||
| 22 | { | ||
| 23 | #[cfg(not(feature = "defmt"))] | ||
| 24 | ::core::assert_eq!($($x)*); | ||
| 25 | #[cfg(feature = "defmt")] | ||
| 26 | ::defmt::assert_eq!($($x)*); | ||
| 27 | } | ||
| 28 | }; | ||
| 29 | } | ||
| 30 | |||
| 31 | macro_rules! assert_ne { | ||
| 32 | ($($x:tt)*) => { | ||
| 33 | { | ||
| 34 | #[cfg(not(feature = "defmt"))] | ||
| 35 | ::core::assert_ne!($($x)*); | ||
| 36 | #[cfg(feature = "defmt")] | ||
| 37 | ::defmt::assert_ne!($($x)*); | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | } | ||
| 41 | |||
| 42 | macro_rules! debug_assert { | ||
| 43 | ($($x:tt)*) => { | ||
| 44 | { | ||
| 45 | #[cfg(not(feature = "defmt"))] | ||
| 46 | ::core::debug_assert!($($x)*); | ||
| 47 | #[cfg(feature = "defmt")] | ||
| 48 | ::defmt::debug_assert!($($x)*); | ||
| 49 | } | ||
| 50 | }; | ||
| 51 | } | ||
| 52 | |||
| 53 | macro_rules! debug_assert_eq { | ||
| 54 | ($($x:tt)*) => { | ||
| 55 | { | ||
| 56 | #[cfg(not(feature = "defmt"))] | ||
| 57 | ::core::debug_assert_eq!($($x)*); | ||
| 58 | #[cfg(feature = "defmt")] | ||
| 59 | ::defmt::debug_assert_eq!($($x)*); | ||
| 60 | } | ||
| 61 | }; | ||
| 62 | } | ||
| 63 | |||
| 64 | macro_rules! debug_assert_ne { | ||
| 65 | ($($x:tt)*) => { | ||
| 66 | { | ||
| 67 | #[cfg(not(feature = "defmt"))] | ||
| 68 | ::core::debug_assert_ne!($($x)*); | ||
| 69 | #[cfg(feature = "defmt")] | ||
| 70 | ::defmt::debug_assert_ne!($($x)*); | ||
| 71 | } | ||
| 72 | }; | ||
| 73 | } | ||
| 74 | |||
| 75 | macro_rules! todo { | ||
| 76 | ($($x:tt)*) => { | ||
| 77 | { | ||
| 78 | #[cfg(not(feature = "defmt"))] | ||
| 79 | ::core::todo!($($x)*); | ||
| 80 | #[cfg(feature = "defmt")] | ||
| 81 | ::defmt::todo!($($x)*); | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | } | ||
| 85 | |||
| 86 | macro_rules! unreachable { | ||
| 87 | ($($x:tt)*) => { | ||
| 88 | { | ||
| 89 | #[cfg(not(feature = "defmt"))] | ||
| 90 | ::core::unreachable!($($x)*); | ||
| 91 | #[cfg(feature = "defmt")] | ||
| 92 | ::defmt::unreachable!($($x)*); | ||
| 93 | } | ||
| 94 | }; | ||
| 95 | } | ||
| 96 | |||
| 97 | macro_rules! panic { | ||
| 98 | ($($x:tt)*) => { | ||
| 99 | { | ||
| 100 | #[cfg(not(feature = "defmt"))] | ||
| 101 | ::core::panic!($($x)*); | ||
| 102 | #[cfg(feature = "defmt")] | ||
| 103 | ::defmt::panic!($($x)*); | ||
| 104 | } | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | macro_rules! trace { | ||
| 109 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 110 | { | ||
| 111 | #[cfg(feature = "log")] | ||
| 112 | ::log::trace!($s $(, $x)*); | ||
| 113 | #[cfg(feature = "defmt")] | ||
| 114 | ::defmt::trace!($s $(, $x)*); | ||
| 115 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 116 | let _ = ($( & $x ),*); | ||
| 117 | } | ||
| 118 | }; | ||
| 119 | } | ||
| 120 | |||
| 121 | macro_rules! debug { | ||
| 122 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 123 | { | ||
| 124 | #[cfg(feature = "log")] | ||
| 125 | ::log::debug!($s $(, $x)*); | ||
| 126 | #[cfg(feature = "defmt")] | ||
| 127 | ::defmt::debug!($s $(, $x)*); | ||
| 128 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 129 | let _ = ($( & $x ),*); | ||
| 130 | } | ||
| 131 | }; | ||
| 132 | } | ||
| 133 | |||
| 134 | macro_rules! info { | ||
| 135 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 136 | { | ||
| 137 | #[cfg(feature = "log")] | ||
| 138 | ::log::info!($s $(, $x)*); | ||
| 139 | #[cfg(feature = "defmt")] | ||
| 140 | ::defmt::info!($s $(, $x)*); | ||
| 141 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 142 | let _ = ($( & $x ),*); | ||
| 143 | } | ||
| 144 | }; | ||
| 145 | } | ||
| 146 | |||
| 147 | macro_rules! warn { | ||
| 148 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 149 | { | ||
| 150 | #[cfg(feature = "log")] | ||
| 151 | ::log::warn!($s $(, $x)*); | ||
| 152 | #[cfg(feature = "defmt")] | ||
| 153 | ::defmt::warn!($s $(, $x)*); | ||
| 154 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 155 | let _ = ($( & $x ),*); | ||
| 156 | } | ||
| 157 | }; | ||
| 158 | } | ||
| 159 | |||
| 160 | macro_rules! error { | ||
| 161 | ($s:literal $(, $x:expr)* $(,)?) => { | ||
| 162 | { | ||
| 163 | #[cfg(feature = "log")] | ||
| 164 | ::log::error!($s $(, $x)*); | ||
| 165 | #[cfg(feature = "defmt")] | ||
| 166 | ::defmt::error!($s $(, $x)*); | ||
| 167 | #[cfg(not(any(feature = "log", feature="defmt")))] | ||
| 168 | let _ = ($( & $x ),*); | ||
| 169 | } | ||
| 170 | }; | ||
| 171 | } | ||
| 172 | |||
| 173 | #[cfg(feature = "defmt")] | ||
| 174 | macro_rules! unwrap { | ||
| 175 | ($($x:tt)*) => { | ||
| 176 | ::defmt::unwrap!($($x)*) | ||
| 177 | }; | ||
| 178 | } | ||
| 179 | |||
| 180 | #[cfg(not(feature = "defmt"))] | ||
| 181 | macro_rules! unwrap { | ||
| 182 | ($arg:expr) => { | ||
| 183 | match $crate::fmt::Try::into_result($arg) { | ||
| 184 | ::core::result::Result::Ok(t) => t, | ||
| 185 | ::core::result::Result::Err(e) => { | ||
| 186 | ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | }; | ||
| 190 | ($arg:expr, $($msg:expr),+ $(,)? ) => { | ||
| 191 | match $crate::fmt::Try::into_result($arg) { | ||
| 192 | ::core::result::Result::Ok(t) => t, | ||
| 193 | ::core::result::Result::Err(e) => { | ||
| 194 | ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | #[cfg(feature = "defmt-timestamp-uptime")] | ||
| 201 | defmt::timestamp! {"{=u64:us}", crate::time::Instant::now().as_micros() } | ||
| 202 | |||
| 203 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] | ||
| 204 | pub struct NoneError; | ||
| 205 | |||
| 206 | pub trait Try { | ||
| 207 | type Ok; | ||
| 208 | type Error; | ||
| 209 | fn into_result(self) -> Result<Self::Ok, Self::Error>; | ||
| 210 | } | ||
| 211 | |||
| 212 | impl<T> Try for Option<T> { | ||
| 213 | type Ok = T; | ||
| 214 | type Error = NoneError; | ||
| 215 | |||
| 216 | #[inline] | ||
| 217 | fn into_result(self) -> Result<T, NoneError> { | ||
| 218 | self.ok_or(NoneError) | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | impl<T, E> Try for Result<T, E> { | ||
| 223 | type Ok = T; | ||
| 224 | type Error = E; | ||
| 225 | |||
| 226 | #[inline] | ||
| 227 | fn into_result(self) -> Self { | ||
| 228 | self | ||
| 229 | } | ||
| 230 | } | ||
| 231 | |||
| 232 | pub struct Bytes<'a>(pub &'a [u8]); | ||
| 233 | |||
| 234 | impl<'a> Debug for Bytes<'a> { | ||
| 235 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 236 | write!(f, "{:#02x?}", self.0) | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | impl<'a> Display for Bytes<'a> { | ||
| 241 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 242 | write!(f, "{:#02x?}", self.0) | ||
| 243 | } | ||
| 244 | } | ||
| 245 | |||
| 246 | impl<'a> LowerHex for Bytes<'a> { | ||
| 247 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 248 | write!(f, "{:#02x?}", self.0) | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | #[cfg(feature = "defmt")] | ||
| 253 | impl<'a> defmt::Format for Bytes<'a> { | ||
| 254 | fn format(&self, fmt: defmt::Formatter) { | ||
| 255 | defmt::write!(fmt, "{:02x}", self.0) | ||
| 256 | } | ||
| 257 | } | ||
diff --git a/cyw43/src/ioctl.rs b/cyw43/src/ioctl.rs new file mode 100644 index 000000000..61524c274 --- /dev/null +++ b/cyw43/src/ioctl.rs | |||
| @@ -0,0 +1,126 @@ | |||
| 1 | use core::cell::{Cell, RefCell}; | ||
| 2 | use core::future::poll_fn; | ||
| 3 | use core::task::{Poll, Waker}; | ||
| 4 | |||
| 5 | use embassy_sync::waitqueue::WakerRegistration; | ||
| 6 | |||
| 7 | use crate::fmt::Bytes; | ||
| 8 | |||
| 9 | #[derive(Clone, Copy)] | ||
| 10 | pub enum IoctlType { | ||
| 11 | Get = 0, | ||
| 12 | Set = 2, | ||
| 13 | } | ||
| 14 | |||
| 15 | #[derive(Clone, Copy)] | ||
| 16 | pub struct PendingIoctl { | ||
| 17 | pub buf: *mut [u8], | ||
| 18 | pub kind: IoctlType, | ||
| 19 | pub cmd: u32, | ||
| 20 | pub iface: u32, | ||
| 21 | } | ||
| 22 | |||
| 23 | #[derive(Clone, Copy)] | ||
| 24 | enum IoctlStateInner { | ||
| 25 | Pending(PendingIoctl), | ||
| 26 | Sent { buf: *mut [u8] }, | ||
| 27 | Done { resp_len: usize }, | ||
| 28 | } | ||
| 29 | |||
| 30 | struct Wakers { | ||
| 31 | control: WakerRegistration, | ||
| 32 | runner: WakerRegistration, | ||
| 33 | } | ||
| 34 | |||
| 35 | impl Default for Wakers { | ||
| 36 | fn default() -> Self { | ||
| 37 | Self { | ||
| 38 | control: WakerRegistration::new(), | ||
| 39 | runner: WakerRegistration::new(), | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub struct IoctlState { | ||
| 45 | state: Cell<IoctlStateInner>, | ||
| 46 | wakers: RefCell<Wakers>, | ||
| 47 | } | ||
| 48 | |||
| 49 | impl IoctlState { | ||
| 50 | pub fn new() -> Self { | ||
| 51 | Self { | ||
| 52 | state: Cell::new(IoctlStateInner::Done { resp_len: 0 }), | ||
| 53 | wakers: Default::default(), | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | fn wake_control(&self) { | ||
| 58 | self.wakers.borrow_mut().control.wake(); | ||
| 59 | } | ||
| 60 | |||
| 61 | fn register_control(&self, waker: &Waker) { | ||
| 62 | self.wakers.borrow_mut().control.register(waker); | ||
| 63 | } | ||
| 64 | |||
| 65 | fn wake_runner(&self) { | ||
| 66 | self.wakers.borrow_mut().runner.wake(); | ||
| 67 | } | ||
| 68 | |||
| 69 | fn register_runner(&self, waker: &Waker) { | ||
| 70 | self.wakers.borrow_mut().runner.register(waker); | ||
| 71 | } | ||
| 72 | |||
| 73 | pub async fn wait_complete(&self) -> usize { | ||
| 74 | poll_fn(|cx| { | ||
| 75 | if let IoctlStateInner::Done { resp_len } = self.state.get() { | ||
| 76 | Poll::Ready(resp_len) | ||
| 77 | } else { | ||
| 78 | self.register_control(cx.waker()); | ||
| 79 | Poll::Pending | ||
| 80 | } | ||
| 81 | }) | ||
| 82 | .await | ||
| 83 | } | ||
| 84 | |||
| 85 | pub async fn wait_pending(&self) -> PendingIoctl { | ||
| 86 | let pending = poll_fn(|cx| { | ||
| 87 | if let IoctlStateInner::Pending(pending) = self.state.get() { | ||
| 88 | Poll::Ready(pending) | ||
| 89 | } else { | ||
| 90 | self.register_runner(cx.waker()); | ||
| 91 | Poll::Pending | ||
| 92 | } | ||
| 93 | }) | ||
| 94 | .await; | ||
| 95 | |||
| 96 | self.state.set(IoctlStateInner::Sent { buf: pending.buf }); | ||
| 97 | pending | ||
| 98 | } | ||
| 99 | |||
| 100 | pub fn cancel_ioctl(&self) { | ||
| 101 | self.state.set(IoctlStateInner::Done { resp_len: 0 }); | ||
| 102 | } | ||
| 103 | |||
| 104 | pub async fn do_ioctl(&self, kind: IoctlType, cmd: u32, iface: u32, buf: &mut [u8]) -> usize { | ||
| 105 | self.state | ||
| 106 | .set(IoctlStateInner::Pending(PendingIoctl { buf, kind, cmd, iface })); | ||
| 107 | self.wake_runner(); | ||
| 108 | self.wait_complete().await | ||
| 109 | } | ||
| 110 | |||
| 111 | pub fn ioctl_done(&self, response: &[u8]) { | ||
| 112 | if let IoctlStateInner::Sent { buf } = self.state.get() { | ||
| 113 | trace!("IOCTL Response: {:02x}", Bytes(response)); | ||
| 114 | |||
| 115 | // TODO fix this | ||
| 116 | (unsafe { &mut *buf }[..response.len()]).copy_from_slice(response); | ||
| 117 | |||
| 118 | self.state.set(IoctlStateInner::Done { | ||
| 119 | resp_len: response.len(), | ||
| 120 | }); | ||
| 121 | self.wake_control(); | ||
| 122 | } else { | ||
| 123 | warn!("IOCTL Response but no pending Ioctl"); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs new file mode 100644 index 000000000..fd11f3674 --- /dev/null +++ b/cyw43/src/lib.rs | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![allow(incomplete_features)] | ||
| 4 | #![feature(async_fn_in_trait, type_alias_impl_trait, concat_bytes)] | ||
| 5 | #![deny(unused_must_use)] | ||
| 6 | |||
| 7 | // This mod MUST go first, so that the others see its macros. | ||
| 8 | pub(crate) mod fmt; | ||
| 9 | |||
| 10 | mod bus; | ||
| 11 | mod consts; | ||
| 12 | mod countries; | ||
| 13 | mod events; | ||
| 14 | mod ioctl; | ||
| 15 | mod structs; | ||
| 16 | |||
| 17 | mod control; | ||
| 18 | mod nvram; | ||
| 19 | mod runner; | ||
| 20 | |||
| 21 | use core::slice; | ||
| 22 | |||
| 23 | use embassy_net_driver_channel as ch; | ||
| 24 | use embedded_hal_1::digital::OutputPin; | ||
| 25 | use events::Events; | ||
| 26 | use ioctl::IoctlState; | ||
| 27 | |||
| 28 | use crate::bus::Bus; | ||
| 29 | pub use crate::bus::SpiBusCyw43; | ||
| 30 | pub use crate::control::{Control, Error as ControlError}; | ||
| 31 | pub use crate::runner::Runner; | ||
| 32 | pub use crate::structs::BssInfo; | ||
| 33 | |||
| 34 | const MTU: usize = 1514; | ||
| 35 | |||
| 36 | #[allow(unused)] | ||
| 37 | #[derive(Clone, Copy, PartialEq, Eq)] | ||
| 38 | enum Core { | ||
| 39 | WLAN = 0, | ||
| 40 | SOCSRAM = 1, | ||
| 41 | SDIOD = 2, | ||
| 42 | } | ||
| 43 | |||
| 44 | impl Core { | ||
| 45 | fn base_addr(&self) -> u32 { | ||
| 46 | match self { | ||
| 47 | Self::WLAN => CHIP.arm_core_base_address, | ||
| 48 | Self::SOCSRAM => CHIP.socsram_wrapper_base_address, | ||
| 49 | Self::SDIOD => CHIP.sdiod_core_base_address, | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | #[allow(unused)] | ||
| 55 | struct Chip { | ||
| 56 | arm_core_base_address: u32, | ||
| 57 | socsram_base_address: u32, | ||
| 58 | socsram_wrapper_base_address: u32, | ||
| 59 | sdiod_core_base_address: u32, | ||
| 60 | pmu_base_address: u32, | ||
| 61 | chip_ram_size: u32, | ||
| 62 | atcm_ram_base_address: u32, | ||
| 63 | socram_srmem_size: u32, | ||
| 64 | chanspec_band_mask: u32, | ||
| 65 | chanspec_band_2g: u32, | ||
| 66 | chanspec_band_5g: u32, | ||
| 67 | chanspec_band_shift: u32, | ||
| 68 | chanspec_bw_10: u32, | ||
| 69 | chanspec_bw_20: u32, | ||
| 70 | chanspec_bw_40: u32, | ||
| 71 | chanspec_bw_mask: u32, | ||
| 72 | chanspec_bw_shift: u32, | ||
| 73 | chanspec_ctl_sb_lower: u32, | ||
| 74 | chanspec_ctl_sb_upper: u32, | ||
| 75 | chanspec_ctl_sb_none: u32, | ||
| 76 | chanspec_ctl_sb_mask: u32, | ||
| 77 | } | ||
| 78 | |||
| 79 | const WRAPPER_REGISTER_OFFSET: u32 = 0x100000; | ||
| 80 | |||
| 81 | // Data for CYW43439 | ||
| 82 | const CHIP: Chip = Chip { | ||
| 83 | arm_core_base_address: 0x18003000 + WRAPPER_REGISTER_OFFSET, | ||
| 84 | socsram_base_address: 0x18004000, | ||
| 85 | socsram_wrapper_base_address: 0x18004000 + WRAPPER_REGISTER_OFFSET, | ||
| 86 | sdiod_core_base_address: 0x18002000, | ||
| 87 | pmu_base_address: 0x18000000, | ||
| 88 | chip_ram_size: 512 * 1024, | ||
| 89 | atcm_ram_base_address: 0, | ||
| 90 | socram_srmem_size: 64 * 1024, | ||
| 91 | chanspec_band_mask: 0xc000, | ||
| 92 | chanspec_band_2g: 0x0000, | ||
| 93 | chanspec_band_5g: 0xc000, | ||
| 94 | chanspec_band_shift: 14, | ||
| 95 | chanspec_bw_10: 0x0800, | ||
| 96 | chanspec_bw_20: 0x1000, | ||
| 97 | chanspec_bw_40: 0x1800, | ||
| 98 | chanspec_bw_mask: 0x3800, | ||
| 99 | chanspec_bw_shift: 11, | ||
| 100 | chanspec_ctl_sb_lower: 0x0000, | ||
| 101 | chanspec_ctl_sb_upper: 0x0100, | ||
| 102 | chanspec_ctl_sb_none: 0x0000, | ||
| 103 | chanspec_ctl_sb_mask: 0x0700, | ||
| 104 | }; | ||
| 105 | |||
| 106 | pub struct State { | ||
| 107 | ioctl_state: IoctlState, | ||
| 108 | ch: ch::State<MTU, 4, 4>, | ||
| 109 | events: Events, | ||
| 110 | } | ||
| 111 | |||
| 112 | impl State { | ||
| 113 | pub fn new() -> Self { | ||
| 114 | Self { | ||
| 115 | ioctl_state: IoctlState::new(), | ||
| 116 | ch: ch::State::new(), | ||
| 117 | events: Events::new(), | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
| 123 | pub enum PowerManagementMode { | ||
| 124 | /// Custom, officially unsupported mode. Use at your own risk. | ||
| 125 | /// All power-saving features set to their max at only a marginal decrease in power consumption | ||
| 126 | /// as oppposed to `Aggressive`. | ||
| 127 | SuperSave, | ||
| 128 | |||
| 129 | /// Aggressive power saving mode. | ||
| 130 | Aggressive, | ||
| 131 | |||
| 132 | /// The default mode. | ||
| 133 | PowerSave, | ||
| 134 | |||
| 135 | /// Performance is prefered over power consumption but still some power is conserved as opposed to | ||
| 136 | /// `None`. | ||
| 137 | Performance, | ||
| 138 | |||
| 139 | /// Unlike all the other PM modes, this lowers the power consumption at all times at the cost of | ||
| 140 | /// a much lower throughput. | ||
| 141 | ThroughputThrottling, | ||
| 142 | |||
| 143 | /// No power management is configured. This consumes the most power. | ||
| 144 | None, | ||
| 145 | } | ||
| 146 | |||
| 147 | impl Default for PowerManagementMode { | ||
| 148 | fn default() -> Self { | ||
| 149 | Self::PowerSave | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | impl PowerManagementMode { | ||
| 154 | fn sleep_ret_ms(&self) -> u16 { | ||
| 155 | match self { | ||
| 156 | PowerManagementMode::SuperSave => 2000, | ||
| 157 | PowerManagementMode::Aggressive => 2000, | ||
| 158 | PowerManagementMode::PowerSave => 200, | ||
| 159 | PowerManagementMode::Performance => 20, | ||
| 160 | PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter | ||
| 161 | PowerManagementMode::None => 0, // value doesn't matter | ||
| 162 | } | ||
| 163 | } | ||
| 164 | |||
| 165 | fn beacon_period(&self) -> u8 { | ||
| 166 | match self { | ||
| 167 | PowerManagementMode::SuperSave => 255, | ||
| 168 | PowerManagementMode::Aggressive => 1, | ||
| 169 | PowerManagementMode::PowerSave => 1, | ||
| 170 | PowerManagementMode::Performance => 1, | ||
| 171 | PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter | ||
| 172 | PowerManagementMode::None => 0, // value doesn't matter | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | fn dtim_period(&self) -> u8 { | ||
| 177 | match self { | ||
| 178 | PowerManagementMode::SuperSave => 255, | ||
| 179 | PowerManagementMode::Aggressive => 1, | ||
| 180 | PowerManagementMode::PowerSave => 1, | ||
| 181 | PowerManagementMode::Performance => 1, | ||
| 182 | PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter | ||
| 183 | PowerManagementMode::None => 0, // value doesn't matter | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | fn assoc(&self) -> u8 { | ||
| 188 | match self { | ||
| 189 | PowerManagementMode::SuperSave => 255, | ||
| 190 | PowerManagementMode::Aggressive => 10, | ||
| 191 | PowerManagementMode::PowerSave => 10, | ||
| 192 | PowerManagementMode::Performance => 1, | ||
| 193 | PowerManagementMode::ThroughputThrottling => 0, // value doesn't matter | ||
| 194 | PowerManagementMode::None => 0, // value doesn't matter | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | fn mode(&self) -> u32 { | ||
| 199 | match self { | ||
| 200 | PowerManagementMode::ThroughputThrottling => 1, | ||
| 201 | PowerManagementMode::None => 0, | ||
| 202 | _ => 2, | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 208 | |||
| 209 | pub async fn new<'a, PWR, SPI>( | ||
| 210 | state: &'a mut State, | ||
| 211 | pwr: PWR, | ||
| 212 | spi: SPI, | ||
| 213 | firmware: &[u8], | ||
| 214 | ) -> (NetDriver<'a>, Control<'a>, Runner<'a, PWR, SPI>) | ||
| 215 | where | ||
| 216 | PWR: OutputPin, | ||
| 217 | SPI: SpiBusCyw43, | ||
| 218 | { | ||
| 219 | let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]); | ||
| 220 | let state_ch = ch_runner.state_runner(); | ||
| 221 | |||
| 222 | let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events); | ||
| 223 | |||
| 224 | runner.init(firmware).await; | ||
| 225 | |||
| 226 | ( | ||
| 227 | device, | ||
| 228 | Control::new(state_ch, &state.events, &state.ioctl_state), | ||
| 229 | runner, | ||
| 230 | ) | ||
| 231 | } | ||
| 232 | |||
| 233 | fn slice8_mut(x: &mut [u32]) -> &mut [u8] { | ||
| 234 | let len = x.len() * 4; | ||
| 235 | unsafe { slice::from_raw_parts_mut(x.as_mut_ptr() as _, len) } | ||
| 236 | } | ||
diff --git a/cyw43/src/nvram.rs b/cyw43/src/nvram.rs new file mode 100644 index 000000000..964a3128d --- /dev/null +++ b/cyw43/src/nvram.rs | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | macro_rules! nvram { | ||
| 2 | ($($s:literal,)*) => { | ||
| 3 | concat_bytes!($($s, b"\x00",)* b"\x00\x00") | ||
| 4 | }; | ||
| 5 | } | ||
| 6 | |||
| 7 | pub static NVRAM: &'static [u8] = &*nvram!( | ||
| 8 | b"NVRAMRev=$Rev$", | ||
| 9 | b"manfid=0x2d0", | ||
| 10 | b"prodid=0x0727", | ||
| 11 | b"vendid=0x14e4", | ||
| 12 | b"devid=0x43e2", | ||
| 13 | b"boardtype=0x0887", | ||
| 14 | b"boardrev=0x1100", | ||
| 15 | b"boardnum=22", | ||
| 16 | b"macaddr=00:A0:50:b5:59:5e", | ||
| 17 | b"sromrev=11", | ||
| 18 | b"boardflags=0x00404001", | ||
| 19 | b"boardflags3=0x04000000", | ||
| 20 | b"xtalfreq=37400", | ||
| 21 | b"nocrc=1", | ||
| 22 | b"ag0=255", | ||
| 23 | b"aa2g=1", | ||
| 24 | b"ccode=ALL", | ||
| 25 | b"pa0itssit=0x20", | ||
| 26 | b"extpagain2g=0", | ||
| 27 | b"pa2ga0=-168,6649,-778", | ||
| 28 | b"AvVmid_c0=0x0,0xc8", | ||
| 29 | b"cckpwroffset0=5", | ||
| 30 | b"maxp2ga0=84", | ||
| 31 | b"txpwrbckof=6", | ||
| 32 | b"cckbw202gpo=0", | ||
| 33 | b"legofdmbw202gpo=0x66111111", | ||
| 34 | b"mcsbw202gpo=0x77711111", | ||
| 35 | b"propbw202gpo=0xdd", | ||
| 36 | b"ofdmdigfilttype=18", | ||
| 37 | b"ofdmdigfilttypebe=18", | ||
| 38 | b"papdmode=1", | ||
| 39 | b"papdvalidtest=1", | ||
| 40 | b"pacalidx2g=45", | ||
| 41 | b"papdepsoffset=-30", | ||
| 42 | b"papdendidx=58", | ||
| 43 | b"ltecxmux=0", | ||
| 44 | b"ltecxpadnum=0x0102", | ||
| 45 | b"ltecxfnsel=0x44", | ||
| 46 | b"ltecxgcigpio=0x01", | ||
| 47 | b"il0macaddr=00:90:4c:c5:12:38", | ||
| 48 | b"wl0id=0x431b", | ||
| 49 | b"deadman_to=0xffffffff", | ||
| 50 | b"muxenab=0x100", | ||
| 51 | b"spurconfig=0x3", | ||
| 52 | b"glitch_based_crsmin=1", | ||
| 53 | b"btc_mode=1", | ||
| 54 | ); | ||
diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs new file mode 100644 index 000000000..5706696b4 --- /dev/null +++ b/cyw43/src/runner.rs | |||
| @@ -0,0 +1,575 @@ | |||
| 1 | use embassy_futures::select::{select3, Either3}; | ||
| 2 | use embassy_net_driver_channel as ch; | ||
| 3 | use embassy_sync::pubsub::PubSubBehavior; | ||
| 4 | use embassy_time::{block_for, Duration, Timer}; | ||
| 5 | use embedded_hal_1::digital::OutputPin; | ||
| 6 | |||
| 7 | use crate::bus::Bus; | ||
| 8 | pub use crate::bus::SpiBusCyw43; | ||
| 9 | use crate::consts::*; | ||
| 10 | use crate::events::{Event, Events, Status}; | ||
| 11 | use crate::fmt::Bytes; | ||
| 12 | use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; | ||
| 13 | use crate::nvram::NVRAM; | ||
| 14 | use crate::structs::*; | ||
| 15 | use crate::{events, slice8_mut, Core, CHIP, MTU}; | ||
| 16 | |||
| 17 | #[cfg(feature = "firmware-logs")] | ||
| 18 | struct LogState { | ||
| 19 | addr: u32, | ||
| 20 | last_idx: usize, | ||
| 21 | buf: [u8; 256], | ||
| 22 | buf_count: usize, | ||
| 23 | } | ||
| 24 | |||
| 25 | #[cfg(feature = "firmware-logs")] | ||
| 26 | impl Default for LogState { | ||
| 27 | fn default() -> Self { | ||
| 28 | Self { | ||
| 29 | addr: Default::default(), | ||
| 30 | last_idx: Default::default(), | ||
| 31 | buf: [0; 256], | ||
| 32 | buf_count: Default::default(), | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | pub struct Runner<'a, PWR, SPI> { | ||
| 38 | ch: ch::Runner<'a, MTU>, | ||
| 39 | bus: Bus<PWR, SPI>, | ||
| 40 | |||
| 41 | ioctl_state: &'a IoctlState, | ||
| 42 | ioctl_id: u16, | ||
| 43 | sdpcm_seq: u8, | ||
| 44 | sdpcm_seq_max: u8, | ||
| 45 | |||
| 46 | events: &'a Events, | ||
| 47 | |||
| 48 | #[cfg(feature = "firmware-logs")] | ||
| 49 | log: LogState, | ||
| 50 | } | ||
| 51 | |||
| 52 | impl<'a, PWR, SPI> Runner<'a, PWR, SPI> | ||
| 53 | where | ||
| 54 | PWR: OutputPin, | ||
| 55 | SPI: SpiBusCyw43, | ||
| 56 | { | ||
| 57 | pub(crate) fn new( | ||
| 58 | ch: ch::Runner<'a, MTU>, | ||
| 59 | bus: Bus<PWR, SPI>, | ||
| 60 | ioctl_state: &'a IoctlState, | ||
| 61 | events: &'a Events, | ||
| 62 | ) -> Self { | ||
| 63 | Self { | ||
| 64 | ch, | ||
| 65 | bus, | ||
| 66 | ioctl_state, | ||
| 67 | ioctl_id: 0, | ||
| 68 | sdpcm_seq: 0, | ||
| 69 | sdpcm_seq_max: 1, | ||
| 70 | events, | ||
| 71 | #[cfg(feature = "firmware-logs")] | ||
| 72 | log: LogState::default(), | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | pub(crate) async fn init(&mut self, firmware: &[u8]) { | ||
| 77 | self.bus.init().await; | ||
| 78 | |||
| 79 | // Init ALP (Active Low Power) clock | ||
| 80 | self.bus | ||
| 81 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, BACKPLANE_ALP_AVAIL_REQ) | ||
| 82 | .await; | ||
| 83 | debug!("waiting for clock..."); | ||
| 84 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & BACKPLANE_ALP_AVAIL == 0 {} | ||
| 85 | debug!("clock ok"); | ||
| 86 | |||
| 87 | let chip_id = self.bus.bp_read16(0x1800_0000).await; | ||
| 88 | debug!("chip ID: {}", chip_id); | ||
| 89 | |||
| 90 | // Upload firmware. | ||
| 91 | self.core_disable(Core::WLAN).await; | ||
| 92 | self.core_reset(Core::SOCSRAM).await; | ||
| 93 | self.bus.bp_write32(CHIP.socsram_base_address + 0x10, 3).await; | ||
| 94 | self.bus.bp_write32(CHIP.socsram_base_address + 0x44, 0).await; | ||
| 95 | |||
| 96 | let ram_addr = CHIP.atcm_ram_base_address; | ||
| 97 | |||
| 98 | debug!("loading fw"); | ||
| 99 | self.bus.bp_write(ram_addr, firmware).await; | ||
| 100 | |||
| 101 | debug!("loading nvram"); | ||
| 102 | // Round up to 4 bytes. | ||
| 103 | let nvram_len = (NVRAM.len() + 3) / 4 * 4; | ||
| 104 | self.bus | ||
| 105 | .bp_write(ram_addr + CHIP.chip_ram_size - 4 - nvram_len as u32, NVRAM) | ||
| 106 | .await; | ||
| 107 | |||
| 108 | let nvram_len_words = nvram_len as u32 / 4; | ||
| 109 | let nvram_len_magic = (!nvram_len_words << 16) | nvram_len_words; | ||
| 110 | self.bus | ||
| 111 | .bp_write32(ram_addr + CHIP.chip_ram_size - 4, nvram_len_magic) | ||
| 112 | .await; | ||
| 113 | |||
| 114 | // Start core! | ||
| 115 | debug!("starting up core..."); | ||
| 116 | self.core_reset(Core::WLAN).await; | ||
| 117 | assert!(self.core_is_up(Core::WLAN).await); | ||
| 118 | |||
| 119 | while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 120 | |||
| 121 | // "Set up the interrupt mask and enable interrupts" | ||
| 122 | // self.bus.bp_write32(CHIP.sdiod_core_base_address + 0x24, 0xF0).await; | ||
| 123 | |||
| 124 | self.bus | ||
| 125 | .write16(FUNC_BUS, REG_BUS_INTERRUPT_ENABLE, IRQ_F2_PACKET_AVAILABLE) | ||
| 126 | .await; | ||
| 127 | |||
| 128 | // "Lower F2 Watermark to avoid DMA Hang in F2 when SD Clock is stopped." | ||
| 129 | // Sounds scary... | ||
| 130 | self.bus | ||
| 131 | .write8(FUNC_BACKPLANE, REG_BACKPLANE_FUNCTION2_WATERMARK, 32) | ||
| 132 | .await; | ||
| 133 | |||
| 134 | // wait for wifi startup | ||
| 135 | debug!("waiting for wifi init..."); | ||
| 136 | while self.bus.read32(FUNC_BUS, REG_BUS_STATUS).await & STATUS_F2_RX_READY == 0 {} | ||
| 137 | |||
| 138 | // Some random configs related to sleep. | ||
| 139 | // These aren't needed if we don't want to sleep the bus. | ||
| 140 | // TODO do we need to sleep the bus to read the irq line, due to | ||
| 141 | // being on the same pin as MOSI/MISO? | ||
| 142 | |||
| 143 | /* | ||
| 144 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL).await; | ||
| 145 | val |= 0x02; // WAKE_TILL_HT_AVAIL | ||
| 146 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_WAKEUP_CTRL, val).await; | ||
| 147 | self.bus.write8(FUNC_BUS, 0xF0, 0x08).await; // SDIOD_CCCR_BRCM_CARDCAP.CMD_NODEC = 1 | ||
| 148 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x02).await; // SBSDIO_FORCE_HT | ||
| 149 | |||
| 150 | let mut val = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR).await; | ||
| 151 | val |= 0x01; // SBSDIO_SLPCSR_KEEP_SDIO_ON | ||
| 152 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_SLEEP_CSR, val).await; | ||
| 153 | */ | ||
| 154 | |||
| 155 | // clear pulls | ||
| 156 | self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP, 0).await; | ||
| 157 | let _ = self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_PULL_UP).await; | ||
| 158 | |||
| 159 | // start HT clock | ||
| 160 | //self.bus.write8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR, 0x10).await; | ||
| 161 | //debug!("waiting for HT clock..."); | ||
| 162 | //while self.bus.read8(FUNC_BACKPLANE, REG_BACKPLANE_CHIP_CLOCK_CSR).await & 0x80 == 0 {} | ||
| 163 | //debug!("clock ok"); | ||
| 164 | |||
| 165 | #[cfg(feature = "firmware-logs")] | ||
| 166 | self.log_init().await; | ||
| 167 | |||
| 168 | debug!("wifi init done"); | ||
| 169 | } | ||
| 170 | |||
| 171 | #[cfg(feature = "firmware-logs")] | ||
| 172 | async fn log_init(&mut self) { | ||
| 173 | // Initialize shared memory for logging. | ||
| 174 | |||
| 175 | let addr = CHIP.atcm_ram_base_address + CHIP.chip_ram_size - 4 - CHIP.socram_srmem_size; | ||
| 176 | let shared_addr = self.bus.bp_read32(addr).await; | ||
| 177 | debug!("shared_addr {:08x}", shared_addr); | ||
| 178 | |||
| 179 | let mut shared = [0; SharedMemData::SIZE]; | ||
| 180 | self.bus.bp_read(shared_addr, &mut shared).await; | ||
| 181 | let shared = SharedMemData::from_bytes(&shared); | ||
| 182 | |||
| 183 | self.log.addr = shared.console_addr + 8; | ||
| 184 | } | ||
| 185 | |||
| 186 | #[cfg(feature = "firmware-logs")] | ||
| 187 | async fn log_read(&mut self) { | ||
| 188 | // Read log struct | ||
| 189 | let mut log = [0; SharedMemLog::SIZE]; | ||
| 190 | self.bus.bp_read(self.log.addr, &mut log).await; | ||
| 191 | let log = SharedMemLog::from_bytes(&log); | ||
| 192 | |||
| 193 | let idx = log.idx as usize; | ||
| 194 | |||
| 195 | // If pointer hasn't moved, no need to do anything. | ||
| 196 | if idx == self.log.last_idx { | ||
| 197 | return; | ||
| 198 | } | ||
| 199 | |||
| 200 | // Read entire buf for now. We could read only what we need, but then we | ||
| 201 | // run into annoying alignment issues in `bp_read`. | ||
| 202 | let mut buf = [0; 0x400]; | ||
| 203 | self.bus.bp_read(log.buf, &mut buf).await; | ||
| 204 | |||
| 205 | while self.log.last_idx != idx as usize { | ||
| 206 | let b = buf[self.log.last_idx]; | ||
| 207 | if b == b'\r' || b == b'\n' { | ||
| 208 | if self.log.buf_count != 0 { | ||
| 209 | let s = unsafe { core::str::from_utf8_unchecked(&self.log.buf[..self.log.buf_count]) }; | ||
| 210 | debug!("LOGS: {}", s); | ||
| 211 | self.log.buf_count = 0; | ||
| 212 | } | ||
| 213 | } else if self.log.buf_count < self.log.buf.len() { | ||
| 214 | self.log.buf[self.log.buf_count] = b; | ||
| 215 | self.log.buf_count += 1; | ||
| 216 | } | ||
| 217 | |||
| 218 | self.log.last_idx += 1; | ||
| 219 | if self.log.last_idx == 0x400 { | ||
| 220 | self.log.last_idx = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 225 | pub async fn run(mut self) -> ! { | ||
| 226 | let mut buf = [0; 512]; | ||
| 227 | loop { | ||
| 228 | #[cfg(feature = "firmware-logs")] | ||
| 229 | self.log_read().await; | ||
| 230 | |||
| 231 | if self.has_credit() { | ||
| 232 | let ioctl = self.ioctl_state.wait_pending(); | ||
| 233 | let tx = self.ch.tx_buf(); | ||
| 234 | let ev = self.bus.wait_for_event(); | ||
| 235 | |||
| 236 | match select3(ioctl, tx, ev).await { | ||
| 237 | Either3::First(PendingIoctl { | ||
| 238 | buf: iobuf, | ||
| 239 | kind, | ||
| 240 | cmd, | ||
| 241 | iface, | ||
| 242 | }) => { | ||
| 243 | self.send_ioctl(kind, cmd, iface, unsafe { &*iobuf }).await; | ||
| 244 | self.check_status(&mut buf).await; | ||
| 245 | } | ||
| 246 | Either3::Second(packet) => { | ||
| 247 | trace!("tx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 248 | |||
| 249 | let mut buf = [0; 512]; | ||
| 250 | let buf8 = slice8_mut(&mut buf); | ||
| 251 | |||
| 252 | // There MUST be 2 bytes of padding between the SDPCM and BDC headers. | ||
| 253 | // And ONLY for data packets! | ||
| 254 | // No idea why, but the firmware will append two zero bytes to the tx'd packets | ||
| 255 | // otherwise. If the packet is exactly 1514 bytes (the max MTU), this makes it | ||
| 256 | // be oversized and get dropped. | ||
| 257 | // WHD adds it here https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/include/whd_sdpcm.h#L90 | ||
| 258 | // and adds it to the header size her https://github.com/Infineon/wifi-host-driver/blob/c04fcbb6b0d049304f376cf483fd7b1b570c8cd5/WiFi_Host_Driver/src/whd_sdpcm.c#L597 | ||
| 259 | // ¯\_(ツ)_/¯ | ||
| 260 | const PADDING_SIZE: usize = 2; | ||
| 261 | let total_len = SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE + packet.len(); | ||
| 262 | |||
| 263 | let seq = self.sdpcm_seq; | ||
| 264 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 265 | |||
| 266 | let sdpcm_header = SdpcmHeader { | ||
| 267 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 268 | len_inv: !total_len as u16, | ||
| 269 | sequence: seq, | ||
| 270 | channel_and_flags: CHANNEL_TYPE_DATA, | ||
| 271 | next_length: 0, | ||
| 272 | header_length: (SdpcmHeader::SIZE + PADDING_SIZE) as _, | ||
| 273 | wireless_flow_control: 0, | ||
| 274 | bus_data_credit: 0, | ||
| 275 | reserved: [0, 0], | ||
| 276 | }; | ||
| 277 | |||
| 278 | let bdc_header = BdcHeader { | ||
| 279 | flags: BDC_VERSION << BDC_VERSION_SHIFT, | ||
| 280 | priority: 0, | ||
| 281 | flags2: 0, | ||
| 282 | data_offset: 0, | ||
| 283 | }; | ||
| 284 | trace!("tx {:?}", sdpcm_header); | ||
| 285 | trace!(" {:?}", bdc_header); | ||
| 286 | |||
| 287 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 288 | buf8[SdpcmHeader::SIZE + PADDING_SIZE..][..BdcHeader::SIZE] | ||
| 289 | .copy_from_slice(&bdc_header.to_bytes()); | ||
| 290 | buf8[SdpcmHeader::SIZE + PADDING_SIZE + BdcHeader::SIZE..][..packet.len()] | ||
| 291 | .copy_from_slice(packet); | ||
| 292 | |||
| 293 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 294 | |||
| 295 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 296 | |||
| 297 | self.bus.wlan_write(&buf[..(total_len / 4)]).await; | ||
| 298 | self.ch.tx_done(); | ||
| 299 | self.check_status(&mut buf).await; | ||
| 300 | } | ||
| 301 | Either3::Third(()) => { | ||
| 302 | self.handle_irq(&mut buf).await; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | } else { | ||
| 306 | warn!("TX stalled"); | ||
| 307 | self.bus.wait_for_event().await; | ||
| 308 | self.handle_irq(&mut buf).await; | ||
| 309 | } | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | /// Wait for IRQ on F2 packet available | ||
| 314 | async fn handle_irq(&mut self, buf: &mut [u32; 512]) { | ||
| 315 | // Receive stuff | ||
| 316 | let irq = self.bus.read16(FUNC_BUS, REG_BUS_INTERRUPT).await; | ||
| 317 | trace!("irq{}", FormatInterrupt(irq)); | ||
| 318 | |||
| 319 | if irq & IRQ_F2_PACKET_AVAILABLE != 0 { | ||
| 320 | self.check_status(buf).await; | ||
| 321 | } | ||
| 322 | |||
| 323 | if irq & IRQ_DATA_UNAVAILABLE != 0 { | ||
| 324 | // TODO what should we do here? | ||
| 325 | warn!("IRQ DATA_UNAVAILABLE, clearing..."); | ||
| 326 | self.bus.write16(FUNC_BUS, REG_BUS_INTERRUPT, 1).await; | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | /// Handle F2 events while status register is set | ||
| 331 | async fn check_status(&mut self, buf: &mut [u32; 512]) { | ||
| 332 | loop { | ||
| 333 | let status = self.bus.status(); | ||
| 334 | trace!("check status{}", FormatStatus(status)); | ||
| 335 | |||
| 336 | if status & STATUS_F2_PKT_AVAILABLE != 0 { | ||
| 337 | let len = (status & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT; | ||
| 338 | self.bus.wlan_read(buf, len).await; | ||
| 339 | trace!("rx {:02x}", Bytes(&slice8_mut(buf)[..(len as usize).min(48)])); | ||
| 340 | self.rx(&mut slice8_mut(buf)[..len as usize]); | ||
| 341 | } else { | ||
| 342 | break; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | } | ||
| 346 | |||
| 347 | fn rx(&mut self, packet: &mut [u8]) { | ||
| 348 | let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return }; | ||
| 349 | |||
| 350 | self.update_credit(&sdpcm_header); | ||
| 351 | |||
| 352 | let channel = sdpcm_header.channel_and_flags & 0x0f; | ||
| 353 | |||
| 354 | match channel { | ||
| 355 | CHANNEL_TYPE_CONTROL => { | ||
| 356 | let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; }; | ||
| 357 | trace!(" {:?}", cdc_header); | ||
| 358 | |||
| 359 | if cdc_header.id == self.ioctl_id { | ||
| 360 | if cdc_header.status != 0 { | ||
| 361 | // TODO: propagate error instead | ||
| 362 | panic!("IOCTL error {}", cdc_header.status as i32); | ||
| 363 | } | ||
| 364 | |||
| 365 | self.ioctl_state.ioctl_done(response); | ||
| 366 | } | ||
| 367 | } | ||
| 368 | CHANNEL_TYPE_EVENT => { | ||
| 369 | let Some((_, bdc_packet)) = BdcHeader::parse(payload) else { | ||
| 370 | warn!("BDC event, incomplete header"); | ||
| 371 | return; | ||
| 372 | }; | ||
| 373 | |||
| 374 | let Some((event_packet, evt_data)) = EventPacket::parse(bdc_packet) else { | ||
| 375 | warn!("BDC event, incomplete data"); | ||
| 376 | return; | ||
| 377 | }; | ||
| 378 | |||
| 379 | const ETH_P_LINK_CTL: u16 = 0x886c; // HPNA, wlan link local tunnel, according to linux if_ether.h | ||
| 380 | if event_packet.eth.ether_type != ETH_P_LINK_CTL { | ||
| 381 | warn!( | ||
| 382 | "unexpected ethernet type 0x{:04x}, expected Broadcom ether type 0x{:04x}", | ||
| 383 | event_packet.eth.ether_type, ETH_P_LINK_CTL | ||
| 384 | ); | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | const BROADCOM_OUI: &[u8] = &[0x00, 0x10, 0x18]; | ||
| 388 | if event_packet.hdr.oui != BROADCOM_OUI { | ||
| 389 | warn!( | ||
| 390 | "unexpected ethernet OUI {:02x}, expected Broadcom OUI {:02x}", | ||
| 391 | Bytes(&event_packet.hdr.oui), | ||
| 392 | Bytes(BROADCOM_OUI) | ||
| 393 | ); | ||
| 394 | return; | ||
| 395 | } | ||
| 396 | const BCMILCP_SUBTYPE_VENDOR_LONG: u16 = 32769; | ||
| 397 | if event_packet.hdr.subtype != BCMILCP_SUBTYPE_VENDOR_LONG { | ||
| 398 | warn!("unexpected subtype {}", event_packet.hdr.subtype); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | |||
| 402 | const BCMILCP_BCM_SUBTYPE_EVENT: u16 = 1; | ||
| 403 | if event_packet.hdr.user_subtype != BCMILCP_BCM_SUBTYPE_EVENT { | ||
| 404 | warn!("unexpected user_subtype {}", event_packet.hdr.subtype); | ||
| 405 | return; | ||
| 406 | } | ||
| 407 | |||
| 408 | let evt_type = events::Event::from(event_packet.msg.event_type as u8); | ||
| 409 | debug!( | ||
| 410 | "=== EVENT {:?}: {:?} {:02x}", | ||
| 411 | evt_type, | ||
| 412 | event_packet.msg, | ||
| 413 | Bytes(evt_data) | ||
| 414 | ); | ||
| 415 | |||
| 416 | if self.events.mask.is_enabled(evt_type) { | ||
| 417 | let status = event_packet.msg.status; | ||
| 418 | let event_payload = match evt_type { | ||
| 419 | Event::ESCAN_RESULT if status == EStatus::PARTIAL => { | ||
| 420 | let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return }; | ||
| 421 | let Some(bss_info) = BssInfo::parse(bss_info) else { return }; | ||
| 422 | events::Payload::BssInfo(*bss_info) | ||
| 423 | } | ||
| 424 | Event::ESCAN_RESULT => events::Payload::None, | ||
| 425 | _ => events::Payload::None, | ||
| 426 | }; | ||
| 427 | |||
| 428 | // this intentionally uses the non-blocking publish immediate | ||
| 429 | // publish() is a deadlock risk in the current design as awaiting here prevents ioctls | ||
| 430 | // The `Runner` always yields when accessing the device, so consumers always have a chance to receive the event | ||
| 431 | // (if they are actively awaiting the queue) | ||
| 432 | self.events.queue.publish_immediate(events::Message::new( | ||
| 433 | Status { | ||
| 434 | event_type: evt_type, | ||
| 435 | status, | ||
| 436 | }, | ||
| 437 | event_payload, | ||
| 438 | )); | ||
| 439 | } | ||
| 440 | } | ||
| 441 | CHANNEL_TYPE_DATA => { | ||
| 442 | let Some((_, packet)) = BdcHeader::parse(payload) else { return }; | ||
| 443 | trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)])); | ||
| 444 | |||
| 445 | match self.ch.try_rx_buf() { | ||
| 446 | Some(buf) => { | ||
| 447 | buf[..packet.len()].copy_from_slice(packet); | ||
| 448 | self.ch.rx_done(packet.len()) | ||
| 449 | } | ||
| 450 | None => warn!("failed to push rxd packet to the channel."), | ||
| 451 | } | ||
| 452 | } | ||
| 453 | _ => {} | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 457 | fn update_credit(&mut self, sdpcm_header: &SdpcmHeader) { | ||
| 458 | if sdpcm_header.channel_and_flags & 0xf < 3 { | ||
| 459 | let mut sdpcm_seq_max = sdpcm_header.bus_data_credit; | ||
| 460 | if sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) > 0x40 { | ||
| 461 | sdpcm_seq_max = self.sdpcm_seq + 2; | ||
| 462 | } | ||
| 463 | self.sdpcm_seq_max = sdpcm_seq_max; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | fn has_credit(&self) -> bool { | ||
| 468 | self.sdpcm_seq != self.sdpcm_seq_max && self.sdpcm_seq_max.wrapping_sub(self.sdpcm_seq) & 0x80 == 0 | ||
| 469 | } | ||
| 470 | |||
| 471 | async fn send_ioctl(&mut self, kind: IoctlType, cmd: u32, iface: u32, data: &[u8]) { | ||
| 472 | let mut buf = [0; 512]; | ||
| 473 | let buf8 = slice8_mut(&mut buf); | ||
| 474 | |||
| 475 | let total_len = SdpcmHeader::SIZE + CdcHeader::SIZE + data.len(); | ||
| 476 | |||
| 477 | let sdpcm_seq = self.sdpcm_seq; | ||
| 478 | self.sdpcm_seq = self.sdpcm_seq.wrapping_add(1); | ||
| 479 | self.ioctl_id = self.ioctl_id.wrapping_add(1); | ||
| 480 | |||
| 481 | let sdpcm_header = SdpcmHeader { | ||
| 482 | len: total_len as u16, // TODO does this len need to be rounded up to u32? | ||
| 483 | len_inv: !total_len as u16, | ||
| 484 | sequence: sdpcm_seq, | ||
| 485 | channel_and_flags: CHANNEL_TYPE_CONTROL, | ||
| 486 | next_length: 0, | ||
| 487 | header_length: SdpcmHeader::SIZE as _, | ||
| 488 | wireless_flow_control: 0, | ||
| 489 | bus_data_credit: 0, | ||
| 490 | reserved: [0, 0], | ||
| 491 | }; | ||
| 492 | |||
| 493 | let cdc_header = CdcHeader { | ||
| 494 | cmd: cmd, | ||
| 495 | len: data.len() as _, | ||
| 496 | flags: kind as u16 | (iface as u16) << 12, | ||
| 497 | id: self.ioctl_id, | ||
| 498 | status: 0, | ||
| 499 | }; | ||
| 500 | trace!("tx {:?}", sdpcm_header); | ||
| 501 | trace!(" {:?}", cdc_header); | ||
| 502 | |||
| 503 | buf8[0..SdpcmHeader::SIZE].copy_from_slice(&sdpcm_header.to_bytes()); | ||
| 504 | buf8[SdpcmHeader::SIZE..][..CdcHeader::SIZE].copy_from_slice(&cdc_header.to_bytes()); | ||
| 505 | buf8[SdpcmHeader::SIZE + CdcHeader::SIZE..][..data.len()].copy_from_slice(data); | ||
| 506 | |||
| 507 | let total_len = (total_len + 3) & !3; // round up to 4byte | ||
| 508 | |||
| 509 | trace!(" {:02x}", Bytes(&buf8[..total_len.min(48)])); | ||
| 510 | |||
| 511 | self.bus.wlan_write(&buf[..total_len / 4]).await; | ||
| 512 | } | ||
| 513 | |||
| 514 | async fn core_disable(&mut self, core: Core) { | ||
| 515 | let base = core.base_addr(); | ||
| 516 | |||
| 517 | // Dummy read? | ||
| 518 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 519 | |||
| 520 | // Check it isn't already reset | ||
| 521 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 522 | if r & AI_RESETCTRL_BIT_RESET != 0 { | ||
| 523 | return; | ||
| 524 | } | ||
| 525 | |||
| 526 | self.bus.bp_write8(base + AI_IOCTRL_OFFSET, 0).await; | ||
| 527 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 528 | |||
| 529 | block_for(Duration::from_millis(1)); | ||
| 530 | |||
| 531 | self.bus | ||
| 532 | .bp_write8(base + AI_RESETCTRL_OFFSET, AI_RESETCTRL_BIT_RESET) | ||
| 533 | .await; | ||
| 534 | let _ = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 535 | } | ||
| 536 | |||
| 537 | async fn core_reset(&mut self, core: Core) { | ||
| 538 | self.core_disable(core).await; | ||
| 539 | |||
| 540 | let base = core.base_addr(); | ||
| 541 | self.bus | ||
| 542 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) | ||
| 543 | .await; | ||
| 544 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 545 | |||
| 546 | self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await; | ||
| 547 | |||
| 548 | Timer::after(Duration::from_millis(1)).await; | ||
| 549 | |||
| 550 | self.bus | ||
| 551 | .bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN) | ||
| 552 | .await; | ||
| 553 | let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 554 | |||
| 555 | Timer::after(Duration::from_millis(1)).await; | ||
| 556 | } | ||
| 557 | |||
| 558 | async fn core_is_up(&mut self, core: Core) -> bool { | ||
| 559 | let base = core.base_addr(); | ||
| 560 | |||
| 561 | let io = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await; | ||
| 562 | if io & (AI_IOCTRL_BIT_FGC | AI_IOCTRL_BIT_CLOCK_EN) != AI_IOCTRL_BIT_CLOCK_EN { | ||
| 563 | debug!("core_is_up: returning false due to bad ioctrl {:02x}", io); | ||
| 564 | return false; | ||
| 565 | } | ||
| 566 | |||
| 567 | let r = self.bus.bp_read8(base + AI_RESETCTRL_OFFSET).await; | ||
| 568 | if r & (AI_RESETCTRL_BIT_RESET) != 0 { | ||
| 569 | debug!("core_is_up: returning false due to bad resetctrl {:02x}", r); | ||
| 570 | return false; | ||
| 571 | } | ||
| 572 | |||
| 573 | true | ||
| 574 | } | ||
| 575 | } | ||
diff --git a/cyw43/src/structs.rs b/cyw43/src/structs.rs new file mode 100644 index 000000000..5ba633c74 --- /dev/null +++ b/cyw43/src/structs.rs | |||
| @@ -0,0 +1,496 @@ | |||
| 1 | use crate::events::Event; | ||
| 2 | use crate::fmt::Bytes; | ||
| 3 | |||
| 4 | macro_rules! impl_bytes { | ||
| 5 | ($t:ident) => { | ||
| 6 | impl $t { | ||
| 7 | pub const SIZE: usize = core::mem::size_of::<Self>(); | ||
| 8 | |||
| 9 | #[allow(unused)] | ||
| 10 | pub fn to_bytes(&self) -> [u8; Self::SIZE] { | ||
| 11 | unsafe { core::mem::transmute(*self) } | ||
| 12 | } | ||
| 13 | |||
| 14 | #[allow(unused)] | ||
| 15 | pub fn from_bytes(bytes: &[u8; Self::SIZE]) -> &Self { | ||
| 16 | let alignment = core::mem::align_of::<Self>(); | ||
| 17 | assert_eq!( | ||
| 18 | bytes.as_ptr().align_offset(alignment), | ||
| 19 | 0, | ||
| 20 | "{} is not aligned", | ||
| 21 | core::any::type_name::<Self>() | ||
| 22 | ); | ||
| 23 | unsafe { core::mem::transmute(bytes) } | ||
| 24 | } | ||
| 25 | |||
| 26 | #[allow(unused)] | ||
| 27 | pub fn from_bytes_mut(bytes: &mut [u8; Self::SIZE]) -> &mut Self { | ||
| 28 | let alignment = core::mem::align_of::<Self>(); | ||
| 29 | assert_eq!( | ||
| 30 | bytes.as_ptr().align_offset(alignment), | ||
| 31 | 0, | ||
| 32 | "{} is not aligned", | ||
| 33 | core::any::type_name::<Self>() | ||
| 34 | ); | ||
| 35 | |||
| 36 | unsafe { core::mem::transmute(bytes) } | ||
| 37 | } | ||
| 38 | } | ||
| 39 | }; | ||
| 40 | } | ||
| 41 | |||
| 42 | #[derive(Clone, Copy)] | ||
| 43 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 44 | #[repr(C)] | ||
| 45 | pub struct SharedMemData { | ||
| 46 | pub flags: u32, | ||
| 47 | pub trap_addr: u32, | ||
| 48 | pub assert_exp_addr: u32, | ||
| 49 | pub assert_file_addr: u32, | ||
| 50 | pub assert_line: u32, | ||
| 51 | pub console_addr: u32, | ||
| 52 | pub msgtrace_addr: u32, | ||
| 53 | pub fwid: u32, | ||
| 54 | } | ||
| 55 | impl_bytes!(SharedMemData); | ||
| 56 | |||
| 57 | #[derive(Clone, Copy)] | ||
| 58 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 59 | #[repr(C)] | ||
| 60 | pub struct SharedMemLog { | ||
| 61 | pub buf: u32, | ||
| 62 | pub buf_size: u32, | ||
| 63 | pub idx: u32, | ||
| 64 | pub out_idx: u32, | ||
| 65 | } | ||
| 66 | impl_bytes!(SharedMemLog); | ||
| 67 | |||
| 68 | #[derive(Debug, Clone, Copy)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | #[repr(C)] | ||
| 71 | pub struct SdpcmHeader { | ||
| 72 | pub len: u16, | ||
| 73 | pub len_inv: u16, | ||
| 74 | /// Rx/Tx sequence number | ||
| 75 | pub sequence: u8, | ||
| 76 | /// 4 MSB Channel number, 4 LSB arbitrary flag | ||
| 77 | pub channel_and_flags: u8, | ||
| 78 | /// Length of next data frame, reserved for Tx | ||
| 79 | pub next_length: u8, | ||
| 80 | /// Data offset | ||
| 81 | pub header_length: u8, | ||
| 82 | /// Flow control bits, reserved for Tx | ||
| 83 | pub wireless_flow_control: u8, | ||
| 84 | /// Maximum Sequence number allowed by firmware for Tx | ||
| 85 | pub bus_data_credit: u8, | ||
| 86 | /// Reserved | ||
| 87 | pub reserved: [u8; 2], | ||
| 88 | } | ||
| 89 | impl_bytes!(SdpcmHeader); | ||
| 90 | |||
| 91 | impl SdpcmHeader { | ||
| 92 | pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { | ||
| 93 | let packet_len = packet.len(); | ||
| 94 | if packet_len < Self::SIZE { | ||
| 95 | warn!("packet too short, len={}", packet.len()); | ||
| 96 | return None; | ||
| 97 | } | ||
| 98 | let (sdpcm_header, sdpcm_packet) = packet.split_at_mut(Self::SIZE); | ||
| 99 | let sdpcm_header = Self::from_bytes_mut(sdpcm_header.try_into().unwrap()); | ||
| 100 | trace!("rx {:?}", sdpcm_header); | ||
| 101 | |||
| 102 | if sdpcm_header.len != !sdpcm_header.len_inv { | ||
| 103 | warn!("len inv mismatch"); | ||
| 104 | return None; | ||
| 105 | } | ||
| 106 | |||
| 107 | if sdpcm_header.len as usize != packet_len { | ||
| 108 | warn!("len from header doesn't match len from spi"); | ||
| 109 | return None; | ||
| 110 | } | ||
| 111 | |||
| 112 | let sdpcm_packet = &mut sdpcm_packet[(sdpcm_header.header_length as usize - Self::SIZE)..]; | ||
| 113 | Some((sdpcm_header, sdpcm_packet)) | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | #[derive(Debug, Clone, Copy)] | ||
| 118 | #[repr(C, packed(2))] | ||
| 119 | pub struct CdcHeader { | ||
| 120 | pub cmd: u32, | ||
| 121 | pub len: u32, | ||
| 122 | pub flags: u16, | ||
| 123 | pub id: u16, | ||
| 124 | pub status: u32, | ||
| 125 | } | ||
| 126 | impl_bytes!(CdcHeader); | ||
| 127 | |||
| 128 | #[cfg(feature = "defmt")] | ||
| 129 | impl defmt::Format for CdcHeader { | ||
| 130 | fn format(&self, fmt: defmt::Formatter) { | ||
| 131 | fn copy<T: Copy>(t: T) -> T { | ||
| 132 | t | ||
| 133 | } | ||
| 134 | |||
| 135 | defmt::write!( | ||
| 136 | fmt, | ||
| 137 | "CdcHeader{{cmd: {=u32:08x}, len: {=u32:08x}, flags: {=u16:04x}, id: {=u16:04x}, status: {=u32:08x}}}", | ||
| 138 | copy(self.cmd), | ||
| 139 | copy(self.len), | ||
| 140 | copy(self.flags), | ||
| 141 | copy(self.id), | ||
| 142 | copy(self.status), | ||
| 143 | ) | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | impl CdcHeader { | ||
| 148 | pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { | ||
| 149 | if packet.len() < Self::SIZE { | ||
| 150 | warn!("payload too short, len={}", packet.len()); | ||
| 151 | return None; | ||
| 152 | } | ||
| 153 | |||
| 154 | let (cdc_header, payload) = packet.split_at_mut(Self::SIZE); | ||
| 155 | let cdc_header = Self::from_bytes_mut(cdc_header.try_into().unwrap()); | ||
| 156 | |||
| 157 | let payload = &mut payload[..cdc_header.len as usize]; | ||
| 158 | Some((cdc_header, payload)) | ||
| 159 | } | ||
| 160 | } | ||
| 161 | |||
| 162 | pub const BDC_VERSION: u8 = 2; | ||
| 163 | pub const BDC_VERSION_SHIFT: u8 = 4; | ||
| 164 | |||
| 165 | #[derive(Debug, Clone, Copy)] | ||
| 166 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 167 | #[repr(C)] | ||
| 168 | pub struct BdcHeader { | ||
| 169 | pub flags: u8, | ||
| 170 | /// 802.1d Priority (low 3 bits) | ||
| 171 | pub priority: u8, | ||
| 172 | pub flags2: u8, | ||
| 173 | /// Offset from end of BDC header to packet data, in 4-uint8_t words. Leaves room for optional headers. | ||
| 174 | pub data_offset: u8, | ||
| 175 | } | ||
| 176 | impl_bytes!(BdcHeader); | ||
| 177 | |||
| 178 | impl BdcHeader { | ||
| 179 | pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { | ||
| 180 | if packet.len() < Self::SIZE { | ||
| 181 | return None; | ||
| 182 | } | ||
| 183 | |||
| 184 | let (bdc_header, bdc_packet) = packet.split_at_mut(Self::SIZE); | ||
| 185 | let bdc_header = Self::from_bytes_mut(bdc_header.try_into().unwrap()); | ||
| 186 | trace!(" {:?}", bdc_header); | ||
| 187 | |||
| 188 | let packet_start = 4 * bdc_header.data_offset as usize; | ||
| 189 | |||
| 190 | let bdc_packet = bdc_packet.get_mut(packet_start..)?; | ||
| 191 | trace!(" {:02x}", Bytes(&bdc_packet[..bdc_packet.len().min(36)])); | ||
| 192 | |||
| 193 | Some((bdc_header, bdc_packet)) | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | #[derive(Clone, Copy)] | ||
| 198 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 199 | #[repr(C)] | ||
| 200 | pub struct EthernetHeader { | ||
| 201 | pub destination_mac: [u8; 6], | ||
| 202 | pub source_mac: [u8; 6], | ||
| 203 | pub ether_type: u16, | ||
| 204 | } | ||
| 205 | |||
| 206 | impl EthernetHeader { | ||
| 207 | pub fn byteswap(&mut self) { | ||
| 208 | self.ether_type = self.ether_type.to_be(); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | #[derive(Clone, Copy)] | ||
| 213 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 214 | #[repr(C)] | ||
| 215 | pub struct EventHeader { | ||
| 216 | pub subtype: u16, | ||
| 217 | pub length: u16, | ||
| 218 | pub version: u8, | ||
| 219 | pub oui: [u8; 3], | ||
| 220 | pub user_subtype: u16, | ||
| 221 | } | ||
| 222 | |||
| 223 | impl EventHeader { | ||
| 224 | pub fn byteswap(&mut self) { | ||
| 225 | self.subtype = self.subtype.to_be(); | ||
| 226 | self.length = self.length.to_be(); | ||
| 227 | self.user_subtype = self.user_subtype.to_be(); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | #[derive(Debug, Clone, Copy)] | ||
| 232 | // #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 233 | #[repr(C, packed(2))] | ||
| 234 | pub struct EventMessage { | ||
| 235 | /// version | ||
| 236 | pub version: u16, | ||
| 237 | /// see flags below | ||
| 238 | pub flags: u16, | ||
| 239 | /// Message (see below) | ||
| 240 | pub event_type: u32, | ||
| 241 | /// Status code (see below) | ||
| 242 | pub status: u32, | ||
| 243 | /// Reason code (if applicable) | ||
| 244 | pub reason: u32, | ||
| 245 | /// WLC_E_AUTH | ||
| 246 | pub auth_type: u32, | ||
| 247 | /// data buf | ||
| 248 | pub datalen: u32, | ||
| 249 | /// Station address (if applicable) | ||
| 250 | pub addr: [u8; 6], | ||
| 251 | /// name of the incoming packet interface | ||
| 252 | pub ifname: [u8; 16], | ||
| 253 | /// destination OS i/f index | ||
| 254 | pub ifidx: u8, | ||
| 255 | /// source bsscfg index | ||
| 256 | pub bsscfgidx: u8, | ||
| 257 | } | ||
| 258 | impl_bytes!(EventMessage); | ||
| 259 | |||
| 260 | #[cfg(feature = "defmt")] | ||
| 261 | impl defmt::Format for EventMessage { | ||
| 262 | fn format(&self, fmt: defmt::Formatter) { | ||
| 263 | let event_type = self.event_type; | ||
| 264 | let status = self.status; | ||
| 265 | let reason = self.reason; | ||
| 266 | let auth_type = self.auth_type; | ||
| 267 | let datalen = self.datalen; | ||
| 268 | |||
| 269 | defmt::write!( | ||
| 270 | fmt, | ||
| 271 | "EventMessage {{ \ | ||
| 272 | version: {=u16}, \ | ||
| 273 | flags: {=u16}, \ | ||
| 274 | event_type: {=u32}, \ | ||
| 275 | status: {=u32}, \ | ||
| 276 | reason: {=u32}, \ | ||
| 277 | auth_type: {=u32}, \ | ||
| 278 | datalen: {=u32}, \ | ||
| 279 | addr: {=[u8; 6]:x}, \ | ||
| 280 | ifname: {=[u8; 16]:x}, \ | ||
| 281 | ifidx: {=u8}, \ | ||
| 282 | bsscfgidx: {=u8}, \ | ||
| 283 | }} ", | ||
| 284 | self.version, | ||
| 285 | self.flags, | ||
| 286 | event_type, | ||
| 287 | status, | ||
| 288 | reason, | ||
| 289 | auth_type, | ||
| 290 | datalen, | ||
| 291 | self.addr, | ||
| 292 | self.ifname, | ||
| 293 | self.ifidx, | ||
| 294 | self.bsscfgidx | ||
| 295 | ); | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | impl EventMessage { | ||
| 300 | pub fn byteswap(&mut self) { | ||
| 301 | self.version = self.version.to_be(); | ||
| 302 | self.flags = self.flags.to_be(); | ||
| 303 | self.event_type = self.event_type.to_be(); | ||
| 304 | self.status = self.status.to_be(); | ||
| 305 | self.reason = self.reason.to_be(); | ||
| 306 | self.auth_type = self.auth_type.to_be(); | ||
| 307 | self.datalen = self.datalen.to_be(); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | #[derive(Clone, Copy)] | ||
| 312 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 313 | #[repr(C, packed(2))] | ||
| 314 | pub struct EventPacket { | ||
| 315 | pub eth: EthernetHeader, | ||
| 316 | pub hdr: EventHeader, | ||
| 317 | pub msg: EventMessage, | ||
| 318 | } | ||
| 319 | impl_bytes!(EventPacket); | ||
| 320 | |||
| 321 | impl EventPacket { | ||
| 322 | pub fn parse(packet: &mut [u8]) -> Option<(&mut Self, &mut [u8])> { | ||
| 323 | if packet.len() < Self::SIZE { | ||
| 324 | return None; | ||
| 325 | } | ||
| 326 | |||
| 327 | let (event_header, event_packet) = packet.split_at_mut(Self::SIZE); | ||
| 328 | let event_header = Self::from_bytes_mut(event_header.try_into().unwrap()); | ||
| 329 | // warn!("event_header {:x}", event_header as *const _); | ||
| 330 | event_header.byteswap(); | ||
| 331 | |||
| 332 | let event_packet = event_packet.get_mut(..event_header.msg.datalen as usize)?; | ||
| 333 | |||
| 334 | Some((event_header, event_packet)) | ||
| 335 | } | ||
| 336 | |||
| 337 | pub fn byteswap(&mut self) { | ||
| 338 | self.eth.byteswap(); | ||
| 339 | self.hdr.byteswap(); | ||
| 340 | self.msg.byteswap(); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | #[derive(Clone, Copy)] | ||
| 345 | #[repr(C)] | ||
| 346 | pub struct DownloadHeader { | ||
| 347 | pub flag: u16, // | ||
| 348 | pub dload_type: u16, | ||
| 349 | pub len: u32, | ||
| 350 | pub crc: u32, | ||
| 351 | } | ||
| 352 | impl_bytes!(DownloadHeader); | ||
| 353 | |||
| 354 | #[allow(unused)] | ||
| 355 | pub const DOWNLOAD_FLAG_NO_CRC: u16 = 0x0001; | ||
| 356 | pub const DOWNLOAD_FLAG_BEGIN: u16 = 0x0002; | ||
| 357 | pub const DOWNLOAD_FLAG_END: u16 = 0x0004; | ||
| 358 | pub const DOWNLOAD_FLAG_HANDLER_VER: u16 = 0x1000; | ||
| 359 | |||
| 360 | // Country Locale Matrix (CLM) | ||
| 361 | pub const DOWNLOAD_TYPE_CLM: u16 = 2; | ||
| 362 | |||
| 363 | #[derive(Clone, Copy)] | ||
| 364 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 365 | #[repr(C)] | ||
| 366 | pub struct CountryInfo { | ||
| 367 | pub country_abbrev: [u8; 4], | ||
| 368 | pub rev: i32, | ||
| 369 | pub country_code: [u8; 4], | ||
| 370 | } | ||
| 371 | impl_bytes!(CountryInfo); | ||
| 372 | |||
| 373 | #[derive(Clone, Copy)] | ||
| 374 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 375 | #[repr(C)] | ||
| 376 | pub struct SsidInfo { | ||
| 377 | pub len: u32, | ||
| 378 | pub ssid: [u8; 32], | ||
| 379 | } | ||
| 380 | impl_bytes!(SsidInfo); | ||
| 381 | |||
| 382 | #[derive(Clone, Copy)] | ||
| 383 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 384 | #[repr(C)] | ||
| 385 | pub struct PassphraseInfo { | ||
| 386 | pub len: u16, | ||
| 387 | pub flags: u16, | ||
| 388 | pub passphrase: [u8; 64], | ||
| 389 | } | ||
| 390 | impl_bytes!(PassphraseInfo); | ||
| 391 | |||
| 392 | #[derive(Clone, Copy)] | ||
| 393 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 394 | #[repr(C)] | ||
| 395 | pub struct SsidInfoWithIndex { | ||
| 396 | pub index: u32, | ||
| 397 | pub ssid_info: SsidInfo, | ||
| 398 | } | ||
| 399 | impl_bytes!(SsidInfoWithIndex); | ||
| 400 | |||
| 401 | #[derive(Clone, Copy)] | ||
| 402 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 403 | #[repr(C)] | ||
| 404 | pub struct EventMask { | ||
| 405 | pub iface: u32, | ||
| 406 | pub events: [u8; 24], | ||
| 407 | } | ||
| 408 | impl_bytes!(EventMask); | ||
| 409 | |||
| 410 | impl EventMask { | ||
| 411 | pub fn unset(&mut self, evt: Event) { | ||
| 412 | let evt = evt as u8 as usize; | ||
| 413 | self.events[evt / 8] &= !(1 << (evt % 8)); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | /// Parameters for a wifi scan | ||
| 418 | #[derive(Clone, Copy)] | ||
| 419 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 420 | #[repr(C)] | ||
| 421 | pub struct ScanParams { | ||
| 422 | pub version: u32, | ||
| 423 | pub action: u16, | ||
| 424 | pub sync_id: u16, | ||
| 425 | pub ssid_len: u32, | ||
| 426 | pub ssid: [u8; 32], | ||
| 427 | pub bssid: [u8; 6], | ||
| 428 | pub bss_type: u8, | ||
| 429 | pub scan_type: u8, | ||
| 430 | pub nprobes: u32, | ||
| 431 | pub active_time: u32, | ||
| 432 | pub passive_time: u32, | ||
| 433 | pub home_time: u32, | ||
| 434 | pub channel_num: u32, | ||
| 435 | pub channel_list: [u16; 1], | ||
| 436 | } | ||
| 437 | impl_bytes!(ScanParams); | ||
| 438 | |||
| 439 | /// Wifi Scan Results Header, followed by `bss_count` `BssInfo` | ||
| 440 | #[derive(Clone, Copy)] | ||
| 441 | // #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 442 | #[repr(C, packed(2))] | ||
| 443 | pub struct ScanResults { | ||
| 444 | pub buflen: u32, | ||
| 445 | pub version: u32, | ||
| 446 | pub sync_id: u16, | ||
| 447 | pub bss_count: u16, | ||
| 448 | } | ||
| 449 | impl_bytes!(ScanResults); | ||
| 450 | |||
| 451 | impl ScanResults { | ||
| 452 | pub fn parse(packet: &mut [u8]) -> Option<(&mut ScanResults, &mut [u8])> { | ||
| 453 | if packet.len() < ScanResults::SIZE { | ||
| 454 | return None; | ||
| 455 | } | ||
| 456 | |||
| 457 | let (scan_results, bssinfo) = packet.split_at_mut(ScanResults::SIZE); | ||
| 458 | let scan_results = ScanResults::from_bytes_mut(scan_results.try_into().unwrap()); | ||
| 459 | |||
| 460 | if scan_results.bss_count > 0 && bssinfo.len() < BssInfo::SIZE { | ||
| 461 | warn!("Scan result, incomplete BssInfo"); | ||
| 462 | return None; | ||
| 463 | } | ||
| 464 | |||
| 465 | Some((scan_results, bssinfo)) | ||
| 466 | } | ||
| 467 | } | ||
| 468 | |||
| 469 | /// Wifi Scan Result | ||
| 470 | #[derive(Clone, Copy)] | ||
| 471 | // #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 472 | #[repr(C, packed(2))] | ||
| 473 | #[non_exhaustive] | ||
| 474 | pub struct BssInfo { | ||
| 475 | pub version: u32, | ||
| 476 | pub length: u32, | ||
| 477 | pub bssid: [u8; 6], | ||
| 478 | pub beacon_period: u16, | ||
| 479 | pub capability: u16, | ||
| 480 | pub ssid_len: u8, | ||
| 481 | pub ssid: [u8; 32], | ||
| 482 | // there will be more stuff here | ||
| 483 | } | ||
| 484 | impl_bytes!(BssInfo); | ||
| 485 | |||
| 486 | impl BssInfo { | ||
| 487 | pub fn parse(packet: &mut [u8]) -> Option<&mut Self> { | ||
| 488 | if packet.len() < BssInfo::SIZE { | ||
| 489 | return None; | ||
| 490 | } | ||
| 491 | |||
| 492 | Some(BssInfo::from_bytes_mut( | ||
| 493 | packet[..BssInfo::SIZE].as_mut().try_into().unwrap(), | ||
| 494 | )) | ||
| 495 | } | ||
| 496 | } | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index ffeb69f15..f77377a6f 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -19,6 +19,8 @@ embassy-lora = { version = "0.1.0", path = "../../embassy-lora", features = ["ti | |||
| 19 | lora-phy = { version = "1" } | 19 | lora-phy = { version = "1" } |
| 20 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } | 20 | lorawan-device = { version = "0.10.0", default-features = false, features = ["async", "external-lora-phy"] } |
| 21 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } | 21 | lorawan = { version = "0.7.3", default-features = false, features = ["default-crypto"] } |
| 22 | cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } | ||
| 23 | cyw43-pio = { path = "../../cyw43-pio", features = ["defmt", "overclock"] } | ||
| 22 | 24 | ||
| 23 | defmt = "0.3" | 25 | defmt = "0.3" |
| 24 | defmt-rtt = "0.4" | 26 | defmt-rtt = "0.4" |
| @@ -36,6 +38,7 @@ st7789 = "0.6.1" | |||
| 36 | display-interface = "0.4.1" | 38 | display-interface = "0.4.1" |
| 37 | byte-slice-cast = { version = "1.2.0", default-features = false } | 39 | byte-slice-cast = { version = "1.2.0", default-features = false } |
| 38 | smart-leds = "0.3.0" | 40 | smart-leds = "0.3.0" |
| 41 | heapless = "0.7.15" | ||
| 39 | 42 | ||
| 40 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } | 43 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" } |
| 41 | embedded-hal-async = "0.2.0-alpha.1" | 44 | embedded-hal-async = "0.2.0-alpha.1" |
diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs new file mode 100644 index 000000000..15264524e --- /dev/null +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | #![feature(async_fn_in_trait)] | ||
| 5 | #![allow(incomplete_features)] | ||
| 6 | |||
| 7 | use core::str::from_utf8; | ||
| 8 | |||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_net::tcp::TcpSocket; | ||
| 13 | use embassy_net::{Config, Stack, StackResources}; | ||
| 14 | use embassy_rp::gpio::{Level, Output}; | ||
| 15 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 16 | use embassy_rp::pio::Pio; | ||
| 17 | use embassy_time::Duration; | ||
| 18 | use embedded_io::asynch::Write; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | macro_rules! singleton { | ||
| 23 | ($val:expr) => {{ | ||
| 24 | type T = impl Sized; | ||
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 26 | STATIC_CELL.init_with(move || $val) | ||
| 27 | }}; | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn wifi_task( | ||
| 32 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | |||
| 48 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 49 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 50 | |||
| 51 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 52 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 53 | // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 54 | // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 55 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 56 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 57 | |||
| 58 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 59 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 60 | let mut pio = Pio::new(p.PIO0); | ||
| 61 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 62 | |||
| 63 | let state = singleton!(cyw43::State::new()); | ||
| 64 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 65 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 66 | |||
| 67 | control.init(clm).await; | ||
| 68 | control | ||
| 69 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 70 | .await; | ||
| 71 | |||
| 72 | // Use a link-local address for communication without DHCP server | ||
| 73 | let config = Config::Static(embassy_net::StaticConfig { | ||
| 74 | address: embassy_net::Ipv4Cidr::new(embassy_net::Ipv4Address::new(169, 254, 1, 1), 16), | ||
| 75 | dns_servers: heapless::Vec::new(), | ||
| 76 | gateway: None, | ||
| 77 | }); | ||
| 78 | |||
| 79 | // Generate random seed | ||
| 80 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 81 | |||
| 82 | // Init network stack | ||
| 83 | let stack = &*singleton!(Stack::new( | ||
| 84 | net_device, | ||
| 85 | config, | ||
| 86 | singleton!(StackResources::<2>::new()), | ||
| 87 | seed | ||
| 88 | )); | ||
| 89 | |||
| 90 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 91 | |||
| 92 | //control.start_ap_open("cyw43", 5).await; | ||
| 93 | control.start_ap_wpa2("cyw43", "password", 5).await; | ||
| 94 | |||
| 95 | // And now we can use it! | ||
| 96 | |||
| 97 | let mut rx_buffer = [0; 4096]; | ||
| 98 | let mut tx_buffer = [0; 4096]; | ||
| 99 | let mut buf = [0; 4096]; | ||
| 100 | |||
| 101 | loop { | ||
| 102 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 103 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 104 | |||
| 105 | control.gpio_set(0, false).await; | ||
| 106 | info!("Listening on TCP:1234..."); | ||
| 107 | if let Err(e) = socket.accept(1234).await { | ||
| 108 | warn!("accept error: {:?}", e); | ||
| 109 | continue; | ||
| 110 | } | ||
| 111 | |||
| 112 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 113 | control.gpio_set(0, true).await; | ||
| 114 | |||
| 115 | loop { | ||
| 116 | let n = match socket.read(&mut buf).await { | ||
| 117 | Ok(0) => { | ||
| 118 | warn!("read EOF"); | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | Ok(n) => n, | ||
| 122 | Err(e) => { | ||
| 123 | warn!("read error: {:?}", e); | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | |||
| 128 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 129 | |||
| 130 | match socket.write_all(&buf[..n]).await { | ||
| 131 | Ok(()) => {} | ||
| 132 | Err(e) => { | ||
| 133 | warn!("write error: {:?}", e); | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | } | ||
| 138 | } | ||
| 139 | } | ||
diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs new file mode 100644 index 000000000..aa5e5a399 --- /dev/null +++ b/examples/rp/src/bin/wifi_scan.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | #![feature(async_fn_in_trait)] | ||
| 5 | #![allow(incomplete_features)] | ||
| 6 | |||
| 7 | use core::str; | ||
| 8 | |||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_net::Stack; | ||
| 13 | use embassy_rp::gpio::{Level, Output}; | ||
| 14 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 15 | use embassy_rp::pio::Pio; | ||
| 16 | use static_cell::StaticCell; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | macro_rules! singleton { | ||
| 20 | ($val:expr) => {{ | ||
| 21 | type T = impl Sized; | ||
| 22 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 23 | STATIC_CELL.init_with(move || $val) | ||
| 24 | }}; | ||
| 25 | } | ||
| 26 | |||
| 27 | #[embassy_executor::task] | ||
| 28 | async fn wifi_task( | ||
| 29 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 30 | ) -> ! { | ||
| 31 | runner.run().await | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 36 | stack.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(spawner: Spawner) { | ||
| 41 | info!("Hello World!"); | ||
| 42 | |||
| 43 | let p = embassy_rp::init(Default::default()); | ||
| 44 | |||
| 45 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 46 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 47 | |||
| 48 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 49 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 50 | // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 51 | // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 52 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 53 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 54 | |||
| 55 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 56 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 57 | let mut pio = Pio::new(p.PIO0); | ||
| 58 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 59 | |||
| 60 | let state = singleton!(cyw43::State::new()); | ||
| 61 | let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 62 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 63 | |||
| 64 | control.init(clm).await; | ||
| 65 | control | ||
| 66 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 67 | .await; | ||
| 68 | |||
| 69 | let mut scanner = control.scan().await; | ||
| 70 | while let Some(bss) = scanner.next().await { | ||
| 71 | if let Ok(ssid_str) = str::from_utf8(&bss.ssid) { | ||
| 72 | info!("scanned {} == {:x}", ssid_str, bss.bssid); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs new file mode 100644 index 000000000..eafa25f68 --- /dev/null +++ b/examples/rp/src/bin/wifi_tcp_server.rs | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | #![feature(async_fn_in_trait)] | ||
| 5 | #![allow(incomplete_features)] | ||
| 6 | |||
| 7 | use core::str::from_utf8; | ||
| 8 | |||
| 9 | use cyw43_pio::PioSpi; | ||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_net::tcp::TcpSocket; | ||
| 13 | use embassy_net::{Config, Stack, StackResources}; | ||
| 14 | use embassy_rp::gpio::{Level, Output}; | ||
| 15 | use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_25, PIO0}; | ||
| 16 | use embassy_rp::pio::Pio; | ||
| 17 | use embassy_time::Duration; | ||
| 18 | use embedded_io::asynch::Write; | ||
| 19 | use static_cell::StaticCell; | ||
| 20 | use {defmt_rtt as _, panic_probe as _}; | ||
| 21 | |||
| 22 | macro_rules! singleton { | ||
| 23 | ($val:expr) => {{ | ||
| 24 | type T = impl Sized; | ||
| 25 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 26 | STATIC_CELL.init_with(move || $val) | ||
| 27 | }}; | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::task] | ||
| 31 | async fn wifi_task( | ||
| 32 | runner: cyw43::Runner<'static, Output<'static, PIN_23>, PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>>, | ||
| 33 | ) -> ! { | ||
| 34 | runner.run().await | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::task] | ||
| 38 | async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! { | ||
| 39 | stack.run().await | ||
| 40 | } | ||
| 41 | |||
| 42 | #[embassy_executor::main] | ||
| 43 | async fn main(spawner: Spawner) { | ||
| 44 | info!("Hello World!"); | ||
| 45 | |||
| 46 | let p = embassy_rp::init(Default::default()); | ||
| 47 | |||
| 48 | let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); | ||
| 49 | let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); | ||
| 50 | |||
| 51 | // To make flashing faster for development, you may want to flash the firmwares independently | ||
| 52 | // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: | ||
| 53 | // probe-rs-cli download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 | ||
| 54 | // probe-rs-cli download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 | ||
| 55 | //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; | ||
| 56 | //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; | ||
| 57 | |||
| 58 | let pwr = Output::new(p.PIN_23, Level::Low); | ||
| 59 | let cs = Output::new(p.PIN_25, Level::High); | ||
| 60 | let mut pio = Pio::new(p.PIO0); | ||
| 61 | let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); | ||
| 62 | |||
| 63 | let state = singleton!(cyw43::State::new()); | ||
| 64 | let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; | ||
| 65 | unwrap!(spawner.spawn(wifi_task(runner))); | ||
| 66 | |||
| 67 | control.init(clm).await; | ||
| 68 | control | ||
| 69 | .set_power_management(cyw43::PowerManagementMode::PowerSave) | ||
| 70 | .await; | ||
| 71 | |||
| 72 | let config = Config::Dhcp(Default::default()); | ||
| 73 | //let config = embassy_net::Config::Static(embassy_net::Config { | ||
| 74 | // address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 69, 2), 24), | ||
| 75 | // dns_servers: Vec::new(), | ||
| 76 | // gateway: Some(Ipv4Address::new(192, 168, 69, 1)), | ||
| 77 | //}); | ||
| 78 | |||
| 79 | // Generate random seed | ||
| 80 | let seed = 0x0123_4567_89ab_cdef; // chosen by fair dice roll. guarenteed to be random. | ||
| 81 | |||
| 82 | // Init network stack | ||
| 83 | let stack = &*singleton!(Stack::new( | ||
| 84 | net_device, | ||
| 85 | config, | ||
| 86 | singleton!(StackResources::<2>::new()), | ||
| 87 | seed | ||
| 88 | )); | ||
| 89 | |||
| 90 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 91 | |||
| 92 | loop { | ||
| 93 | //control.join_open(env!("WIFI_NETWORK")).await; | ||
| 94 | match control.join_wpa2(env!("WIFI_NETWORK"), env!("WIFI_PASSWORD")).await { | ||
| 95 | Ok(_) => break, | ||
| 96 | Err(err) => { | ||
| 97 | info!("join failed with status={}", err.status); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | // And now we can use it! | ||
| 103 | |||
| 104 | let mut rx_buffer = [0; 4096]; | ||
| 105 | let mut tx_buffer = [0; 4096]; | ||
| 106 | let mut buf = [0; 4096]; | ||
| 107 | |||
| 108 | loop { | ||
| 109 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 110 | socket.set_timeout(Some(Duration::from_secs(10))); | ||
| 111 | |||
| 112 | control.gpio_set(0, false).await; | ||
| 113 | info!("Listening on TCP:1234..."); | ||
| 114 | if let Err(e) = socket.accept(1234).await { | ||
| 115 | warn!("accept error: {:?}", e); | ||
| 116 | continue; | ||
| 117 | } | ||
| 118 | |||
| 119 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 120 | control.gpio_set(0, true).await; | ||
| 121 | |||
| 122 | loop { | ||
| 123 | let n = match socket.read(&mut buf).await { | ||
| 124 | Ok(0) => { | ||
| 125 | warn!("read EOF"); | ||
| 126 | break; | ||
| 127 | } | ||
| 128 | Ok(n) => n, | ||
| 129 | Err(e) => { | ||
| 130 | warn!("read error: {:?}", e); | ||
| 131 | break; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | info!("rxd {}", from_utf8(&buf[..n]).unwrap()); | ||
| 136 | |||
| 137 | match socket.write_all(&buf[..n]).await { | ||
| 138 | Ok(()) => {} | ||
| 139 | Err(e) => { | ||
| 140 | warn!("write error: {:?}", e); | ||
| 141 | break; | ||
| 142 | } | ||
| 143 | }; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | } | ||
