Compare commits

...

583 Commits

Author SHA1 Message Date
f8299d10f7 Prepare executor and macros for release (#1825)
* Set release date, bump macros version

* Add pool_size change to changelog
2023-08-25 23:32:00 +02:00
8339423a2f Merge pull request #1821 from embassy-rs/net-ppp
Add embassy-net-ppp driver.
2023-08-25 18:50:10 +00:00
295542f4d3 Merge pull request #1792 from vDorst/adin1110-pr
Added support and example for Analog ADIN1110 SPE SPI ethernet chip.
2023-08-25 18:47:32 +00:00
d812cc5745 net-ppp: add std example. 2023-08-25 20:45:23 +02:00
623f37a273 net-ppp: add callback for IP configuration. 2023-08-25 20:45:23 +02:00
a026db3f57 net-ppp: use From and ? to handle write errors. 2023-08-25 20:45:23 +02:00
c2d601abef net-ppp: take serial port and config in run(), allow calling it multiple times. 2023-08-25 20:45:23 +02:00
2303382dfd net-ppp: nicer processing loop structure that can't deadlock. 2023-08-25 20:45:23 +02:00
aacf14b62a net-ppp: Add it. 2023-08-25 20:45:23 +02:00
100200d021 net-driver-channel: do not hardcode medium to ethernet. 2023-08-25 20:45:23 +02:00
b5748524f8 net: improve error message on unsupported medium. 2023-08-25 20:45:23 +02:00
6d3377e6a6 Merge pull request #1824 from embassy-rs/net-change-config
net: allow changing IP config at runtime.
2023-08-25 18:35:14 +00:00
cc8961034e net: allow changing IP config at runtime. 2023-08-25 20:26:46 +02:00
2a6b743b9e Merge pull request #1814 from xoviat/rtc-lp
stm32: add low-power mod
2023-08-24 01:30:17 +00:00
ecc305bbfe stm32: fix executor version 2023-08-23 20:28:41 -05:00
9c94eac919 Merge branch 'main' of https://github.com/embassy-rs/embassy into rtc-lp 2023-08-23 20:27:23 -05:00
00aadf6085 stm32/lp: pipe through lp ready 2023-08-23 20:22:11 -05:00
83f224e140 stm32/lp: add refcount 2023-08-23 20:18:34 -05:00
e987259716 rtc: cleanup 2023-08-23 20:01:35 -05:00
fc04d2a33c rtc: get wakup irq working 2023-08-23 19:52:32 -05:00
1d4d11ba25 refactor write_fifo(), don´t copy frame data
Now this function uses frame data directly in the SPI transaction
instead making a copy of it.
Also fixing some length calculations and refactor/add tests to test this
function.
2023-08-24 00:52:21 +02:00
5f5e3bcd18 Replace size_align_u32() with next_multiple_of()
Currently next_multiple_of() is behinged a Feature gate: int_rounding.
See https://github.com/rust-lang/rust/issues/88581
But it seems that this function is stablized in rust 1.73.
See https://github.com/rust-lang/rust/pull/94455

Currently Embassy is still using nightly for many other unstable
features. So I do see an issue to use this function.
2023-08-24 00:52:21 +02:00
4b6045d446 Remove the SPI::Error as a generic parameter. 2023-08-24 00:52:21 +02:00
ca588f901f add embassy-net-adin1110 to .github/ci/doc.sh 2023-08-24 00:52:21 +02:00
681165e84c add embassy-net-adin1110 to .github/ci/test.sh 2023-08-24 00:52:21 +02:00
0a7c061ddc rustfmt 2023-08-24 00:52:21 +02:00
1ded213ae9 remove the type_alias_impl_trait feature 2023-08-24 00:52:21 +02:00
0e9524de62 more clippy fixes 2023-08-24 00:52:21 +02:00
e082cd0cda Remove unneeded files. 2023-08-24 00:52:21 +02:00
10bf33dcac Fix linting.
I like program with deny(clippy::pedantic) but it was set as allowed, so
I did get less linting errors/hints. Now it is corrected and also fix
the lint errors and hints.

Also fixes some comment and demagic some values.

Rename `FEC` to more appropriate name `FSC`.
2023-08-24 00:52:21 +02:00
46ddf7013a update README.md after review 2023-08-24 00:52:21 +02:00
577d644e22 fix warning 2023-08-24 00:52:21 +02:00
2cf6a59114 stm32l4: Add EVAL-ADIN1110EBZ example with basic http server
Page show the current temperature and auto refresh every 1s.
2023-08-24 00:52:06 +02:00
e19f7d9a76 Add embassy-net-adin1110
This add a library for Analog ADIN1110 SPE SPI chip.
This library is inspired by `embassy-net-w5500`.
2023-08-24 00:43:51 +02:00
bed1f07c15 Merge pull request #1817 from bugadani/executor-release
Bump executor crate version to 0.3.0
2023-08-23 19:05:50 +00:00
a2c718f61c Bump executor crate version to 0.3.0 2023-08-23 20:34:37 +02:00
0568738f77 Merge pull request #1815 from embassy-rs/esp-hosted-oopsie
net-esp-hosted: fix oopsie
2023-08-23 09:42:32 +00:00
a6d22e199a net-esp-hosted: fix set link down on disconnect. 2023-08-23 11:37:50 +02:00
1ea4c58c39 stm32: impl. draft cfgr pwr 2023-08-22 20:00:00 -05:00
faab2d0d53 stm32: add executor to low-power mod 2023-08-22 17:31:40 -05:00
6d35bcc3d9 stm32: add low-power mod 2023-08-22 17:00:00 -05:00
7d6edd7b15 Merge branch 'main' of https://github.com/embassy-rs/embassy into rtc-lp 2023-08-22 16:58:43 -05:00
9e3266b745 rtc: make fns private 2023-08-22 16:58:19 -05:00
7bff2ebab3 Merge pull request #1766 from xoviat/rtc-w
stm32/rtc: add start/stop wakeup
2023-08-22 21:50:53 +00:00
048bdf6968 stm32/rtc: allow dead code 2023-08-22 16:48:08 -05:00
b3212ae383 Merge pull request #1763 from rubdos/sender-receiver-with-ctx
Refactor Channel/Sender/Receiver poll methods
2023-08-22 21:25:29 +00:00
b436aad2a0 Merge pull request #1810 from soypat/write_wlan-fix
rp::wlan_write: avoid ineffectual writing of entire buffer over spi
2023-08-22 21:20:59 +00:00
31c5bdfa29 Merge pull request #1812 from bugadani/storage
Make `AvailableTask` public
2023-08-22 21:14:29 +00:00
906ab06a6e Merge pull request #1813 from diondokter/stm32-brr-u32
Modified the STM Uart baud calculation to be fully 32-bit
2023-08-22 19:56:04 +00:00
a5484cd119 Modified the brr calculation to be fully 32-bit 2023-08-22 20:52:02 +02:00
c39671266e Deprecate *recv* in favor of *receive* 2023-08-22 16:58:31 +02:00
8878ce046c rtc: fix rtcinstant delay computation 2023-08-21 18:33:10 -05:00
5bfddfc9b6 stm32/rcc: add rtc to f410 2023-08-21 18:10:10 -05:00
7148397771 stm32/rtc: misc fixes 2023-08-21 18:00:49 -05:00
8c12453544 stm32/rcc: set rtc clock on f4 2023-08-21 17:50:18 -05:00
f723982bec rtc: impl. draft rtcinstant api 2023-08-21 17:44:38 -05:00
0a73c84df0 Make AvailableTask public, deduplicate 2023-08-21 18:12:41 +02:00
977ae5e3e4 don't pay for what you don't use 2023-08-20 20:16:23 -03:00
2687008242 Merge pull request #1805 from embassy-rs/update-nightly-6
Update Nightly.
2023-08-20 09:05:14 +00:00
6ef888d73e Merge pull request #1807 from romixlab/main
Fix hardcoded buffer length in USB NCM causing broken link on USB 2.0.
2023-08-20 09:05:07 +00:00
67ca88d5a0 stm32/can: make latency assertion more lenient. 2023-08-20 10:57:28 +02:00
17e9a8ebe1 Fix hardcoded buffer length in USB NCM causing broken link on USB 2.0. 2023-08-20 07:42:54 +02:00
0032f8a2ec rustfmt. 2023-08-19 19:45:55 +02:00
2d980068c0 Update Nightly. 2023-08-19 19:31:49 +02:00
6babd5752e Merge pull request #1804 from embassy-rs/fix-f37x
stm32: fix f37x build.
2023-08-18 23:21:39 +00:00
5d4da78c94 Fix lora docs build. 2023-08-19 01:15:32 +02:00
cc400aa178 stm32: fix f37x build.
originally broke in https://github.com/embassy-rs/embassy/pull/1762
2023-08-19 01:15:32 +02:00
7b3cb2ce04 Merge pull request #1802 from avlec/error-translation
Add error translation to tcp errors
2023-08-18 21:58:27 +00:00
afd8be416e Merge pull request #1803 from randomplum/flash_h7ab
configure flash latency after axi clock and  handle different flash on STM32H7A/B devices
2023-08-18 21:53:21 +00:00
5bc0175be9 configure flash latency after axi clock and handle different flash in STM32H7A/B devices 2023-08-18 23:44:56 +02:00
eb05a18c45 add error translation to tcp errors
Translation of tpc client ConnectError and Error to the appropriate
embedded_io_async errors
2023-08-18 14:43:44 -07:00
97da34595c Merge pull request #1798 from aurelj/stm32_pwm_polarity
stm32: allow setting the PWM output polarity
2023-08-18 14:51:48 +00:00
2ea17d2783 stm32: allow setting the PWM output polarity 2023-08-18 16:45:50 +02:00
8754a1d378 Merge pull request #1779 from MabezDev/forward-capacity-impls
Forward capacity impls
2023-08-18 14:21:48 +00:00
d327b626e3 Merge pull request #1788 from embassy-rs/esp-hosted-cleanup
net-esp-hosted: misc improvements.
2023-08-18 14:21:10 +00:00
f48d13a16a Merge pull request #1796 from embassy-rs/enc28j60-fixes
enc28j60: add HIL test, workaround RX buffer corruption.
2023-08-18 14:20:44 +00:00
4c11fffc90 Merge pull request #1797 from aurelj/stm32_pwm_max_duty
stm32: allow setting PWM duty cycle to 100%
2023-08-18 14:20:32 +00:00
7f97efd922 net-enc28j60: add HIL test. 2023-08-18 16:11:18 +02:00
78bb261246 stm32: allow setting PWM duty cycle to 100%
Setting the compare_value to max_compare_value make the PWM output
go low when the timer reach the max_compare_value and go high again
on the next timer clock, when the value roll back to 0.
So to allow a 100% PWM that never goes low, the compare_value must
be set to max_compare_value + 1.
2023-08-18 16:10:49 +02:00
73942f50cb net-enc28j60: reset rx logic when buffer corrupts. 2023-08-18 15:51:48 +02:00
3ebb93e47d net-enc28j60: remove useless 1ms sleep. 2023-08-18 15:44:52 +02:00
5329f234ba Merge pull request #1795 from kext/msos-multisz-terminator
embassy-usb: Fixed the final null terminator for RegMultiSz.
2023-08-18 11:28:06 +00:00
2cc82ef660 Merge pull request #1794 from embassy-rs/rp-flash-naming
rp/flash: change naming to `blocking_*`, `new_blocking`.
2023-08-18 11:24:27 +00:00
b948e37769 rp/flash: change naming to blocking_*, new_blocking.
- Needed for consistency with other drivers.
- Having two `new()` functions sometimes resulted in 'multiple applicable methods' errors.
2023-08-18 13:21:21 +02:00
91b10dd799 Fixed the final null terminator for RegMultiSz.
The RegMultiSz value should be terminated by an empty UTF-16 string, i.e. 2 null bytes.
2023-08-18 13:09:27 +02:00
94fa95c699 Merge pull request #1793 from ARizzo35/stm32l4-rtc-pwren
stm32l4: set pwren in rcc regardless of clock source
2023-08-18 10:19:54 +00:00
62e66cdda3 stm32l4: set pwren in rcc regardless of clock source 2023-08-17 19:16:03 -05:00
1cb76e0d99 net-esp-hosted: enable heartbeats from esp32 to detect if it crashes. 2023-08-17 01:03:12 +02:00
ef7523e5b7 net-esp-hosted: put link down on wifi disconnect. 2023-08-17 01:03:12 +02:00
065b0f34af net-esp-hosted: sane error handling in control requests. 2023-08-17 01:03:12 +02:00
2a4ebdc150 Merge pull request #1787 from embassy-rs/nrf-internal-defmt
nrf: enable defmt for embassy-hal-internal.
2023-08-16 21:42:55 +00:00
bb275f7e25 nrf: enable defmt for embassy-hal-internal. 2023-08-16 23:37:40 +02:00
4f453d7ed6 Merge pull request #1786 from embassy-rs/net-w5100s-hil
net-wiznet: add HIL test using w5100s.
2023-08-16 15:57:10 +00:00
8a9f49c163 net-wiznet: add HIL test using w5100s. 2023-08-16 17:51:47 +02:00
19b1e32c2c Merge pull request #1785 from oll3/fix/set-rtc-clock-source
stm32-wl: set RTC clock source on RCC init
2023-08-16 15:02:24 +00:00
c80c323634 stm32-wl: set RTC clock source on RCC init 2023-08-16 14:41:00 +02:00
dd5a886830 Merge pull request #1784 from sgoll/check-rcc-before-apply
stm32f2: Check RCC settings before applying
2023-08-16 12:17:38 +00:00
df6952648e Make sure to check RCC settings for compatibility before applying 2023-08-16 14:11:09 +02:00
f26dd54f63 Update embedded-hal to 1.0.0-rc.1 (#1783) 2023-08-16 00:40:56 +02:00
ffe9688952 Merge pull request #1525 from embassy-rs/w5100s
wip: w5100s
2023-08-15 21:15:52 +00:00
11b66a73b4 net-wiznet: rename from embassy-net-w5500. 2023-08-15 23:05:55 +02:00
1d4b941d52 net-w5500: add w5100s support. 2023-08-15 22:53:22 +02:00
76276c326a net-w5500: extract chip-specific stuff to a trait. 2023-08-15 22:52:37 +02:00
a436bd068f net-w5500: inline socket into device. 2023-08-15 22:52:37 +02:00
c367b84ee5 net-w5500: add Address type. 2023-08-15 22:52:37 +02:00
46f671ae42 rp: fix async spi read sometimes hanging. 2023-08-15 22:47:58 +02:00
96e0ace89e Merge pull request #1781 from bugadani/typo
Fix typo
2023-08-15 17:24:28 +00:00
8655ba110c Fix typo 2023-08-15 19:13:36 +02:00
7d24e433d8 Merge pull request #1780 from embassy-rs/w5500-simplify
net-w5500: simplify rx logic.
2023-08-15 15:32:29 +00:00
098fcb14b5 net-w5500: simplify rx logic. 2023-08-15 16:47:45 +02:00
c114ea024a Add udp capacity impls 2023-08-15 15:31:23 +01:00
b1ef009c6b Add tcp capacity impls 2023-08-15 15:31:23 +01:00
b6b4448045 ci/docs: build embassy-net-enc28j60. 2023-08-15 15:13:03 +02:00
cb3644856d Merge pull request #1777 from MabezDev/embassy-net/async-send-recv
embassy-net:tcp:write_with/read_with
2023-08-15 13:08:05 +00:00
03576b9e83 Merge pull request #1746 from embassy-rs/enc28j60-v2
wip: enc28j60 driver.
2023-08-15 12:34:19 +00:00
ea9f887ee1 net-enc28j60: add docs, readme. 2023-08-15 14:27:31 +02:00
f7f75167ac cleanup vscode settings. 2023-08-15 14:15:29 +02:00
253b28deba net-enc28j60: fix PHY read unreliable due to missing dummy byte. 2023-08-15 14:09:36 +02:00
4af1cf88d2 net-enc28j60: add example. 2023-08-15 14:09:36 +02:00
2c1402843a wip: enc28j60 driver. 2023-08-15 14:09:36 +02:00
0fd9d7400b Build stm32 docs last. 2023-08-15 00:19:17 +02:00
bd58b5002a Merge pull request #1774 from bugadani/executor
Allow custom executors without indirection
2023-08-14 20:43:51 +00:00
890f29ccfe Update docs 2023-08-14 17:57:01 +02:00
e4f3979ec8 Don't check context if only thread-mode is enabled 2023-08-14 16:46:02 +02:00
07c3600127 Hide Pender 2023-08-14 16:35:48 +02:00
da4f15d944 Fix fn name 2023-08-14 16:34:10 +02:00
fbec797d64 embassy-net:tcp:send/recv
- Add async versions of smoltcp's `send` and `recv` closure based API.
2023-08-14 15:33:02 +01:00
9954346143 Remove interrupt executor, remove PenderContext 2023-08-14 16:04:11 +02:00
3a51e2d9ca Make PenderContext actually pointer-size 2023-08-14 15:45:43 +02:00
986a63ebb8 Remove the non-specific thread-mode executor 2023-08-14 15:41:53 +02:00
4c4b12c307 Make PenderContext opaque 2023-08-14 15:16:40 +02:00
f6007869bf Remove the Pender enum 2023-08-14 09:00:08 +02:00
454a7cbf4c Remove pender-callback 2023-08-14 08:32:26 +02:00
ec6bd27df6 Remove thread-context feature 2023-08-14 08:22:22 +02:00
6ab0d71d92 Tweak identifiers and comments 2023-08-12 22:42:50 +02:00
d5e66f6f87 Lift thread-context feature restrictions 2023-08-12 22:20:11 +02:00
bce250bbdc Remove unnecessary !Send markers 2023-08-12 22:17:03 +02:00
fbf50cdae8 Remove Pender wrapper 2023-08-12 22:13:08 +02:00
675b7fb605 POC: allow custom executors 2023-08-12 18:29:56 +02:00
0727f8690c Merge pull request #1773 from embassy-rs/boot-improvs
boot: release flash after prepare and refactor api
2023-08-11 19:49:33 +00:00
55ff397c0c boot: release flash after prepare and refactor api
This refactoring of the chip specific bootloader creates the internal boot instance and aligned
buffer in the prepare stage, so that they are automatically dropped after. This unlocks a use
case where peripherals owning the flash need to be Drop'ed before load() happens.
2023-08-11 20:58:31 +02:00
b1ec460b9a Implement Channel::poll_receive(..) -> Poll<T> 2023-08-11 13:22:56 +02:00
f9d251cd5c Channel poll methods return Poll instead of bool 2023-08-11 12:13:46 +02:00
b658f10db9 Expose poll_ready_to_{send,receive} in Sender/Receiver 2023-08-11 12:13:42 +02:00
3a3f3b492f rustfmt 2023-08-10 19:14:55 -05:00
b69861013a stm32/rtc: implement stop_wakeup_alarm 2023-08-10 19:13:48 -05:00
a0c69ffe02 stm32/rtc: autocompute wakeup psc. 2023-08-10 18:59:18 -05:00
c1da2c0219 Merge pull request #1769 from embassy-rs/spim-disable-irq-on-drop
fix: ensure spi irq is disabled when dropped
2023-08-10 21:46:06 +00:00
611e82711a Merge pull request #1770 from bugadani/exec
Executor: prepare 0.2.1 release
2023-08-10 21:26:09 +00:00
baef856206 Bump version 2023-08-10 23:20:31 +02:00
858d520882 Executor: Add changelog 2023-08-10 23:15:02 +02:00
7d8e3951ba fix: ensure spi irq is disabled when dropped 2023-08-10 22:26:56 +02:00
3b43b00867 Merge pull request #1762 from dreilly1982/adc-f3-build-rs-updates
don't generate adc peripheral for f3 series
2023-08-10 15:45:21 +00:00
95262ad559 Merge pull request #1743 from xoviat/dma-2
stm32/dma: consolidate ringbuf
2023-08-10 15:21:31 +00:00
43e2edfbda Merge pull request #1748 from lorislibralato/main
Avoid calling pend() when waking expired timers
2023-08-10 15:10:10 +00:00
fb49e03eda Merge pull request #1768 from smartelfi/main
fix rng ced toggling sequence on reset.
2023-08-10 15:09:31 +00:00
c312884692 added exclusion for adc v4 as well 2023-08-10 08:21:03 -05:00
bc156afbb2 fix rng ced toggling sequence on reset. 2023-08-10 16:16:45 +03:00
8a9622ec2e update metapac tag 2023-08-10 08:03:35 -05:00
0705152105 stm32/rtc: add start/stop wakeup 2023-08-09 20:15:14 -05:00
8d71fbd032 Merge pull request #1765 from embassy-rs/fmt-not-pub
fmt mod must not be public.
2023-08-09 17:06:28 +00:00
820852be28 fmt mod must not be public. 2023-08-09 18:33:20 +02:00
fcb77f3f96 Merge pull request #1753 from xoviat/rtc-wb
stm32/rtc: enable in rcc mod
2023-08-09 01:48:11 +00:00
32fdd4c787 tests/stm32: fix rtc test 2023-08-08 20:33:24 -05:00
6a73ab1afa stm32/l4: set rtc clock source in rcc 2023-08-08 19:58:03 -05:00
6fc5c608f8 stm32/rtc: remove generics and segregate clock sel 2023-08-08 19:47:01 -05:00
b7114fb951 Merge pull request #1761 from sgoll/fuse-ticker
Mark `Ticker` stream as fused
2023-08-08 23:14:24 +00:00
d375c46590 Merge pull request #1751 from oro-os/add-pin-drop-docs
stm32: add note about Output/OutputOpenDrain drop behavior
2023-08-08 23:05:12 +00:00
c40b944da6 Mark Ticker stream as FusedStream 2023-08-09 00:17:02 +02:00
e31545860e don't generate adc peripheral for f3 series 2023-08-08 16:46:30 -05:00
47b8e04b1c Merge pull request #1760 from sinewave-ee/master
embassy-sync: manual Copy impls for channel and pipe
2023-08-08 09:41:03 +00:00
2ab9a07b64 embassy-sync: manual Copy impls for channel and pipe 2023-08-08 11:22:01 +02:00
5d5cd23715 Update to embedded-io 0.5 (#1752) 2023-08-07 13:43:09 +02:00
77844e2055 Merge pull request #1755 from GrantM11235/clippy-fixes
embassy-stm32: Misc clippy fixes
2023-08-06 23:38:32 +00:00
28eb3b95b0 Merge pull request #1756 from dhylands/fix-usb-logger
Make usb-logger read and discard input data
2023-08-06 23:35:53 +00:00
8b837fae07 Make usb-logger read and discard input data
This allows normal linux terminal emulators, like screen or picocom
to be used with the usb_logger. Without this, calling `tcsetattr` with
`TCSAFLUSH` will hang.
2023-08-06 13:08:08 -07:00
d49f40dd5c embassy-stm32: Misc clippy fixes 2023-08-06 15:00:39 -05:00
477a90b8e3 Merge pull request #1739 from embassy-rs/refactor-fw-updater
Refactor firmware updater
2023-08-06 18:06:18 +00:00
a34331ae5f Refactor firmware updater
* Allow manipulating state without accessing DFU partition.
* Provide aligned buffer when creating updater to reduce potential wrong parameters passed.
2023-08-06 19:46:53 +02:00
b555af1c5d stm32/rtc: fix exampel 2023-08-06 12:12:18 -05:00
ae608cf2fa stm32: fix rtc and examples 2023-08-06 12:06:29 -05:00
28618d12a1 stm32/rtc: restructure 2023-08-06 11:58:28 -05:00
66c1712118 stm32/rtc: enable in rcc mod 2023-08-06 11:11:53 -05:00
7b3d7a3826 stm32: add note about Output/OutputOpenDrain drop behavior 2023-08-06 05:58:38 +02:00
d7031fbe92 Merge branch 'embassy-rs:main' into main 2023-08-05 21:05:53 +02:00
02fcb07aa9 Merge branch 'main' of github.com:lorislibralato/embassy 2023-08-05 21:05:10 +02:00
66faba2df7 add wake_task_no_pend for expired timer enqueue inside run_queue 2023-08-05 21:04:51 +02:00
766b5fc6f6 Merge pull request #1747 from xoviat/fix-spi
stm32: update metapac version
2023-08-05 00:17:05 +00:00
a2fd7108ff stm32: update metapac version 2023-08-04 19:08:53 -05:00
630372b183 Merge pull request #1745 from pennae/rp-dormant-sleep
rp: add generic dormant-sleep functionality
2023-08-04 22:59:03 +00:00
3c5f011245 rp: add generic dormant-sleep functionality
this is "generic" in that it doesn't require the user to set up anything
specific to go to dormant sleep, unlike the C sdk which requires clock
sources to be configured explicitly and doesn't much care about PLLs. we
will instead take a snapshot of the current clock configuration, switch
to a known clock source (very slow rosc, in this case), go to sleep, and
on wakeup undo everything we've done (ensuring stability of PLLs and
such).

tested locally, but adding tests to HIL seems infeasible. we'd need at
least another pico or extensive modifications to teleprobe since
dormant-sleep breaks SWD (except to rescue-dp), neither of which is
feasible at this point. if we *did* want to add tests we should check
for both rtc wakeups (with an external rtc clock source) and gpio wakeups.
2023-08-05 00:57:29 +02:00
e80db42061 stm32/dma: minor cleanup, optmization 2023-08-04 17:15:56 -05:00
7e269f6f17 stm32/dma: consolidate ringbuf 2023-08-03 21:12:34 -05:00
30358c435e Merge pull request #1741 from pennae/rp-adc-test
rp: fix adc test flakiness
2023-08-03 21:39:04 +00:00
55e07712e5 rp: fix adc test flakiness
GP29 is connected to the cyw43 SCK pin. cyw43 is selected by
default (due to rp2040 pins being input/pulldown by default), so the
wifi chip is always selected and watches the SCK pin. this little bit of
load on the SCK pin is enough to disturb the 300k voltage divider used
for VSYS sensing, making the test flaky.
2023-08-03 23:38:23 +02:00
293edc5772 Merge pull request #1740 from adamgreig/stm32f334-examples
Fix package name for stm32f334-examples
2023-08-03 21:05:28 +00:00
dc5acc687f Fix package name for stm32f334-examples 2023-08-03 21:57:29 +01:00
a40daa923b Merge pull request #1715 from pennae/rp-dormant-input
rp: add dormant-wake functionality for Input
2023-08-03 17:30:07 +00:00
9dfda46e0c rp: add dormant-wake functionality for Input
this temporarily takes ownership of pins because we need to clear edge
interrupts before waiting for them (otherwise we may wait indefinitely),
we want to clean up the dormant-wake bits after a wakeup, and doing
anything *else* with the input while we're waiting for a wakeup isn't
possible at all. doing it like this lets us not impose any cost on those
who don't use dormant wakes without entangling dormant waits too badly
with regular interrupt waits.
2023-08-03 19:22:47 +02:00
4d60c715e6 net: move tuntap from std example to separate crate. (#1737) 2023-08-03 14:23:11 +02:00
2c96fe917d Merge pull request #1675 from pennae/rp-adc
rp: add dma-from-adc
2023-08-03 11:25:51 +00:00
b42a7ebd8c Merge pull request #1733 from jannic/fix-link
Fix link to embassy-time in embassy-net/README.md
2023-08-02 21:21:36 +00:00
9b1bdc3099 Merge pull request #1734 from embassy-rs/generic-nrf-watchdog-flash
feat: make nrf bootloader watchdog generic for any flash
2023-08-02 21:06:14 +00:00
bcaef1de18 feat: make nrf bootloader watchdog generic for any flash 2023-08-02 22:57:42 +02:00
33778d3772 Fix link to embassy-time in embassy-net/README.md 2023-08-02 20:13:48 +00:00
a6b8f3d994 rp: add single-channel dma from adc
with uniform treatment of adc inputs it's easy enough to add a new
sampling method. dma sampling only supports one channel at the moment,
though round-robin sampling would be a simple extension (probably a new
trait that's implemented for Channel and &[Channel]). continuous dma as
proposed in #1608 also isn't done here, we'd expect that to be a
compound dma::Channel that internally splits a buffer in half and
dispatches callbacks or something like that.
2023-08-02 17:04:32 +02:00
b166ed6b78 rp: generalize adc inputs from pins to channels
this lets us treat pins and the temperature sensor uniformly using the
same interface. uniformity in turn lets us add more adc features without
combinatorial explosion of methods and types needed to handle them all.
2023-08-01 18:31:28 +02:00
54d31c98fe rp: remove AdcChannel::channel
we're not using it, and actually using it is more trouble than it's
worth. remove the false assurance instead.
2023-08-01 18:31:28 +02:00
48eac0b146 rp: add AdcChannel trait
this is more general than AdcPin and can also cover the temperature
sensor.
2023-08-01 18:31:28 +02:00
0d8a9b1e7a Merge pull request #1729 from mattico/i2c-async-timeout
stm32: add async timeout functions to I2c and TimeoutI2c
2023-08-01 08:20:48 +00:00
ef3b1f46a9 Merge pull request #1728 from pennae/rp-gpio-banks
rp: fix qspi gpio interrupts, make qspi gpio optional
2023-08-01 08:18:39 +00:00
5e4f65fe1f Merge pull request #1730 from bartekkowalski/main
LSE requires setting PWREN bit on STM32L4
2023-08-01 08:07:55 +00:00
5fcebd28f4 Fix unlocking the backup domain when enabling LSE
Set PWREN bit to enable the power interface clock before enabling access to the backup domain.
2023-08-01 13:46:34 +09:30
a1fce1b554 Merge pull request #1714 from xoviat/dma
stm32/dma: add writable ringbuf
2023-07-31 22:57:30 +00:00
bbc8424a5b stm32/dma: remove trace 2023-07-31 17:55:25 -05:00
036bc669cd stm32: only enable async TimeoutI2c on V2 I2C peripheral 2023-07-31 14:17:50 -05:00
26cc0e634d stm32: add async timeout functions to I2c and TimeoutI2c 2023-07-31 13:47:03 -05:00
1b0f4ee653 stm32: add outlives bounds to TimeoutI2c impl blocks
This should make usage and error messages more clear.
2023-07-31 13:35:06 -05:00
f3ad0c6ade rp: fix qspi gpio interrupts
so far only bank0 interrupts were processed and configured, even if a
qspi pin was given. this is obviously not the right thing to do, so
let's rectify that. the fact that no problems have shown up so far does
suggest that most, if not all, applications don't use this functionality
at all.
2023-07-31 20:02:06 +02:00
4da9743317 Merge pull request #1690 from Sizurka/rp-flash-ptr-fix
rp: Fix ROM cache ptr() returning the trampoline
2023-07-31 17:26:59 +00:00
dca1777a2f rp: make QSPI gpio support optional
this will be mostly not useful to anyone since flash is attached to
qspi, and using flash chips that don't use the *entire* qspi interface
will severly slow down the chip. the code overhead is minimal right now,
but if we also fix interrupt support on qspi pins this will
change (adding more code to potentially hot paths, using more memory for
wakers that are never used, and preventing the qspi gpio irq from being
used in software interrupts as RTIC applications may want to do).
2023-07-31 19:13:10 +02:00
4a9df60a7b Merge pull request #1727 from embassy-rs/hrtim-v2
stm32: add hrtim v2
2023-07-31 17:04:51 +00:00
2c6fcdbd3f rp: add gpio::Pin::io() for access to io banks 2023-07-31 18:36:37 +02:00
e6d4043279 rp: rename gpio::Pin::io to gpio::Pin::gpio
we'll need access to the pin io bank registers for an upcoming fix, and
having both `io` and `io_bank` or similar can get confusing quickly.
rename `io` to `gpio` to avoid this, and also match the type while there.
2023-07-31 18:28:31 +02:00
5c2ba3b212 stm32: add hrtim v2 2023-07-31 15:42:03 +02:00
ebc173ea75 Merge pull request #1726 from rubdos/doc-ieee802154
Enable IEEE802.15.4 doc building
2023-07-31 13:04:40 +00:00
b394cc3394 Enable IEEE802.15.4 doc building 2023-07-31 15:02:55 +02:00
6caf627262 Merge pull request #1704 from rubdos/ieee802154-fixes
Expose IEEE802.15.4 address in Driver
2023-07-31 12:30:33 +00:00
3d68d42132 CI: ip, ethernet and ieee802.15.4 should be able to co-exist 2023-07-31 14:21:27 +02:00
bdd59b8988 Only skip default-gateway assignment with Medium::Ip 2023-07-31 14:21:27 +02:00
9f55228be0 Use hardware_address() for all media 2023-07-31 14:21:26 +02:00
83ff3cbc69 Add Ip hardware address to Driver 2023-07-31 14:21:26 +02:00
4afdce4ec5 Introduce driver::HardwareAddress without smoltcp dependency 2023-07-31 14:21:26 +02:00
71fcea159f Merge pull request #1725 from embassy-rs/flash-pointless-set-bits
stm32/flash: avoid pointless "if flag is set, set it".
2023-07-31 10:55:30 +00:00
036e00113e stm32/flash: avoid pointless "if flag is set, set it". 2023-07-31 12:48:52 +02:00
958cace36d Merge pull request #1724 from bguruprasath5/stm32g0-flash-support
Added STM32G0 Flash Support
2023-07-31 10:35:28 +00:00
2568c714c8 Merge pull request #1687 from chemicstry/bxcan_timestamp
stm32/can: implement proper RX timestamps
2023-07-31 10:28:05 +00:00
83ab8e057a stm32/can: Fix latency measurement in tests 2023-07-31 13:24:50 +03:00
0ddabf0423 changed ADDR variable to addr 2023-07-31 15:37:01 +05:30
b4d0f24bf9 changed ADDR variable to addr 2023-07-31 15:36:25 +05:30
5a2f61a031 added working example for flash 2023-07-31 15:29:26 +05:30
5b4c099afc added working example for flash 2023-07-31 15:06:15 +05:30
c3357f884a added working example for flash 2023-07-31 14:45:23 +05:30
eff2d71f28 Merge pull request #1723 from JuliDi/patch-1
Fix probe-rs chip type in stm32h7 example
2023-07-31 08:29:30 +00:00
42b21fd7ae added flash support 2023-07-31 13:56:16 +05:30
027801db60 Fix probe chip type in stm32h7 example 2023-07-31 10:17:44 +02:00
ad85beb677 stm32/can: Add more derives for CAN Envelope 2023-07-31 10:32:17 +03:00
780569c08a Merge remote-tracking branch 'origin/main' into bxcan_timestamp 2023-07-31 10:29:20 +03:00
ffa0c08140 stm32/dma: fix condition check 2023-07-30 20:22:14 -05:00
c38c85ef1f stm32/dma: add traces 2023-07-30 19:39:17 -05:00
6c6bd11c1a Merge pull request #1606 from JcBernack/rng-update
STM32: RNG update
2023-07-30 23:44:11 +00:00
4999b045df stm32/rng: use bind_interrupts!. 2023-07-31 01:41:12 +02:00
105aa8f452 Merge pull request #1718 from copterust/stm32-spi-set-freq-in-config
Move frequency to SPI config
2023-07-30 22:05:22 +00:00
d8f02e151b Set frequency in stm32 SPI examples 2023-07-31 00:02:50 +02:00
3aef5999d5 Merge pull request #1716 from xoviat/rcc-p
stm32/rcc: extract and combine ahb/apb prescalers
2023-07-30 20:43:54 +00:00
3c3a1d89b5 Merge pull request #1721 from MabezDev/rtc-f2-dont-reset-bd
stm32f2: Avoid resetting RTC backup domain
2023-07-30 20:26:27 +00:00
e8d3e86591 stm32f2: Avoid resetting rtc backup domain
Also ensure the pwr is enabled before trying to initialize. For the F2
series this is in a seperate clock control register.
2023-07-30 21:22:48 +01:00
d6c5c1772c improve RNG polling 2023-07-30 22:19:34 +02:00
b65406791a add RNG conditioning 2023-07-30 22:16:42 +02:00
f3237d7a2c Merge pull request #1720 from mvniekerk/main
Uart pio fix zeros
2023-07-30 20:15:12 +00:00
56b21ad429 Uart pio fix zeros
Prevent UART from only getting 0s from the output
2023-07-30 22:13:27 +02:00
538cf2bc24 stm32/dma: fix condition check 2023-07-30 14:02:41 -05:00
d8420ed5a0 Remove unused imports 2023-07-30 19:34:27 +02:00
04ed45941a Fix format in stm32 SPI examples 2023-07-30 19:31:22 +02:00
55fb1d5126 Fix more stm32 SPI examples 2023-07-30 19:26:24 +02:00
4f791799a9 Fix formatting 2023-07-30 19:12:49 +02:00
d2127f6b82 Fix stm32 SPI examples 2023-07-30 18:58:40 +02:00
1d815f4ba0 Fix typo 2023-07-30 18:20:36 +02:00
aef93246b4 Fix Spi::new_internal call in i2s 2023-07-30 18:11:39 +02:00
6b1d802caa Move frequency to SPI config 2023-07-30 18:01:34 +02:00
6f30e92c7a stm32/dma: don't write to full ringbuf 2023-07-30 10:57:17 -05:00
39c1cc9f00 Merge pull request #1717 from OueslatiGhaith/wpan
wpan: fix examples
2023-07-30 15:53:28 +00:00
73057ee241 wpan: fix examples 2023-07-30 16:46:33 +01:00
a8a491212b stm32/rcc: cleanup merge 2023-07-30 10:18:54 -05:00
2f18770e27 stm32/rcc: extract and combine ahb/apb prescalers 2023-07-30 09:52:30 -05:00
087e649bc2 stm32/dma: fix typos 2023-07-30 09:28:02 -05:00
fd9b6487e1 stm32/dma: impl. wringbuf for bdma 2023-07-30 09:25:58 -05:00
603c4cb4fa stm32/dma: complete initial ringbuf impl. 2023-07-30 09:18:33 -05:00
8bed573b88 Merge pull request #1713 from MabezDev/stm32f2-pll-overflow
stm32f2 PLL overflow with crystal
2023-07-30 12:16:52 +00:00
2a004251a7 Merge pull request #1712 from xoviat/pwm
stm32/pwm: add output type control
2023-07-30 12:15:47 +00:00
8064f4bfe0 stm32/dma: add draft writable dma buf 2023-07-29 20:10:29 -05:00
6256a6c57c fix comments 2023-07-29 19:27:16 -05:00
bae31ebce7 stm32/dma: rename ringbuf 2023-07-29 19:25:18 -05:00
e0ce7fcde7 stm32f2 pll overflow with crystal
With a large enough HSE input frequency, the vco clock calculation will
overflow a u32. Therefore, in this specific case we have to use the
inner value and cast to u64 to ensure the mul isn't clipped before
applying the divider.
2023-07-30 01:00:53 +01:00
a9f6e30bcd rustfmt 2023-07-29 12:03:01 -05:00
0d7b005252 stm32/pwm: add output type control 2023-07-29 12:01:32 -05:00
fcbfd224a7 Merge pull request #1706 from mattico/timeouti2c-lifetime
TimeoutI2c: allow ref to live shorter than peripheral
2023-07-28 23:08:06 +00:00
eb097b9d03 Merge pull request #1710 from Sizurka/rp-async-flash
rp: add async flash
2023-07-28 22:56:33 +00:00
7ed9e29326 rp: add async flash
Implement an async flash mode using the XIP background best effort
read interface.  Only reads are actually async, write and erase remain
blocking.
2023-07-28 16:50:54 -06:00
bdc4aa4a3b Merge pull request #1582 from xoviat/hrtim
Add the high resolution timer
2023-07-28 22:44:03 +00:00
5bb5654d84 stm32/hrtim: pub instance 2023-07-28 17:39:01 -05:00
a8d3bcbb75 stm32/hrtim: shorten names 2023-07-28 17:37:14 -05:00
ec787d3518 stm32/hrtim: cleanup merge issues 2023-07-28 17:27:15 -05:00
c7c701b3e3 Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim 2023-07-28 17:18:22 -05:00
e495d606ec stm32/hrtim: extract traits 2023-07-28 17:16:46 -05:00
28136579e9 stm32/hrtim: extract into mod 2023-07-28 17:07:08 -05:00
cc414e63d3 Merge pull request #1709 from brandonros/cyw43-firmware-sync
sync latest cyw43-firmware
2023-07-28 21:59:53 +00:00
fd47445d75 cyw43: Update firmware in HIL test. 2023-07-28 23:58:47 +02:00
d39404cdda fix flaky test wifi_esp_hosted_perf 2023-07-28 23:49:37 +02:00
29acc46501 core::fmt devours your RAM and flash and explodes your stack. (#1708) 2023-07-28 23:47:07 +02:00
cffb819e61 changelog 2023-07-28 17:34:07 -04:00
b344c843c4 sync latest cyw43-firmware 2023-07-28 17:25:07 -04:00
e3cc0d168c Merge pull request #1707 from pennae/rp-pio-load
rp: relocate programs implicitly during load
2023-07-28 17:47:34 +00:00
cbc8871a0b rp: relocate programs implicitly during load
this removed the RelocatedProgram construction step from pio uses.
there's not all that much to be said for the extra step because the
origin can be set on the input program itself, and the remaining
information exposed by RelocatedProgram can be exposed from
LoadedProgram instead (even though it's already available on the pio_asm
programs, albeit perhaps less convenient). we do lose access to the
relocated instruction iterator, but we also cannot think of anything
this iterator would actually be useful for outside of program loading.
2023-07-28 19:33:02 +02:00
e97b14c068 Merge pull request #1705 from JuliDi/stm32h7-dac-dma-example
[STM32] H7 DAC DMA example and feature documentation
2023-07-28 17:07:05 +00:00
5a8704b4d8 TimeoutI2c: allow ref to live shorter than peripheral 2023-07-28 11:16:43 -05:00
6dd2fc5941 add document-features 2023-07-28 16:59:13 +02:00
69c0a89aa5 Use HardwareAddress in Driver 2023-07-28 16:40:15 +02:00
937a63ce28 remove memory.x files for other stm32 examples 2023-07-28 16:38:02 +02:00
b57ba84da5 add dac-dma example for h7, remove memory.x 2023-07-28 16:34:20 +02:00
c3ba08ffb6 Add IEEE802.15.4 address to embassy net Stack 2023-07-28 16:22:03 +02:00
c52d1d11f9 Expose IEEE802.15.4 address in Driver 2023-07-28 15:55:10 +02:00
d752a3f980 Merge pull request #1702 from rubdos/ieee802154-fixes
Allow ethernet and 802.15.4 to coexist
2023-07-28 13:47:49 +00:00
973b152375 CI: ethernet and ieee802.15.4 should be able to co-exist 2023-07-28 15:41:45 +02:00
3690af9bea stm32/timer: merge pwm module into timer. (#1703)
The traits there are applicable to timer use cases other than PWM.
It doesn't make sense to keep them separated.
2023-07-28 15:29:27 +02:00
f81ee103bf Allow ethernet and 802.15.4 to coexist
Co-authored-by: Thibaut Vandervelden <thvdveld@vub.be>
2023-07-28 15:11:24 +02:00
b124222649 Merge pull request #1699 from mvniekerk/main
RP2040: PIO UART example
2023-07-28 11:58:07 +00:00
8d8c642845 Merge pull request #1701 from chemicstry/bxcan_methods2
stm32/can: implement more convenience methods
2023-07-28 11:44:30 +00:00
d5f9d17b7c Make pipes local vars. 2023-07-28 13:38:26 +02:00
036e6ae30c Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700) 2023-07-28 13:23:22 +02:00
38b5d1ee2b stm32/can: implement more convenience methods 2023-07-28 14:22:24 +03:00
146c744223 Fixes as per PR 2023-07-28 12:56:31 +02:00
0ced8400d0 Merge pull request #1674 from pennae/rp-stack-guards
Rp stack guards
2023-07-28 10:43:54 +00:00
2e4f89068a Merge pull request #1686 from xoviat/usart
stm32/usart: fix error msg for lptim
2023-07-28 10:42:32 +00:00
f9dd751b6b Merge pull request #1697 from JuliDi/dac-adc-hil-test
[STM32] Add DAC HIL test with ADC
2023-07-28 10:41:27 +00:00
6b6acc256d Merge remote-tracking branch 'origin/main' 2023-07-28 11:57:50 +02:00
91338adc15 Don't include embedded-hal-common 2023-07-28 11:56:59 +02:00
1d4e1092c4 Merge branch 'embassy-rs:main' into main 2023-07-28 11:38:45 +02:00
0f1ff77fcc Comments 2023-07-28 11:38:08 +02:00
e947aa0153 Comments 2023-07-28 11:37:38 +02:00
44c8db2911 Merge pull request #1681 from alexferro/feature/stm32-dma-read-exact
Add a STM32/DMARingBuffer::read_exact helper
2023-07-28 01:16:48 +00:00
93864610ce Add DAC HIL test with ADC 2023-07-27 19:04:43 +02:00
bbd2563e13 Merge pull request #1696 from OueslatiGhaith/hci
wpan: update stm32wb-hci to version 0.1.4
2023-07-27 15:08:53 +00:00
a6543cef16 wpan: update stm32wb-hci 2023-07-27 15:00:01 +01:00
2815540167 Merge pull request #1694 from ceekdee/main
Use lora-phy v1.2.1; modify embassy-lora dependencies
2023-07-27 13:55:37 +00:00
8f1ea85938 Merge branch 'main' into main 2023-07-27 08:50:53 -05:00
3ee3f0e21c Merge pull request #1693 from esden/ex-enable-release-debug
Added debug=2 in release profile to all examples.
2023-07-27 10:56:42 +00:00
13acca624f Correct embassy-lora time feature 2023-07-26 22:23:02 -05:00
c54ae73d49 Use lora-phy v1.2.1; modify embassy-lora dependencies. 2023-07-26 21:51:09 -05:00
858ddf6777 Added debug=2 in release profile to all examples.
This makes rtt output work right when using `cargo run` in release mode.

Debug was already enabled for release builds in some of the examples but
not all.
2023-07-26 18:32:40 -07:00
a5f2152077 rp: Fix ROM cache ptr() returning the trampoline
Make sure that the ptr() function for ROM functions always returns
the actual ROM pointer.  This allows the use of flash I/O while the
function cache is enabled.
2023-07-26 06:50:43 -06:00
a56ef685f3 stm32: forward defmt feature to embassy-time 2023-07-25 12:19:42 +03:00
62ab0bf2e7 stm32/can: implement proper RX timestamps 2023-07-25 12:12:45 +03:00
77e34c5e8a Merge pull request #1684 from xoviat/wpan
stm32/rcc: move rcc logic from ipcc
2023-07-25 01:22:07 +00:00
270d1d59a0 stm32/rcc: use wpan default only for wpan 2023-07-24 18:25:15 -05:00
3c41784de8 stm32/usart: fix error msg for lptim 2023-07-24 18:08:23 -05:00
1425dda0a7 stm32/rcc: fix minor issues 2023-07-24 17:19:45 -05:00
a60d92cfbb Tx and Rx setup 2023-07-24 22:20:00 +02:00
9f898c460f Merge pull request #1685 from chemicstry/bxcan_async_enable
stm32/can: bxcan async enable
2023-07-24 15:24:18 +00:00
2a0fe73045 stm32/can: bxcan async enable 2023-07-24 17:53:23 +03:00
8d50f8a3d3 Merge pull request #1678 from JuliDi/dac-v3
Add DAC v3
2023-07-24 14:42:15 +00:00
622fcb0e10 Merge branch 'embassy-rs:main' into dac-v3 2023-07-24 16:40:05 +02:00
7fc138c91e Merge pull request #1676 from adamgreig/fix-dac-example
stm32: Fix DAC examples
2023-07-24 14:18:37 +00:00
5b32db7564 Merge branch 'embassy-rs:main' into dac-v3 2023-07-24 12:51:18 +02:00
bd60f003e0 stm32/rcc: move rcc logic from ipcc 2023-07-23 17:01:34 -05:00
4883fdd154 Add a STM32/DMARingBuffer::read_exact helper
This provides a helper function with an async implementation, that
will only return (or error) when it was able to read that many bytes,
sleeping until ready.

Additionally, corrected the documentation for Ringbuffer functions to use
"elements" instead of "bytes" as the types were already generic over the
word/element size.
2023-07-22 17:17:01 -06:00
18b9b6c780 Merge pull request #1679 from adamgreig/examples-chip-name
Add notes about setting chip name correctly for examples
2023-07-22 22:07:29 +00:00
fbcc587eca update readme 2023-07-22 17:06:04 -05:00
fbe30b2453 Add notes about setting chip name correctly for examples. 2023-07-22 21:58:29 +01:00
d42dff45de Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim 2023-07-22 14:49:31 -05:00
8e230bf6ec add missing TransferOptions fields for DMA 2023-07-22 21:36:03 +02:00
603ea9f310 Merge pull request #1677 from xoviat/update-metapac
stm32: update metapac
2023-07-22 18:35:09 +00:00
a56b3e9a44 update feature gates for v3 2023-07-22 19:47:36 +02:00
0378366e29 Merge remote-tracking branch 'xoviat/update-metapac' into fix-dac-example 2023-07-22 19:26:20 +02:00
80ce6d1fb7 update DAC triggers to incorporate v3 2023-07-22 19:25:02 +02:00
ba8e5d8589 rustfmt 2023-07-22 12:10:50 -05:00
192cdc2f85 stm32: suppress adc f3 2023-07-22 12:07:02 -05:00
64f8a779ca stm32: add dac fix 2023-07-22 11:54:54 -05:00
5693ed1178 stm32: add minimal fdcan impl 2023-07-22 11:50:30 -05:00
19c6c698b5 stm32: update metapac 2023-07-22 11:43:14 -05:00
224fbc8125 stm32: remove duplicate features from stm32f4 examples Cargo.toml 2023-07-22 13:19:29 +01:00
e5b4641f9e stm32/dac: set pin mode to analog (ref #334) 2023-07-22 13:19:26 +01:00
c83552eadc stm32: fix DAC examples
The DAC driver defaults to enabling the channel trigger, but leaves it
at the default value of TIM6 TRGO, then performs a software trigger
after writing the new output value. We could change the trigger
selection to software trigger, but for this example it's simpler to just
disable the trigger.
2023-07-22 12:57:49 +01:00
4db63677f6 Merge pull request #1658 from xoviat/mac
implement most infra for wpan mac
2023-07-21 22:29:15 +00:00
f4d6a23f92 wpan/mac: misc fixes 2023-07-21 17:02:36 -05:00
2cdd593290 Merge branch 'main' of https://github.com/embassy-rs/embassy into mac 2023-07-21 16:24:48 -05:00
c675208b8a wpan: complete prelim. command impl. 2023-07-21 16:10:34 -05:00
e9445ec72d rp: allow for MPU-based stack guards on core 0 as well
using these will require some linker script intervention. setting the
core0 stack needs linker intervention anyway (to provide _stack_start),
having it also provide _stack_end for the guard to use is not that much
of a stretch.
2023-07-21 18:51:35 +02:00
899a68325c wpan: impl. draft control scheme 2023-07-20 20:51:49 -05:00
c80c232a72 wpan: impl. debug for structs 2023-07-20 19:52:36 -05:00
83ff626c47 wpan/mac: incr. runner msdu handle 2023-07-20 17:00:03 -05:00
809d3476aa wpan: further optimize mac event 2023-07-20 16:45:04 -05:00
4d1d125f41 Merge pull request #1673 from pennae/rp-disable-adc
rp: disable adc hardware on Adc drop
2023-07-20 14:21:40 +00:00
4d6b3c57b1 rp: fix multicore stack guard setup
the region field of the register is four bits wide followed by the valid
bit that causes the rnr update we rely on for the rasr write. 0x08 is
just a bit short to reach the valid bit, and since rp2040 has only 8
regions it (at best) doesn't do anything at all.
2023-07-20 16:08:59 +02:00
a3d4ae85b0 rp: disable adc hardware on Adc drop
the adc constantly pulls a small but significant amount of current while
the hardware is enabled. this can have quite an effect on sleeping
devices that also use the adc.
2023-07-20 15:48:59 +02:00
02d57afd51 rustfmt 2023-07-19 17:52:07 -05:00
28254842db - optimize event to parse opcode only once
- optimze channels
- return mut ref for smoltcp rx
2023-07-19 17:49:08 -05:00
3382ca1a54 Merge pull request #1667 from quentinmit/nrf-pdm
nrf/pdm: Add continuous sampling API
2023-07-19 10:15:39 +00:00
07a9a4ffd8 Merge pull request #1671 from alepez/uart-swap
stm32/uart: add swap_rx_tx
2023-07-19 10:12:49 +00:00
36ff688fab stm32/uart: optimize swap_rx_tx 2023-07-19 10:50:40 +02:00
3df2c71e6c stm32/uart: add swap_rx_tx 2023-07-19 10:26:47 +02:00
ca1d4179a7 wpan: implement initial event loop 2023-07-18 20:52:03 -05:00
890d113b85 wpan: fully implement initial draft concept 2023-07-18 18:28:12 -05:00
7555a1e302 cargo fmt 2023-07-18 18:32:19 -04:00
be7fbe50d7 Update pdm driver to build with all the PACs 2023-07-18 18:31:32 -04:00
2c01f277c2 cargo fmt 2023-07-18 17:17:04 -04:00
c333d855fc Remove merge error 2023-07-18 17:14:25 -04:00
42de1c3a06 Merge remote-tracking branch 'origin/main' into nrf-pdm 2023-07-18 17:13:00 -04:00
a1d3bc30fa net-esp-hosted: build docs. 2023-07-18 18:15:35 +02:00
98576c17b6 Fix multicast support (#1670) 2023-07-18 17:35:20 +02:00
27a3d2cd0b Merge pull request #1669 from embassy-rs/nocrlf
ci: add check for no CRLF line endings.
2023-07-18 12:33:14 +00:00
10f5966787 Convert files to LF endings. 2023-07-18 14:24:58 +02:00
48957dce87 Add gitattributes to control crlf conversion. 2023-07-18 14:23:37 +02:00
fc901f9856 ci: add check for no CRLF line endings. 2023-07-18 14:23:37 +02:00
13964c7fca Merge pull request #1668 from bjoernQ/fix-embassy-net-dual-stack
Make dual-stack work in embassy-net
2023-07-18 11:30:27 +00:00
a1cc3f2c60 Merge pull request #1666 from alexferro/possible-1664-fix
Embassy-rp I2C: Fix 1664 (async transaction failure)
2023-07-18 11:24:52 +00:00
15a6e04887 Merge pull request #1665 from xoviat/eth
stm32/eth: fix cfg(not(time))
2023-07-18 11:22:42 +00:00
6bf4717b0a cfg-gate unapply_config_v4 2023-07-18 10:57:05 +02:00
f581831b86 Make dual-stack work in embassy-net 2023-07-18 10:39:29 +02:00
6f02403184 Merge remote-tracking branch 'origin/main' into nrf-pdm 2023-07-17 21:31:43 -04:00
d040871f7a wpan: fix comp errors and impl. some of runner 2023-07-17 20:14:06 -05:00
8f23b6faa6 wpan: refactor control, driver 2023-07-17 19:26:58 -05:00
1d2c47273d Merge branch 'master' into mac 2023-07-17 16:38:46 -05:00
55ac480cb0 stm32/eth: fix cfg(not(time)) 2023-07-17 16:24:09 -05:00
e4ad1aa542 Embassy-rp I2C: Fix 1664
Change embassy-rp i2c.rs impl of embedded_hal_async::i2c::I2c::transaction
to only do the call to setup() for address once per call to transactions.
Calling setup multiple times results in I2C transactions being skipped
on the bus, even across calls to transaction() or devices.
2023-07-16 19:59:35 -06:00
6b5df4523a Merge pull request #1662 from xoviat/mac-2
wpan/mac: use slice view to avoid copy
2023-07-17 00:01:27 +00:00
7b34f5e866 wpan: make dataind fields private 2023-07-16 18:54:11 -05:00
fe1e7c4d76 wpan: fix datarequest 2023-07-16 18:07:05 -05:00
34217ea797 wpan: add slice data view 2023-07-16 17:28:34 -05:00
a0515ca7ac wpan: add repr(c) to mac responses 2023-07-16 16:16:56 -05:00
28b419d65e wpan/mac: use lifetimes to control events 2023-07-16 15:09:30 -05:00
7c465465c1 wpan: use builtin conversion methods 2023-07-16 13:59:15 -05:00
e95a7dc555 wpan/mac: use slice view to avoid copy 2023-07-16 12:41:57 -05:00
582006c75c wpan/mac: further cleanup 2023-07-16 09:32:54 -05:00
c7ec45a004 Merge pull request #1659 from maximedeboeck/hid-tests
Add usb-hid keyboard example for rp pico.
2023-07-16 11:17:49 +00:00
88d1976e81 Added usb-hid keyboard example for rp pico. 2023-07-16 12:31:56 +02:00
cd592cb055 wpan: add files from cyw43 2023-07-15 19:15:01 -05:00
0b63af3313 wpan: prepare net impl. 2023-07-15 19:02:04 -05:00
25197308e3 Merge pull request #1653 from xoviat/eth
stm32/eth: solve busy-loop polling
2023-07-15 21:18:03 +00:00
cf278ea1b6 Merge pull request #1656 from xoviat/mac-4
wpan: factor mac logic into other mod
2023-07-15 19:49:16 +00:00
4db4200c37 wpan: factor mac logic into other mod 2023-07-15 14:47:34 -05:00
758862f4b1 Merge pull request #1655 from xoviat/mac-3
wpan: add mac
2023-07-15 19:39:06 +00:00
3705b4f40d rustfmt 2023-07-15 14:38:02 -05:00
1f63fdbb15 stm32/tests: fix cargo 2023-07-15 14:31:35 -05:00
d11a94e2a7 wpan: add mac test 2023-07-15 14:28:42 -05:00
d6dd5ea5d3 revert toolchain changes 2023-07-15 14:19:32 -05:00
8a146a50ec Merge branch 'master' into mac-3 2023-07-15 14:18:01 -05:00
17d5e1c470 stm32/eth: add set_poll_interval 2023-07-15 12:02:08 -05:00
975a780efe stm32/eth: impl. poll interval 2023-07-15 09:57:09 -05:00
c3774607a5 stm32/eth: convert static metho 2023-07-15 09:37:25 -05:00
bb24cfd1e8 stm32/eth: add f4 example 2023-07-15 09:32:44 -05:00
48b37aa2bf stm32/eth: refactor genericsmi 2023-07-15 09:32:36 -05:00
0bde4992ea Merge pull request #1652 from OueslatiGhaith/wpan_hci
wpan: update `stm32wb-hci`
2023-07-15 14:00:18 +00:00
7ec7d1bbcc fix ci issue 2023-07-15 14:56:26 +01:00
0628dd997f fix test 2023-07-15 13:52:04 +01:00
283ec756a9 stm32wb: add gatt server example 2023-07-15 13:37:41 +01:00
5b076cb0dd wpan: update stm32wb-hci to version 0.1.3 2023-07-15 13:33:10 +01:00
3bae533066 Enable RTC on STM32WL chips (#1645)
* Add clippy allow to not report if same then branch

* Support enabling RTC clock on STM32WL

* Add clippy allow to not report if same then branch

* Support enabling RTC clock on STM32WL

* Add rtc example for stm32wl

* Address code review feedback
2023-07-15 13:40:23 +02:00
7b36fe049d Merge pull request #1649 from simmsb/master
rp: Check intrstatus before signalling suspended
2023-07-14 18:52:53 +00:00
4b3fda4f96 Merge pull request #1650 from henrikberg/rp_examples_doc
RP examples gets file description
2023-07-14 18:45:34 +00:00
56ca179475 Round temp to make more sense. 2023-07-13 22:47:03 +02:00
460cdc9e0f Check intrstatus before signalling suspended 2023-07-13 19:29:09 +01:00
f90b170dad cleanup 2023-07-13 16:29:29 +01:00
68792bb918 final structs
unchecked
2023-07-13 15:48:15 +01:00
3f0c8bafb0 make it work, disgustingly 2023-07-13 15:20:50 +01:00
588c0479f5 Add descriptions to all RP2040 examples. Some need hardware that was not specified. 2023-07-13 11:16:11 +02:00
d979841f17 Merge pull request #1644 from embassy-rs/remove-atompoly
Remove trivial to remove uses of atomic-polyfill.
2023-07-12 16:35:00 +00:00
dff9bd9711 Remove trivial to remove uses of atomic-polyfill. 2023-07-12 18:30:43 +02:00
eccd2ecebf change MacAddress to a union instead of an enum 2023-07-12 16:49:37 +01:00
ed86fc175f Merge pull request #1637 from ShakenCodes/main
Ensure I2C master_stop() called after error
2023-07-12 15:46:28 +00:00
132327a40d Merge pull request #1642 from henrikberg/rtc_rp_hb
RP2040 Rtc update with example
2023-07-12 15:46:00 +00:00
a615a70eda Merge pull request #1643 from bugadani/clear_timer
Reset `expires_at` of finished task
2023-07-12 15:40:10 +00:00
a2501bd5c1 Allow clearing finished task from timer queue 2023-07-12 16:56:02 +02:00
ff2daaff67 RP: Watchdog scratch set/get with index: usize. 2023-07-12 16:41:35 +02:00
d5a4457b5e parsing MAC structs 2023-07-12 15:06:56 +01:00
6d402fe393 RP: Don't reset RTC in Clock::init. Updated example. 2023-07-12 15:16:56 +02:00
466a391b52 RP: Add save/restore to Rtc. Example use. 2023-07-12 14:22:48 +02:00
a93714327e RP: Rename Rtc to match STM32 impl. Remove setting RTC in new(). 2023-07-12 14:22:48 +02:00
029b156563 RP: Add scratchN registers to watchdog. Add Clone and Debug to DateTime 2023-07-12 14:22:48 +02:00
55a5e9b3a5 RP: Add RTC example to rp2040. 2023-07-12 14:22:48 +02:00
d8c7c3fc4b Merge pull request #1641 from royb3/poll_udp_socket
Adding polling functions for udp send_to and recv_from.
2023-07-12 10:46:08 +00:00
f192f44018 fmt 2023-07-12 11:32:02 +02:00
b81c14f442 Add polling fn's for send_to and recv_from in UdpSocket. 2023-07-12 11:32:02 +02:00
f54e1cea90 Add poll functions on UdpSocket. 2023-07-12 11:32:02 +02:00
fbddfcbfb7 wip: added MAC indications 2023-07-11 17:19:32 +01:00
67b14e6e7a wip: added MAC responses 2023-07-11 16:54:48 +01:00
6f4172fbc1 wip: added MAC commands 2023-07-11 16:07:33 +01:00
c6e2f4a90b Merge pull request #1639 from embassy-rs/rp-gpio-set-low-fix
rp/gpio: fix is_set_high/is_set_low, expand tests.
2023-07-11 13:16:27 +02:00
91c1d17f16 rp/gpio: fix is_set_high/is_set_low, expand tests. 2023-07-11 12:40:07 +02:00
29f3d5b68d Ensure I2C master_stop() called after error 2023-07-10 16:40:33 -07:00
4aca7c8811 wip 2023-07-10 16:54:48 +01:00
8a811cfcf7 Merge pull request #1632 from xoviat/wpan
wpan: get mac working
2023-07-09 22:18:16 +00:00
bf4493dbdf rustfmt 2023-07-09 16:08:39 -05:00
c1bf5aee24 mac: move table initialization after sys ready 2023-07-09 16:01:13 -05:00
735d676a72 wpan: update alignment control 2023-07-09 15:50:01 -05:00
37c103b5f3 Merge pull request #1630 from jamwaffles/fix-embassy-time-std
Enable `critical-section/std` when using `std` feature of `embassy-time`
2023-07-08 16:25:58 +00:00
05c524a7db Enable critical-section/std when using std feature of embassy-time 2023-07-08 11:20:26 +01:00
758a2c528f Merge pull request #1629 from davidpurser/stm32h7-pll-fix
Correctly calculate target VCO frequency from multipliers
2023-07-08 02:42:52 +00:00
69b4e898b3 Correctly calculate target VCO frequency from multipliers 2023-07-07 20:52:44 -05:00
b0da6318f3 Merge pull request #1623 from pennae/rp-adc
rp/adc: rewrite the module
2023-07-07 15:52:48 +00:00
972cdd4265 rp/adc: rewrite the module
- don't require an irq binding for blocking-only adc
- abstract adc pins into an AnyPin like interface, erasing the actual
  peripheral type at runtime.
- add pull-up/pull-down functions for adc pins
- add a test (mostly a copy of the example, to be honest)
- configure adc pads according to datasheet
- report conversion errors (although they seem exceedingly rare?)
- drop embedded-hal interfaces. embedded-hal channels can do neither
  AnyPin nor pullup/pulldown without encoding both into the type
2023-07-07 17:46:35 +02:00
f9aebfce01 Merge pull request #1628 from royb3/reexport_ipendpoint
Re-export smoltcp::wire::IpEndpoint
2023-07-07 14:51:27 +00:00
151557fec3 Re-export smoltcp::wire::IpEndpoint 2023-07-07 16:38:56 +02:00
7d68ca1f3b Merge pull request #1627 from pennae/rp-pio-irq
rp/pio: use bind_interrupts for irqs
2023-07-07 14:31:09 +00:00
4b63829110 rp/pio: use bind_interrupts for irqs
closes #1338
2023-07-07 16:27:10 +02:00
e196387e69 Merge pull request #1626 from embassy-rs/otg-read-disable
stm32/otg: implement `EndpointError::Disabled` for reads.
2023-07-07 01:58:39 +00:00
f8d608093f stm32/otg: implement EndpointError::Disabled for reads.
It was implemented only for writes.
2023-07-07 03:55:57 +02:00
ffeb40ff43 stm32/otg: change some info logs to trace. 2023-07-06 13:49:19 +02:00
47305c2bf2 ci: build doc with 4 threads instead of 6, to avoid running out of disk space. 2023-07-06 02:32:49 +02:00
c421b7f5f0 Merge pull request #1624 from embassy-rs/release-embassy-time-v0.1.2
Release embassy-time v0.1.2
2023-07-05 23:34:04 +00:00
d137286981 Release embassy-time v0.1.2 2023-07-06 01:29:44 +02:00
864202a23a Merge pull request #1578 from schphil/can-split
stm32 can split method
2023-07-05 23:27:53 +00:00
a77fb0f630 Merge pull request #1622 from embassy-rs/misc-fixes
Downgrade nightly, misc fixes
2023-07-05 17:44:46 +00:00
a42ac86f1b Remove wifi envvars. They're annoying, they cause rust-analyzer errors when opening the examples. 2023-07-05 19:20:49 +02:00
c6cd69887c Downgrade nightly.
Newer nightlies have a bad perf regression https://github.com/rust-lang/rust/issues/113372
2023-07-05 19:14:11 +02:00
d1711036db stm32-wpan: fix wrong src_base 2023-07-05 19:13:46 +02:00
8313b7315a Merge pull request #1600 from ilikepi63/main
feature(1354): Added lifetimes to Event + Tasks
2023-07-05 17:13:26 +00:00
d7ecf6f593 Update embassy-nrf/src/ppi/mod.rs
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2023-07-05 19:10:43 +02:00
082147939d Update embassy-nrf/src/ppi/ppi.rs
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2023-07-05 19:10:30 +02:00
67c4d165c7 Update embassy-nrf/src/ppi/ppi.rs
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2023-07-05 19:10:22 +02:00
fb3e6a2b40 Update embassy-nrf/src/ppi/mod.rs
Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
2023-07-05 19:10:16 +02:00
8ee2f50b8c Removed unnecessary lifetime naming 2023-07-05 19:01:28 +02:00
46a4600952 Merge pull request #1607 from MathiasKoch/embassy-stm32/rcc-rtc-l4
feature(embassy-stm32): L4 enable APB to allow RTC to work
2023-07-05 14:17:35 +00:00
27992d9c07 Merge pull request #1621 from rasmuspeders1/main
Remove bad semicolon in time driver example struct declaration
2023-07-05 15:45:27 +02:00
a0dc87d64e Remove semicolon in time driver example struct declaration
The semicolon is not allowed with struct declarations with braces.
The doc test compiles fine for some reason!?
2023-07-05 14:07:05 +02:00
1255d8a8ce Merge branch 'main' of https://github.com/embassy-rs/embassy into embassy-stm32/rcc-rtc-l4 2023-07-05 12:36:42 +02:00
7d3eb6463a Removed unnecessary space 2023-07-05 11:34:33 +02:00
ab7fcf1d5b Removed unnecessary changes 2023-07-05 09:23:39 +02:00
2c5146f19f Fixed Lifetimes in Events & Tasks 2023-07-05 09:20:56 +02:00
3341b53eb4 Merge pull request #1619 from embassy-rs/nrf-time
nrf: build docs with `time` feature.
2023-07-04 22:40:52 +00:00
70c05c62e4 nrf: build docs with time feature. 2023-07-05 00:35:22 +02:00
eb57bb298f Merge pull request #1617 from xoviat/const-rcc
stm32/rcc: allow const-propagation
2023-07-04 22:31:55 +00:00
953c745ed8 stm32/rcc: allow const-propagation 2023-07-04 16:29:46 -05:00
ce73c29246 Merge pull request #1616 from embassy-rs/update-nightly
Update nightly
2023-07-04 19:44:55 +00:00
9c4df46c46 rustfmt. 2023-07-04 21:34:55 +02:00
2a035a24a6 Update nightly. 2023-07-04 20:15:30 +02:00
10c0174903 doc: upload statics too. 2023-07-04 20:15:30 +02:00
d26a247a32 Merge pull request #1615 from embassy-rs/update-eh2
update embedded-hal crates.
2023-07-04 18:03:05 +00:00
a101d9078d update embedded-hal crates. 2023-07-04 19:59:36 +02:00
b2f843a4ce Merge pull request #1614 from cumthugo/fix_queue_size
time: fix queue size
2023-07-04 13:17:14 +00:00
40d25da793 time: fix queue size 2023-07-04 21:13:31 +08:00
b956d5d06c Merge pull request #1613 from diondokter/fix-nrf-spim-nightly
Fix nrf spim nightly error
2023-07-04 10:55:15 +00:00
0845eab8a0 Merge pull request #1612 from wyager/fmc-bank1-16bit
Add stm32 FMC support for bank 1 16-bit RAM
2023-07-04 10:45:17 +00:00
582c721aec Add lifetimes to the functions 2023-07-04 11:48:59 +02:00
0c4180cdd0 fmt 2023-07-03 19:39:58 -04:00
d9824dfd64 Add bank 1 16 bit 2023-07-03 19:39:51 -04:00
a088c4bee6 fix stm32 can test 2023-07-04 00:39:10 +02:00
8359d8c020 make stm32 can test work 2023-07-04 00:34:24 +02:00
1869fe02ba make stm32f4 example work 2023-07-04 00:21:08 +02:00
e3e8d82933 remove unused imports from example 2023-07-03 23:52:52 +02:00
a96f30edf4 allow deed code can rx & clippy 2023-07-03 23:48:07 +02:00
af15b49bfe fmt 2023-07-03 22:57:33 +02:00
60b2f075dc Merge branch 'main' of https://github.com/embassy-rs/embassy into embassy-stm32/rcc-rtc-l4 2023-07-03 19:33:26 +02:00
0c49e6747c wip 2023-07-02 22:00:50 -05:00
99b4ea7c1d Merge pull request #1609 from embassy-rs/fix-peripheral-deref
hal-common: require DerefMut for peripherals, not just Deref.
2023-07-02 20:20:47 +00:00
c9b9be5b81 hal-common: require DerefMut for peripherals, not just Deref.
Otherwise you can create multiple drivers on the same singleton like this:

```rust
let mut input = Input::new(&pin, Pull::None);
let mut output = Output::new(&pin, Level::Low, Speed::Low);
input.is_high();
output.set_high();
input.is_high();
output.set_high();
```

Thanks @pennae for reporting.
2023-07-02 22:16:01 +02:00
2e6b813225 hrtim: add guardrails on bridge sec. duty 2023-07-02 09:17:12 -05:00
aceba1c03f hrtim: fix example and auto adjust psc. 2023-07-01 21:47:44 -05:00
8141d53d94 Merge branch 'main' of https://github.com/embassy-rs/embassy into hrtim 2023-07-01 17:32:25 -05:00
21a8653195 hrtim: minor cleanup 2023-07-01 17:32:16 -05:00
d372df7ddb L4: Switch to MSI to prevent problems with PLL configuration, and enable power to AHB bus clock to allow RTC to run 2023-07-01 12:16:23 +02:00
6e13f5b387 rustfmt 2023-06-30 18:33:22 -05:00
c07854fed8 stm32/hrtim: minor fixes 2023-06-30 18:22:02 -05:00
8c4997c5fc stm32/hrtim: impl. bridge, dead-time part. res. 2023-06-30 18:22:01 -05:00
3252eaa060 stm32/hrtim: add example impl. 2023-06-30 18:21:59 -05:00
348019e37f stm32/hrtim: impl channel alloc type system 2023-06-30 18:21:58 -05:00
b9eb3dfad7 stm32/hrtim: add api concept 2023-06-30 18:21:57 -05:00
71513ccb39 stm32/hrtim: impl. draft frequency computation 2023-06-30 18:21:57 -05:00
cdb3fb059f stm32/hrtim: first draft 2023-06-30 18:21:42 -05:00
93caf97a04 Formatting stuff 2023-06-30 11:54:37 +02:00
bca2c54948 Adjusted build issue 2023-06-30 11:50:27 +02:00
81cbb0fc32 Attempt to fix certain borrowing rule issues 2023-06-30 11:47:20 +02:00
c69f2929c0 Build failures 2023-06-30 11:37:53 +02:00
4d23ea554b Build failures 2023-06-30 11:34:13 +02:00
d6fde756a8 Build failures 2023-06-30 11:32:11 +02:00
2432cece38 Lifetimes in dppi 2023-06-29 18:36:12 +02:00
fef338f5c2 Lifetime groups 2023-06-29 18:13:46 +02:00
24e186e684 feature(1354): Added lifetimes to Event + 2023-06-29 18:09:26 +02:00
71afa40a69 Merge branch 'embassy-rs:main' into can-split 2023-06-23 10:19:30 +02:00
89fbb02979 add as_mut 2023-06-22 17:49:33 +02:00
76a334bd7c add as_mut & set loopback true in example 2023-06-22 17:47:58 +02:00
f47a148f51 add stm32f7 can example 2023-06-22 17:18:55 +02:00
5ecf9ec7bc split can 2023-06-22 17:17:51 +02:00
d896f80405 util -> sync rename 2022-08-23 23:02:48 -04:00
2900ab79e7 Merge remote-tracking branch 'origin/master' into nrf-pdm 2022-08-23 23:01:51 -04:00
14eae9ca06 Optimize pdm_continuous example 2022-08-21 12:40:51 -04:00
64154fec8c Demonstrate FFT in example 2022-08-21 02:43:13 -04:00
ed97e61dbe PDM clock frequency control 2022-08-21 02:16:26 -04:00
029713eca0 Stop PDM sampling when future is dropped 2022-08-21 02:04:11 -04:00
3d26573c6b Discard the first N samples due to transients 2022-08-21 01:44:04 -04:00
0963b5f92c Add continuous PDM sampling with example 2022-08-20 17:58:54 -04:00
530f192acc Set gain at runtime 2022-08-20 17:08:29 -04:00
a46f33b214 Initial PDM driver 2022-08-20 16:37:51 -04:00
490 changed files with 19991 additions and 5663 deletions

41
.gitattributes vendored Normal file
View File

@ -0,0 +1,41 @@
* text=auto
*.adoc text
*.html text
*.in text
*.json text
*.md text
*.proto text
*.py text
*.rs text
*.service text
*.sh text
*.toml text
*.txt text
*.x text
*.yml text
*.raw binary
*.bin binary
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary
*.ez binary
*.bz2 binary
*.swp binary

17
.github/ci/crlf.sh vendored Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
## on push branch~=gh-readonly-queue/main/.*
## on pull_request
set -euo pipefail
FILES_WITH_CRLF=$(find ! -path "./.git/*" -not -type d | xargs file -N | (grep " CRLF " || true))
if [ -z "$FILES_WITH_CRLF" ]; then
echo -e "No files with CRLF endings found."
exit 0
else
NR_FILES=$(echo "$FILES_WITH_CRLF" | wc -l)
echo -e "ERROR: Found ${NR_FILES} files with CRLF endings."
echo "$FILES_WITH_CRLF"
exit "$NR_FILES"
fi

61
.github/ci/doc.sh vendored
View File

@ -6,7 +6,7 @@ set -euo pipefail
export RUSTUP_HOME=/ci/cache/rustup
export CARGO_HOME=/ci/cache/cargo
export CARGO_TARGET_DIR=/ci/cache/target
export BUILDER_THREADS=6
export BUILDER_THREADS=4
export BUILDER_COMPRESS=true
# force rustup to download the toolchain before starting building.
@ -15,30 +15,41 @@ export BUILDER_COMPRESS=true
# which makes rustup very sad
rustc --version > /dev/null
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/git.zup
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
docserver-builder -i ./embassy-boot/rp -o webroot/crates/embassy-boot-rp/git.zup
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
docserver-builder -i ./embassy-net-driver -o webroot/crates/embassy-net-driver/git.zup
docserver-builder -i ./embassy-net-driver-channel -o webroot/crates/embassy-net-driver-channel/git.zup
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
docserver-builder -i ./embassy-net-esp-hosted -o webroot/crates/embassy-net-esp-hosted/git.zup
docserver-builder -i ./embassy-stm32-wpan -o webroot/crates/embassy-stm32-wpan/git.zup --output-static webroot/static
docserver-builder -i ./embassy-net-adin1110 -o webroot/crates/embassy-net-adin1110/git.zup
export KUBECONFIG=/ci/secrets/kubeconfig.yml
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp crates $POD:/data
kubectl cp webroot/crates $POD:/data
kubectl cp webroot/static $POD:/data
# build and upload stm32 last
# so that it doesn't prevent other crates from getting docs updates when it breaks.
rm -rf webroot
docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
kubectl cp webroot/crates $POD:/data

4
.github/ci/test.sh vendored
View File

@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json
cargo test --manifest-path ./embassy-sync/Cargo.toml
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
cargo test --manifest-path ./embassy-hal-common/Cargo.toml
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
@ -28,3 +28,5 @@ cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --featu
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f732ze,exti,time-driver-any,exti
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f769ni,exti,time-driver-any,exti
cargo test --manifest-path ./embassy-net-adin1110/Cargo.toml

18
.vscode/settings.json vendored
View File

@ -6,16 +6,21 @@
"rust-analyzer.check.allTargets": false,
"rust-analyzer.check.noDefaultFeatures": true,
"rust-analyzer.cargo.noDefaultFeatures": true,
"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
"rust-analyzer.showUnlinkedFileNotification": false,
// uncomment the target of your chip.
//"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
//"rust-analyzer.cargo.target": "thumbv7m-none-eabi",
"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
//"rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf",
"rust-analyzer.cargo.features": [
///"nightly",
// Uncomment if the example has a "nightly" feature.
"nightly",
],
"rust-analyzer.linkedProjects": [
// Declare for the target you wish to develop
// "embassy-executor/Cargo.toml",
// "embassy-sync/Cargo.toml",
"examples/stm32wl/Cargo.toml",
// Uncomment ONE line for the chip you want to work on.
// This makes rust-analyzer work on the example crate and all its dependencies.
"examples/nrf52840/Cargo.toml",
// "examples/nrf52840-rtic/Cargo.toml",
// "examples/nrf5340/Cargo.toml",
// "examples/nrf-rtos-trace/Cargo.toml",
// "examples/rp/Cargo.toml",
@ -25,6 +30,7 @@
// "examples/stm32f1/Cargo.toml",
// "examples/stm32f2/Cargo.toml",
// "examples/stm32f3/Cargo.toml",
// "examples/stm32f334/Cargo.toml",
// "examples/stm32f4/Cargo.toml",
// "examples/stm32f7/Cargo.toml",
// "examples/stm32g0/Cargo.toml",

View File

@ -33,6 +33,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
- **Bluetooth** -
The <a href="https://github.com/embassy-rs/nrf-softdevice">nrf-softdevice</a> crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
The <a href="https://github.com/embassy-rs/embassy/tree/main/embassy-stm32-wpan">embassy-stm32-wpan</a> crate provides Bluetooth Low Energy 5.x support for stm32wb microcontrollers.
- **LoRa** -
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
@ -111,6 +112,12 @@ cargo install probe-rs --features cli
cd examples/nrf52840
```
- Ensure `Cargo.toml` sets the right feature for the name of the chip you are programming.
If this name is incorrect, the example may fail to run or immediately crash
after being programmed.
- Ensure `.cargo/config.toml` contains the name of the chip you are programming.
- Run the example
For example:
@ -119,6 +126,8 @@ For example:
cargo run --release --bin blinky
```
For more help getting started, see [Getting Started][1] and [Running the Examples][2].
## Developing Embassy with Rust Analyzer based editors
The [Rust Analyzer](https://rust-analyzer.github.io/) is used by [Visual Studio Code](https://code.visualstudio.com/)
@ -151,3 +160,5 @@ This work is licensed under either of
at your option.
[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started
[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples

23
ci.sh
View File

@ -3,11 +3,7 @@
set -euo pipefail
export RUSTFLAGS=-Dwarnings
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
# needed by wifi examples
export WIFI_NETWORK=x
export WIFI_PASSWORD=x
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
TARGET=$(rustc -vV | sed -n 's|host: ||p')
@ -24,20 +20,19 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,defmt-timestamp-uptime,tick-hz-32_768,generic-queue-8 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet,unstable-traits,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,nightly \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154,nightly \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
@ -57,6 +52,7 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,intrinsics \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \
@ -85,11 +81,13 @@ cargo batch \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
@ -119,6 +117,7 @@ cargo batch \
--- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \
--- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \
--- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \
--- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \
--- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \
--- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \

View File

@ -16,9 +16,7 @@ cargo batch \
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52805,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52810,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
@ -36,6 +34,7 @@ cargo batch \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,log \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi \
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features qspi-as-gpio \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g491re,defmt,exti,time-driver-any,unstable-traits \
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \

Binary file not shown.

Binary file not shown.

View File

@ -2,4 +2,8 @@
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt)
## Changelog
* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62

View File

@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43;
use embassy_rp::dma::Channel;
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
use fixed::FixedU32;
use pio_proc::pio_asm;
@ -88,8 +87,6 @@ where
".wrap"
);
let relocated = RelocatedProgram::new(&program.program);
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
pin_io.set_pull(Pull::None);
pin_io.set_schmitt(true);
@ -102,7 +99,8 @@ where
pin_clk.set_slew_rate(SlewRate::Fast);
let mut cfg = Config::default();
cfg.use_program(&common.load_program(&relocated), &[&pin_clk]);
let loaded_program = common.load_program(&program.program);
cfg.use_program(&loaded_program, &[&pin_clk]);
cfg.set_out_pins(&[&pin_io]);
cfg.set_in_pins(&[&pin_io]);
cfg.set_set_pins(&[&pin_io]);
@ -142,7 +140,7 @@ where
sm,
irq,
dma: dma.into_ref(),
wrap_target: relocated.wrap().target,
wrap_target: loaded_program.wrap.target,
}
}

View File

@ -11,7 +11,7 @@ log = ["dep:log"]
firmware-logs = []
[dependencies]
embassy-time = { version = "0.1.0", path = "../embassy-time"}
embassy-time = { version = "0.1.2", path = "../embassy-time"}
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
@ -24,7 +24,7 @@ cortex-m = "0.7.6"
cortex-m-rt = "0.7.0"
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.10" }
embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-rc.1" }
num_enum = { version = "0.5.7", default-features = false }
[package.metadata.embassy_docs]

View File

@ -30,7 +30,7 @@ TODO:
### Example 2: Create an access point (IP and credentials in the code)
- `cargo run --release --bin wifi_ap_tcp_server`
### Example 3: Connect to an existing network and create a server
- `WIFI_NETWORK=MyWifiNetwork WIFI_PASSWORD=MyWifiPassword cargo run --release --bin wifi_tcp_server`
- `cargo run --release --bin wifi_tcp_server`
After a few seconds, you should see that DHCP picks up an IP address like this
```

View File

@ -102,7 +102,7 @@ where
cmd_buf[0] = cmd;
cmd_buf[1..][..buf.len()].copy_from_slice(buf);
self.status = self.spi.cmd_write(&cmd_buf).await;
self.status = self.spi.cmd_write(&cmd_buf[..buf.len() + 1]).await;
}
#[allow(unused)]

View File

@ -216,7 +216,7 @@ where
PWR: OutputPin,
SPI: SpiBusCyw43,
{
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
let state_ch = ch_runner.state_runner();
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);

View File

@ -345,7 +345,9 @@ where
}
fn rx(&mut self, packet: &mut [u8]) {
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else { return };
let Some((sdpcm_header, payload)) = SdpcmHeader::parse(packet) else {
return;
};
self.update_credit(&sdpcm_header);
@ -353,7 +355,9 @@ where
match channel {
CHANNEL_TYPE_CONTROL => {
let Some((cdc_header, response)) = CdcHeader::parse(payload) else { return; };
let Some((cdc_header, response)) = CdcHeader::parse(payload) else {
return;
};
trace!(" {:?}", cdc_header);
if cdc_header.id == self.ioctl_id {
@ -417,8 +421,12 @@ where
let status = event_packet.msg.status;
let event_payload = match evt_type {
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
return;
};
let Some(bss_info) = BssInfo::parse(bss_info) else {
return;
};
events::Payload::BssInfo(*bss_info)
}
Event::ESCAN_RESULT => events::Payload::None,
@ -439,7 +447,9 @@ where
}
}
CHANNEL_TYPE_DATA => {
let Some((_, packet)) = BdcHeader::parse(payload) else { return };
let Some((_, packet)) = BdcHeader::parse(payload) else {
return;
};
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
match self.ch.try_rx_buf() {

View File

@ -6,7 +6,7 @@ version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-executor = { version = "0.2.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-executor = { version = "0.3.0", path = "../../../../../embassy-executor", features = ["defmt", "nightly", "integrated-timers", "arch-cortex-m", "executor-thread"] }
embassy-time = { version = "0.1.0", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }

View File

@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
cortex-m = "0.7"
cortex-m-rt = "0.7"
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
embassy-executor = { version = "0.2.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
embassy-executor = { version = "0.3.0", features = ["nightly", "arch-cortex-m", "executor-thread"] }
defmt = "0.3.0"
defmt-rtt = "0.3.0"

View File

@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
/// 'mess up' the internal bootloader state
pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: STATE,
state: FirmwareState<'d, STATE>,
}
#[cfg(target_os = "none")]
@ -47,22 +47,12 @@ impl<'a, FLASH: NorFlash>
}
}
impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
impl<'d, DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<'d, DFU, STATE> {
/// Create a firmware updater instance with partition ranges for the update and state partitions.
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
Self {
dfu: config.dfu,
state: config.state,
}
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
async fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned).await? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
state: FirmwareState::new(config.state, aligned),
}
}
@ -71,14 +61,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, aligned).await?;
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.get_state().await
}
/// Verify the DFU given a public key. If there is an error then DO NOT
@ -92,23 +76,16 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
///
/// If no signature feature is set then this method will always return a
/// signature error.
///
/// # Safety
///
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
#[cfg(feature = "_verify")]
pub async fn verify_and_mark_updated(
&mut self,
_public_key: &[u8],
_signature: &[u8],
_update_len: u32,
_aligned: &mut [u8],
) -> Result<(), FirmwareUpdaterError> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32);
self.verify_booted(_aligned).await?;
self.state.verify_booted().await?;
#[cfg(feature = "ed25519-dalek")]
{
@ -121,8 +98,9 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
let public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
let mut chunk_buf = [0; 2];
let mut message = [0; 64];
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
public_key.verify(&message, &signature).map_err(into_signature_error)?
}
@ -143,7 +121,8 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
let mut message = [0; 64];
self.hash::<Sha512>(_update_len, _aligned, &mut message).await?;
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message).await?;
let r = public_key.verify(&message, &signature);
trace!(
@ -156,7 +135,7 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
r.map_err(into_signature_error)?
}
self.set_magic(_aligned, SWAP_MAGIC).await
self.state.mark_updated().await
}
/// Verify the update in DFU with any digest.
@ -177,49 +156,14 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
}
/// Mark to trigger firmware swap on next boot.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
#[cfg(not(feature = "_verify"))]
pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, SWAP_MAGIC).await
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_updated().await
}
/// Mark firmware boot successful and stop rollback on reset.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub async fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, BOOT_MAGIC).await
}
async fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, aligned).await?;
if aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, aligned).await?;
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, aligned).await?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32).await?;
// Set magic
aligned.fill(magic);
self.state.write(0, aligned).await?;
}
Ok(())
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_booted().await
}
/// Write data to a flash page.
@ -229,16 +173,10 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub async fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE);
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned).await?;
self.state.verify_booted().await?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32).await?;
@ -252,20 +190,94 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
///
/// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub async fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned).await?;
pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
self.state.verify_booted().await?;
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
Ok(&mut self.dfu)
}
}
/// Manages the state partition of the firmware update.
///
/// Can be used standalone for more fine grained control, or as part of the updater.
pub struct FirmwareState<'d, STATE> {
state: STATE,
aligned: &'d mut [u8],
}
impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> {
/// Create a firmware state instance with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
Self { state, aligned }
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
async fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
if self.get_state().await? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
/// Obtain the current state.
///
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned).await?;
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Mark to trigger firmware swap on next boot.
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(SWAP_MAGIC).await
}
/// Mark firmware boot successful and stop rollback on reset.
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(BOOT_MAGIC).await
}
async fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned).await?;
if self.aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned).await?;
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
self.aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned).await?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32).await?;
// Set magic
self.aligned.fill(magic);
self.state.write(0, &self.aligned).await?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use embassy_embedded_hal::flash::partition::Partition;
@ -288,8 +300,8 @@ mod tests {
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();

View File

@ -10,9 +10,9 @@ use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, STATE_ERASE_VALUE, SWAP_MAG
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
/// 'mess up' the internal bootloader state
pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
dfu: DFU,
state: STATE,
state: BlockingFirmwareState<'d, STATE>,
}
#[cfg(target_os = "none")]
@ -49,22 +49,17 @@ impl<'a, FLASH: NorFlash>
}
}
impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
impl<'d, DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<'d, DFU, STATE> {
/// Create a firmware updater instance with partition ranges for the update and state partitions.
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>) -> Self {
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(config: FirmwareUpdaterConfig<DFU, STATE>, aligned: &'d mut [u8]) -> Self {
Self {
dfu: config.dfu,
state: config.state,
}
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
fn verify_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
if self.get_state(aligned)? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
state: BlockingFirmwareState::new(config.state, aligned),
}
}
@ -73,14 +68,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, aligned)?;
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.get_state()
}
/// Verify the DFU given a public key. If there is an error then DO NOT
@ -94,23 +83,16 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
///
/// If no signature feature is set then this method will always return a
/// signature error.
///
/// # Safety
///
/// The `_aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
#[cfg(feature = "_verify")]
pub fn verify_and_mark_updated(
&mut self,
_public_key: &[u8],
_signature: &[u8],
_update_len: u32,
_aligned: &mut [u8],
) -> Result<(), FirmwareUpdaterError> {
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
assert!(_update_len <= self.dfu.capacity() as u32);
self.verify_booted(_aligned)?;
self.state.verify_booted()?;
#[cfg(feature = "ed25519-dalek")]
{
@ -124,7 +106,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
let signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
let mut message = [0; 64];
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
public_key.verify(&message, &signature).map_err(into_signature_error)?
}
@ -145,7 +128,8 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
let signature = Signature::try_from(&signature).map_err(into_signature_error)?;
let mut message = [0; 64];
self.hash::<Sha512>(_update_len, _aligned, &mut message)?;
let mut chunk_buf = [0; 2];
self.hash::<Sha512>(_update_len, &mut chunk_buf, &mut message)?;
let r = public_key.verify(&message, &signature);
trace!(
@ -158,7 +142,7 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
r.map_err(into_signature_error)?
}
self.set_magic(_aligned, SWAP_MAGIC)
self.state.mark_updated()
}
/// Verify the update in DFU with any digest.
@ -179,49 +163,14 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
}
/// Mark to trigger firmware swap on next boot.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
#[cfg(not(feature = "_verify"))]
pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, SWAP_MAGIC)
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_updated()
}
/// Mark firmware boot successful and stop rollback on reset.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub fn mark_booted(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.set_magic(aligned, BOOT_MAGIC)
}
fn set_magic(&mut self, aligned: &mut [u8], magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, aligned)?;
if aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, aligned)?;
if aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, aligned)?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32)?;
// Set magic
aligned.fill(magic);
self.state.write(0, aligned)?;
}
Ok(())
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.state.mark_booted()
}
/// Write data to a flash page.
@ -231,15 +180,9 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub fn write_firmware(
&mut self,
aligned: &mut [u8],
offset: usize,
data: &[u8],
) -> Result<(), FirmwareUpdaterError> {
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= DFU::ERASE_SIZE);
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned)?;
self.state.verify_booted()?;
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
@ -253,19 +196,94 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
///
/// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being written to.
pub fn prepare_update(&mut self, aligned: &mut [u8]) -> Result<&mut DFU, FirmwareUpdaterError> {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
self.verify_booted(aligned)?;
pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
self.state.verify_booted()?;
self.dfu.erase(0, self.dfu.capacity() as u32)?;
Ok(&mut self.dfu)
}
}
/// Manages the state partition of the firmware update.
///
/// Can be used standalone for more fine grained control, or as part of the updater.
pub struct BlockingFirmwareState<'d, STATE> {
state: STATE,
aligned: &'d mut [u8],
}
impl<'d, STATE: NorFlash> BlockingFirmwareState<'d, STATE> {
/// Create a firmware state instance with a buffer for magic content and state partition.
///
/// # Safety
///
/// The `aligned` buffer must have a size of STATE::WRITE_SIZE, and follow the alignment rules for the flash being read from
/// and written to.
pub fn new(state: STATE, aligned: &'d mut [u8]) -> Self {
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
Self { state, aligned }
}
// Make sure we are running a booted firmware to avoid reverting to a bad state.
fn verify_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
if self.get_state()? == State::Boot {
Ok(())
} else {
Err(FirmwareUpdaterError::BadState)
}
}
/// Obtain the current state.
///
/// This is useful to check if the bootloader has just done a swap, in order
/// to do verifications and self-tests of the new image before calling
/// `mark_booted`.
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned)?;
if !self.aligned.iter().any(|&b| b != SWAP_MAGIC) {
Ok(State::Swap)
} else {
Ok(State::Boot)
}
}
/// Mark to trigger firmware swap on next boot.
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(SWAP_MAGIC)
}
/// Mark firmware boot successful and stop rollback on reset.
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
self.set_magic(BOOT_MAGIC)
}
fn set_magic(&mut self, magic: u8) -> Result<(), FirmwareUpdaterError> {
self.state.read(0, &mut self.aligned)?;
if self.aligned.iter().any(|&b| b != magic) {
// Read progress validity
self.state.read(STATE::WRITE_SIZE as u32, &mut self.aligned)?;
if self.aligned.iter().any(|&b| b != STATE_ERASE_VALUE) {
// The current progress validity marker is invalid
} else {
// Invalidate progress
self.aligned.fill(!STATE_ERASE_VALUE);
self.state.write(STATE::WRITE_SIZE as u32, &self.aligned)?;
}
// Clear magic and progress
self.state.erase(0, self.state.capacity() as u32)?;
// Set magic
self.aligned.fill(magic);
self.state.write(0, &self.aligned)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use core::cell::RefCell;
@ -283,14 +301,14 @@ mod tests {
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
let state = BlockingPartition::new(&flash, 0, 4096);
let dfu = BlockingPartition::new(&flash, 65536, 65536);
let mut aligned = [0; 8];
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
let mut to_write = [0; 4096];
to_write[..7].copy_from_slice(update.as_slice());
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
let mut aligned = [0; 8];
updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap();
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
updater.write_firmware(0, to_write.as_slice()).unwrap();
let mut chunk_buf = [0; 2];
let mut hash = [0; 20];
updater

View File

@ -3,8 +3,8 @@ mod asynch;
mod blocking;
#[cfg(feature = "nightly")]
pub use asynch::FirmwareUpdater;
pub use blocking::BlockingFirmwareUpdater;
pub use asynch::{FirmwareState, FirmwareUpdater};
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
/// Firmware updater flash configuration holding the two flashes used by the updater

View File

@ -16,9 +16,11 @@ mod test_flash;
// TODO: Use the value provided by NorFlash when available
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
pub use firmware_updater::{
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError,
};
#[cfg(feature = "nightly")]
pub use firmware_updater::FirmwareUpdater;
pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
pub use firmware_updater::{FirmwareState, FirmwareUpdater};
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
@ -118,15 +120,18 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
// Writing after marking updated is not allowed until marked as booted.
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(&mut aligned, 0, &UPDATE));
let res: Result<(), FirmwareUpdaterError> = block_on(updater.write_firmware(0, &UPDATE));
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
let flash = flash.into_blocking();
@ -158,11 +163,14 @@ mod tests {
// Mark as booted
let flash = flash.into_async();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.mark_booted(&mut aligned)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.mark_booted()).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -190,12 +198,15 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -232,12 +243,15 @@ mod tests {
block_on(flash.active().erase(0, ORIGINAL.len() as u32)).unwrap();
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
block_on(updater.mark_updated(&mut aligned)).unwrap();
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
block_on(updater.mark_updated()).unwrap();
let flash = flash.into_blocking();
let mut bootloader = BootLoader::new(BootLoaderConfig {
@ -293,18 +307,19 @@ mod tests {
// On with the test
let flash = flash.into_async();
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
});
let mut aligned = [0; 4];
let mut updater = FirmwareUpdater::new(
FirmwareUpdaterConfig {
dfu: flash.dfu(),
state: flash.state(),
},
&mut aligned,
);
assert!(block_on(updater.verify_and_mark_updated(
&public_key.to_bytes(),
&signature.to_bytes(),
firmware_len as u32,
&mut aligned,
))
.is_ok());
}

View File

@ -3,37 +3,28 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig};
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embassy_nrf::nvmc::PAGE_SIZE;
use embassy_nrf::peripherals::WDT;
use embassy_nrf::wdt;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for nRF devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(&mut self.aligned_buf.0)
.expect("Boot prepare error");
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware.
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
Self
}
/// Boots the application without softdevice mechanisms.
@ -43,8 +34,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(not(feature = "softdevice"))]
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
let mut p = cortex_m::Peripherals::steal();
p.SCB.invalidate_icache();
p.SCB.vtor.write(start);
@ -57,7 +46,7 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
#[cfg(feature = "softdevice")]
pub unsafe fn load(&mut self, _app: u32) -> ! {
pub unsafe fn load(self, _app: u32) -> ! {
use nrf_softdevice_mbr as mbr;
const NRF_SUCCESS: u32 = 0;
@ -104,15 +93,15 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
}
}
/// A flash implementation that wraps NVMC and will pet a watchdog when touching flash.
pub struct WatchdogFlash<'d> {
flash: Nvmc<'d>,
/// A flash implementation that wraps any flash and will pet a watchdog when touching flash.
pub struct WatchdogFlash<FLASH> {
flash: FLASH,
wdt: wdt::WatchdogHandle,
}
impl<'d> WatchdogFlash<'d> {
impl<FLASH> WatchdogFlash<FLASH> {
/// Start a new watchdog with a given flash and WDT peripheral and a timeout
pub fn start(flash: Nvmc<'d>, wdt: WDT, config: wdt::Config) -> Self {
pub fn start(flash: FLASH, wdt: WDT, config: wdt::Config) -> Self {
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
Ok(x) => x,
Err(_) => {
@ -127,13 +116,13 @@ impl<'d> WatchdogFlash<'d> {
}
}
impl<'d> ErrorType for WatchdogFlash<'d> {
type Error = <Nvmc<'d> as ErrorType>::Error;
impl<FLASH: ErrorType> ErrorType for WatchdogFlash<FLASH> {
type Error = FLASH::Error;
}
impl<'d> NorFlash for WatchdogFlash<'d> {
const WRITE_SIZE: usize = <Nvmc<'d> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Nvmc<'d> as NorFlash>::ERASE_SIZE;
impl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {
const WRITE_SIZE: usize = FLASH::WRITE_SIZE;
const ERASE_SIZE: usize = FLASH::ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.wdt.pet();
@ -145,8 +134,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> {
}
}
impl<'d> ReadNorFlash for WatchdogFlash<'d> {
const READ_SIZE: usize = <Nvmc<'d> as ReadNorFlash>::READ_SIZE;
impl<FLASH: ReadNorFlash> ReadNorFlash for WatchdogFlash<FLASH> {
const READ_SIZE: usize = FLASH::READ_SIZE;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.wdt.pet();
self.flash.read(offset, data)

View File

@ -3,38 +3,29 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
use embassy_rp::flash::{Flash, ERASE_SIZE};
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
use embassy_rp::peripherals::{FLASH, WATCHDOG};
use embassy_rp::watchdog::Watchdog;
use embassy_time::Duration;
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
/// A bootloader for RP2040 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE>;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(self.aligned_buf.as_mut())
.expect("Boot prepare error");
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self
}
/// Boots the application.
@ -43,8 +34,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal();
@ -58,14 +47,14 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
/// A flash implementation that will feed a watchdog when touching flash.
pub struct WatchdogFlash<'d, const SIZE: usize> {
flash: Flash<'d, FLASH, SIZE>,
flash: Flash<'d, FLASH, Blocking, SIZE>,
watchdog: Watchdog,
}
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash);
let flash = Flash::<_, Blocking, SIZE>::new_blocking(flash);
let mut watchdog = Watchdog::new(watchdog);
watchdog.start(timeout);
Self { flash, watchdog }
@ -73,28 +62,28 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
}
impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> {
type Error = <Flash<'d, FLASH, SIZE> as ErrorType>::Error;
type Error = <Flash<'d, FLASH, Blocking, SIZE> as ErrorType>::Error;
}
impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
const WRITE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::ERASE_SIZE;
const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE;
const ERASE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
self.watchdog.feed();
self.flash.erase(from, to)
self.flash.blocking_erase(from, to)
}
fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
self.watchdog.feed();
self.flash.write(offset, data)
self.flash.blocking_write(offset, data)
}
}
impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
const READ_SIZE: usize = <Flash<'d, FLASH, SIZE> as ReadNorFlash>::READ_SIZE;
const READ_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as ReadNorFlash>::READ_SIZE;
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
self.watchdog.feed();
self.flash.read(offset, data)
self.flash.blocking_read(offset, data)
}
fn capacity(&self) -> usize {
self.flash.capacity()

View File

@ -3,34 +3,25 @@
#![doc = include_str!("../README.md")]
mod fmt;
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
};
#[cfg(feature = "nightly")]
pub use embassy_boot::FirmwareUpdater;
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
use embedded_storage::nor_flash::NorFlash;
/// A bootloader for STM32 devices.
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> {
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
}
pub struct BootLoader;
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
BootLoader<ACTIVE, DFU, STATE, BUFFER_SIZE>
{
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
Self {
boot: embassy_boot::BootLoader::new(config),
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
}
}
/// Inspect the bootloader state and perform actions required before booting, such as swapping
/// firmware.
pub fn prepare(&mut self) {
self.boot
.prepare_boot(self.aligned_buf.as_mut())
.expect("Boot prepare error");
impl BootLoader {
/// Inspect the bootloader state and perform actions required before booting, such as swapping firmware
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
Self
}
/// Boots the application.
@ -39,8 +30,6 @@ impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
///
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
pub unsafe fn load(self, start: u32) -> ! {
core::mem::drop(self.boot);
trace!("Loading app at 0x{:x}", start);
#[allow(unused_mut)]
let mut p = cortex_m::Peripherals::steal();

View File

@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu"
std = []
# Enable nightly-only features
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
time = ["dep:embassy-time"]
default = ["time"]
[dependencies]
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
"unproven",
] }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
embedded-storage = "0.3.0"
embedded-storage-async = { version = "0.4.0", optional = true }
nb = "1.0.0"

View File

@ -1,4 +1,4 @@
use embedded_hal_02::{blocking, serial};
use embedded_hal_02::blocking;
/// Wrapper that implements async traits using blocking implementations.
///
@ -74,7 +74,21 @@ where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
// Ensure we write the expected bytes
for i in 0..core::cmp::min(read.len(), write.len()) {
read[i] = write[i].clone();
@ -83,52 +97,12 @@ where
Ok(())
}
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
todo!()
}
}
impl<T, E> embedded_hal_async::spi::SpiBusFlush for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusWrite<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
self.wrapped.write(data)?;
Ok(())
}
}
impl<T, E> embedded_hal_async::spi::SpiBusRead<u8> for BlockingAsync<T>
where
E: embedded_hal_1::spi::Error + 'static,
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
async fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(data)?;
Ok(())
}
}
// Uart implementatinos
impl<T, E> embedded_hal_1::serial::ErrorType for BlockingAsync<T>
where
T: serial::Read<u8, Error = E>,
E: embedded_hal_1::serial::Error + 'static,
{
type Error = E;
}
/// NOR flash wrapper
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};

View File

@ -69,54 +69,39 @@ where
type Error = T::Error;
}
impl<T> embedded_hal_async::spi::SpiBus<u8> for YieldingAsync<T>
impl<T, Word: 'static + Copy> embedded_hal_async::spi::SpiBus<Word> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBus,
{
async fn transfer<'a>(&'a mut self, read: &'a mut [u8], write: &'a [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusFlush for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusFlush,
T: embedded_hal_async::spi::SpiBus<Word>,
{
async fn flush(&mut self) -> Result<(), Self::Error> {
self.wrapped.flush().await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusWrite<u8>,
{
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
self.wrapped.write(data).await?;
yield_now().await;
Ok(())
}
}
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
where
T: embedded_hal_async::spi::SpiBusRead<u8>,
{
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
self.wrapped.read(data).await?;
yield_now().await;
Ok(())
}
async fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.wrapped.transfer(read, write).await?;
yield_now().await;
Ok(())
}
async fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.wrapped.transfer_in_place(words).await?;
yield_now().await;
Ok(())
}
}
///

View File

@ -56,62 +56,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -129,6 +73,12 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};
@ -172,64 +122,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<M, BUS, CS> spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusWrite + SetConfig,
CS: OutputPin,
{
async fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.write(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: spi::SpiBusRead + SetConfig,
CS: OutputPin,
{
async fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
let mut bus = self.bus.lock().await;
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res: Result<(), BUS::Error> = try {
for buf in operations {
bus.read(buf).await?;
}
};
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush().await;
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
}
}
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -248,6 +140,12 @@ where
Operation::Write(buf) => bus.write(buf).await?,
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).await?,
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => return Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::Timer::after(embassy_time::Duration::from_micros(*us as _)).await
}
}
}
};

View File

@ -22,7 +22,7 @@ use core::cell::RefCell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::blocking_mutex::Mutex;
use embedded_hal_1::digital::OutputPin;
use embedded_hal_1::spi::{self, Operation, SpiBus, SpiBusRead, SpiBusWrite};
use embedded_hal_1::spi::{self, Operation, SpiBus};
use crate::shared_bus::SpiDeviceError;
use crate::SetConfig;
@ -48,58 +48,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
where
M: RawMutex,
@ -116,6 +64,13 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.
@ -199,58 +154,6 @@ where
type Error = SpiDeviceError<BUS::Error, CS::Error>;
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceRead for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusRead + SetConfig,
CS: OutputPin,
{
fn read_transaction(&mut self, operations: &mut [&mut [u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter_mut().try_for_each(|buf| bus.read(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDeviceWrite for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
BUS: SpiBusWrite + SetConfig,
CS: OutputPin,
{
fn write_transaction(&mut self, operations: &[&[u8]]) -> Result<(), Self::Error> {
self.bus.lock(|bus| {
let mut bus = bus.borrow_mut();
bus.set_config(&self.config);
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
let op_res = operations.iter().try_for_each(|buf| bus.write(buf));
// On failure, it's important to still flush and deassert CS.
let flush_res = bus.flush();
let cs_res = self.cs.set_high();
let op_res = op_res.map_err(SpiDeviceError::Spi)?;
flush_res.map_err(SpiDeviceError::Spi)?;
cs_res.map_err(SpiDeviceError::Cs)?;
Ok(op_res)
})
}
}
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
where
M: RawMutex,
@ -268,6 +171,13 @@ where
Operation::Write(buf) => bus.write(buf),
Operation::Transfer(read, write) => bus.transfer(read, write),
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
#[cfg(not(feature = "time"))]
Operation::DelayUs(_) => Err(SpiDeviceError::DelayUsNotSupported),
#[cfg(feature = "time")]
Operation::DelayUs(us) => {
embassy_time::block_for(embassy_time::Duration::from_micros(*us as _));
Ok(())
}
});
// On failure, it's important to still flush and deassert CS.

View File

@ -30,11 +30,14 @@ where
/// Error returned by SPI device implementations in this crate.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum SpiDeviceError<BUS, CS> {
/// An operation on the inner SPI bus failed.
Spi(BUS),
/// Setting the value of the Chip Select (CS) pin failed.
Cs(CS),
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
DelayUsNotSupported,
}
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
@ -46,6 +49,7 @@ where
match self {
Self::Spi(e) => e.kind(),
Self::Cs(_) => spi::ErrorKind::Other,
Self::DelayUsNotSupported => spi::ErrorKind::Other,
}
}
}

View File

@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.3.0 - 2023-08-25
- Replaced Pender. Implementations now must define an extern function called `__pender`.
- Made `raw::AvailableTask` public
- Made `SpawnToken::new_failed` public
- You can now use arbitrary expressions to specify `#[task(pool_size = X)]`
## 0.2.1 - 2023-08-10
- Avoid calling `pend()` when waking expired timers
- Properly reset finished task state with `integrated-timers` enabled
- Introduce `InterruptExecutor::spawner()`
- Fix incorrect critical section in Xtensa executor
## 0.2.0 - 2023-04-27
- Replace unnecessary atomics in runqueue

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-executor"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "async/await executor designed for embedded usage"
@ -14,7 +14,7 @@ categories = [
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
features = ["nightly", "defmt", "pender-callback"]
features = ["nightly", "defmt"]
flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
@ -25,7 +25,7 @@ flavors = [
[package.metadata.docs.rs]
default-target = "thumbv7em-none-eabi"
targets = ["thumbv7em-none-eabi"]
features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"]
features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
[features]
@ -37,9 +37,6 @@ arch-xtensa = ["_arch"]
arch-riscv32 = ["_arch"]
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
# Enable creating a `Pender` from an arbitrary function pointer callback.
pender-callback = []
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
executor-thread = []
# Enable the interrupt-mode executor (available in Cortex-M only)
@ -61,8 +58,8 @@ log = { version = "0.4.14", optional = true }
rtos-trace = { version = "0.1.2", optional = true }
futures-util = { version = "0.3.17", default-features = false }
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true}
atomic-polyfill = "1.0.1"
critical-section = "1.1"
static_cell = "1.1"

View File

@ -1,3 +1,49 @@
const THREAD_PENDER: usize = usize::MAX;
#[export_name = "__pender"]
#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
fn __pender(context: *mut ()) {
unsafe {
// Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt
// request number given to `InterruptExecutor::start`.
let context = context as usize;
#[cfg(feature = "executor-thread")]
// Try to make Rust optimize the branching away if we only use thread mode.
if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER {
core::arch::asm!("sev");
return;
}
#[cfg(feature = "executor-interrupt")]
{
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
#[derive(Clone, Copy)]
struct Irq(u16);
unsafe impl InterruptNumber for Irq {
fn number(self) -> u16 {
self.0
}
}
let irq = Irq(context as u16);
// STIR is faster, but is only available in v7 and higher.
#[cfg(not(armv6m))]
{
let mut nvic: NVIC = core::mem::transmute(());
nvic.request(irq);
}
#[cfg(armv6m)]
NVIC::pend(irq);
}
}
}
#[cfg(feature = "executor-thread")]
pub use thread::*;
#[cfg(feature = "executor-thread")]
@ -8,18 +54,9 @@ mod thread {
#[cfg(feature = "nightly")]
pub use embassy_macros::main_cortex_m as main;
use crate::raw::{Pender, PenderInner};
use crate::arch::THREAD_PENDER;
use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
pub(crate) fn pend(self) {
unsafe { core::arch::asm!("sev") }
}
}
/// Thread mode executor, using WFE/SEV.
///
/// This is the simplest and most common kind of executor. It runs on
@ -39,7 +76,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
not_send: PhantomData,
}
}
@ -86,30 +123,7 @@ mod interrupt {
use cortex_m::interrupt::InterruptNumber;
use cortex_m::peripheral::NVIC;
use crate::raw::{self, Pender, PenderInner};
#[derive(Clone, Copy)]
pub(crate) struct InterruptPender(u16);
impl InterruptPender {
pub(crate) fn pend(self) {
// STIR is faster, but is only available in v7 and higher.
#[cfg(not(armv6m))]
{
let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
nvic.request(self);
}
#[cfg(armv6m)]
cortex_m::peripheral::NVIC::pend(self);
}
}
unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
fn number(self) -> u16 {
self.0
}
}
use crate::raw;
/// Interrupt mode executor.
///
@ -194,9 +208,7 @@ mod interrupt {
unsafe {
(&mut *self.executor.get())
.as_mut_ptr()
.write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
irq.number(),
)))))
.write(raw::Executor::new(irq.number() as *mut ()))
}
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };

View File

@ -11,22 +11,16 @@ mod thread {
#[cfg(feature = "nightly")]
pub use embassy_macros::main_riscv as main;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
}
}
/// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
#[export_name = "__pender"]
fn __pender(_context: *mut ()) {
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
}
/// RISCV32 Executor
pub struct Executor {
inner: raw::Executor,
@ -37,7 +31,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}

View File

@ -11,17 +11,12 @@ mod thread {
#[cfg(feature = "nightly")]
pub use embassy_macros::main_std as main;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender(&'static Signaler);
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
self.0.signal()
}
#[export_name = "__pender"]
fn __pender(context: *mut ()) {
let signaler: &'static Signaler = unsafe { std::mem::transmute(context) };
signaler.signal()
}
/// Single-threaded std-based executor.
@ -34,9 +29,9 @@ mod thread {
impl Executor {
/// Create a new Executor.
pub fn new() -> Self {
let signaler = &*Box::leak(Box::new(Signaler::new()));
let signaler = Box::leak(Box::new(Signaler::new()));
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
inner: raw::Executor::new(signaler as *mut Signaler as *mut ()),
not_send: PhantomData,
signaler,
}

View File

@ -14,14 +14,12 @@ mod thread {
use wasm_bindgen::prelude::*;
use crate::raw::util::UninitCell;
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner};
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
pub struct Executor {
inner: raw::Executor,
ctx: &'static WasmContext,
not_send: PhantomData<*mut ()>,
#[export_name = "__pender"]
fn __pender(context: *mut ()) {
let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
}
pub(crate) struct WasmContext {
@ -29,16 +27,6 @@ mod thread {
closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
}
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender(&'static WasmContext);
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
}
}
impl WasmContext {
pub fn new() -> Self {
Self {
@ -48,14 +36,21 @@ mod thread {
}
}
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
pub struct Executor {
inner: raw::Executor,
ctx: &'static WasmContext,
not_send: PhantomData<*mut ()>,
}
impl Executor {
/// Create a new Executor.
pub fn new() -> Self {
let ctx = &*Box::leak(Box::new(WasmContext::new()));
let ctx = Box::leak(Box::new(WasmContext::new()));
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
not_send: PhantomData,
inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),
ctx,
not_send: PhantomData,
}
}

View File

@ -8,22 +8,16 @@ mod thread {
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
use crate::raw::{Pender, PenderInner};
use crate::{raw, Spawner};
#[derive(Copy, Clone)]
pub(crate) struct ThreadPender;
impl ThreadPender {
#[allow(unused)]
pub(crate) fn pend(self) {
SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
}
}
/// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
#[export_name = "__pender"]
fn __pender(_context: *mut ()) {
SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
}
/// Xtensa Executor
pub struct Executor {
inner: raw::Executor,
@ -34,7 +28,7 @@ mod thread {
/// Create a new Executor.
pub fn new() -> Self {
Self {
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
inner: raw::Executor::new(core::ptr::null_mut()),
not_send: PhantomData,
}
}
@ -77,8 +71,8 @@ mod thread {
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
core::arch::asm!(
"wsr.ps {0}",
"rsync", in(reg) token)
"wsr.ps {0}",
"rsync", in(reg) token)
} else {
// waiti sets the PS.INTLEVEL when slipping into sleep
// because critical sections in Xtensa are implemented via increasing

View File

@ -147,10 +147,7 @@ impl<F: Future + 'static> TaskStorage<F> {
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
let task = AvailableTask::claim(self);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<F>::new(task) }
}
Some(task) => task.initialize(future),
None => SpawnToken::new_failed(),
}
}
@ -165,6 +162,9 @@ impl<F: Future + 'static> TaskStorage<F> {
Poll::Ready(_) => {
this.future.drop_in_place();
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
#[cfg(feature = "integrated-timers")]
this.raw.expires_at.set(Instant::MAX);
}
Poll::Pending => {}
}
@ -183,12 +183,16 @@ impl<F: Future + 'static> TaskStorage<F> {
}
}
struct AvailableTask<F: Future + 'static> {
/// An uninitialized [`TaskStorage`].
pub struct AvailableTask<F: Future + 'static> {
task: &'static TaskStorage<F>,
}
impl<F: Future + 'static> AvailableTask<F> {
fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
/// Try to claim a [`TaskStorage`].
///
/// This function returns `None` if a task has already been spawned and has not finished running.
pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
task.raw
.state
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
@ -196,61 +200,30 @@ impl<F: Future + 'static> AvailableTask<F> {
.map(|_| Self { task })
}
fn initialize(self, future: impl FnOnce() -> F) -> TaskRef {
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
unsafe {
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
self.task.future.write(future());
}
TaskRef::new(self.task)
}
}
/// Raw storage that can hold up to N tasks of the same type.
///
/// This is essentially a `[TaskStorage<F>; N]`.
pub struct TaskPool<F: Future + 'static, const N: usize> {
pool: [TaskStorage<F>; N],
}
let task = TaskRef::new(self.task);
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
/// Create a new TaskPool, with all tasks in non-spawned state.
pub const fn new() -> Self {
Self {
pool: [TaskStorage::NEW; N],
SpawnToken::new(task)
}
}
/// Try to spawn a task in the pool.
///
/// See [`TaskStorage::spawn()`] for details.
///
/// This will loop over the pool and spawn the task in the first storage that
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
let task = self.pool.iter().find_map(AvailableTask::claim);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<F>::new(task) }
}
None => SpawnToken::new_failed(),
}
/// Initialize the [`TaskStorage`] to run the given future.
pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
self.initialize_impl::<F>(future)
}
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
/// the future is !Send.
/// Initialize the [`TaskStorage`] to run the given future.
///
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
/// by the Embassy macros ONLY.
/// # Safety
///
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
/// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
/// is an `async fn`, NOT a hand-written `Future`.
#[doc(hidden)]
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
where
FutFn: FnOnce() -> F,
{
pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
// When send-spawning a task, we construct the future in this thread, and effectively
// "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
// send-spawning should require the future `F` to be `Send`.
@ -276,66 +249,73 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
//
// This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly
// by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
self.initialize_impl::<FutFn>(future)
}
}
let task = self.pool.iter().find_map(AvailableTask::claim);
match task {
Some(task) => {
let task = task.initialize(future);
unsafe { SpawnToken::<FutFn>::new(task) }
}
/// Raw storage that can hold up to N tasks of the same type.
///
/// This is essentially a `[TaskStorage<F>; N]`.
pub struct TaskPool<F: Future + 'static, const N: usize> {
pool: [TaskStorage<F>; N],
}
impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
/// Create a new TaskPool, with all tasks in non-spawned state.
pub const fn new() -> Self {
Self {
pool: [TaskStorage::NEW; N],
}
}
fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> {
match self.pool.iter().find_map(AvailableTask::claim) {
Some(task) => task.initialize_impl::<T>(future),
None => SpawnToken::new_failed(),
}
}
/// Try to spawn a task in the pool.
///
/// See [`TaskStorage::spawn()`] for details.
///
/// This will loop over the pool and spawn the task in the first storage that
/// is currently free. If none is free, a "poisoned" SpawnToken is returned,
/// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
self.spawn_impl::<F>(future)
}
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
/// the future is !Send.
///
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
/// by the Embassy macros ONLY.
///
/// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
/// is an `async fn`, NOT a hand-written `Future`.
#[doc(hidden)]
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
where
FutFn: FnOnce() -> F,
{
// See the comment in AvailableTask::__initialize_async_fn for explanation.
self.spawn_impl::<FutFn>(future)
}
}
#[derive(Clone, Copy)]
pub(crate) enum PenderInner {
#[cfg(feature = "executor-thread")]
Thread(crate::arch::ThreadPender),
#[cfg(feature = "executor-interrupt")]
Interrupt(crate::arch::InterruptPender),
#[cfg(feature = "pender-callback")]
Callback { func: fn(*mut ()), context: *mut () },
}
pub(crate) struct Pender(*mut ());
unsafe impl Send for PenderInner {}
unsafe impl Sync for PenderInner {}
/// Platform/architecture-specific action executed when an executor has pending work.
///
/// When a task within an executor is woken, the `Pender` is called. This does a
/// platform/architecture-specific action to signal there is pending work in the executor.
/// When this happens, you must arrange for [`Executor::poll`] to be called.
///
/// You can think of it as a waker, but for the whole executor.
pub struct Pender(pub(crate) PenderInner);
unsafe impl Send for Pender {}
unsafe impl Sync for Pender {}
impl Pender {
/// Create a `Pender` that will call an arbitrary function pointer.
///
/// # Arguments
///
/// - `func`: The function pointer to call.
/// - `context`: Opaque context pointer, that will be passed to the function pointer.
#[cfg(feature = "pender-callback")]
pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
Self(PenderInner::Callback {
func,
context: context.into(),
})
}
}
impl Pender {
pub(crate) fn pend(&self) {
match self.0 {
#[cfg(feature = "executor-thread")]
PenderInner::Thread(x) => x.pend(),
#[cfg(feature = "executor-interrupt")]
PenderInner::Interrupt(x) => x.pend(),
#[cfg(feature = "pender-callback")]
PenderInner::Callback { func, context } => func(context),
pub(crate) fn pend(self) {
extern "Rust" {
fn __pender(context: *mut ());
}
unsafe { __pender(self.0) };
}
}
@ -406,7 +386,7 @@ impl SyncExecutor {
#[allow(clippy::never_loop)]
loop {
#[cfg(feature = "integrated-timers")]
self.timer_queue.dequeue_expired(Instant::now(), |task| wake_task(task));
self.timer_queue.dequeue_expired(Instant::now(), wake_task_no_pend);
self.run_queue.dequeue_all(|p| {
let task = p.header();
@ -469,15 +449,31 @@ impl SyncExecutor {
///
/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
/// that "want to run").
/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
/// to do. You must arrange for `poll()` to be called as soon as possible.
/// - You must supply a pender function, as shown below. The executor will call it to notify you
/// it has work to do. You must arrange for `poll()` to be called as soon as possible.
/// - Enabling `arch-xx` features will define a pender function for you. This means that you
/// are limited to using the executors provided to you by the architecture/platform
/// implementation. If you need a different executor, you must not enable `arch-xx` features.
///
/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority
/// The pender can be called from *any* context: any thread, any interrupt priority
/// level, etc. It may be called synchronously from any `Executor` method call as well.
/// You must deal with this correctly.
///
/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
/// the requirement for `poll` to not be called reentrantly.
///
/// The pender function must be exported with the name `__pender` and have the following signature:
///
/// ```rust
/// #[export_name = "__pender"]
/// fn pender(context: *mut ()) {
/// // schedule `poll()` to be called
/// }
/// ```
///
/// The `context` argument is a piece of arbitrary data the executor will pass to the pender.
/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example,
/// differentiate between executors, or to pass a pointer to a callback that should be called.
#[repr(transparent)]
pub struct Executor {
pub(crate) inner: SyncExecutor,
@ -492,12 +488,12 @@ impl Executor {
/// Create a new executor.
///
/// When the executor has work to do, it will call the [`Pender`].
/// When the executor has work to do, it will call the pender function and pass `context` to it.
///
/// See [`Executor`] docs for details on `Pender`.
pub fn new(pender: Pender) -> Self {
/// See [`Executor`] docs for details on the pender.
pub fn new(context: *mut ()) -> Self {
Self {
inner: SyncExecutor::new(pender),
inner: SyncExecutor::new(Pender(context)),
_not_sync: PhantomData,
}
}
@ -520,16 +516,16 @@ impl Executor {
/// This loops over all tasks that are queued to be polled (i.e. they're
/// freshly spawned or they've been woken). Other tasks are not polled.
///
/// You must call `poll` after receiving a call to the [`Pender`]. It is OK
/// to call `poll` even when not requested by the `Pender`, but it wastes
/// You must call `poll` after receiving a call to the pender. It is OK
/// to call `poll` even when not requested by the pender, but it wastes
/// energy.
///
/// # Safety
///
/// You must NOT call `poll` reentrantly on the same executor.
///
/// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
/// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to
/// In particular, note that `poll` may call the pender synchronously. Therefore, you
/// must NOT directly call `poll()` from the pender callback. Instead, the callback has to
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's
/// no `poll()` already running.
pub unsafe fn poll(&'static self) {
@ -570,6 +566,31 @@ pub fn wake_task(task: TaskRef) {
}
}
/// Wake a task by `TaskRef` without calling pend.
///
/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
pub fn wake_task_no_pend(task: TaskRef) {
let header = task.header();
let res = header.state.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
// If already scheduled, or if not started,
if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) {
None
} else {
// Mark it as scheduled
Some(state | STATE_RUN_QUEUED)
}
});
if res.is_ok() {
// We have just marked the task as scheduled, so enqueue it.
unsafe {
let executor = header.executor.get().unwrap_unchecked();
executor.run_queue.enqueue(task);
}
}
}
#[cfg(feature = "integrated-timers")]
struct TimerQueue;

View File

@ -33,7 +33,8 @@ impl<S> SpawnToken<S> {
}
}
pub(crate) fn new_failed() -> Self {
/// Return a SpawnToken that represents a failed spawn.
pub fn new_failed() -> Self {
Self {
raw_task: None,
phantom: PhantomData,

View File

@ -1,5 +1,5 @@
[package]
name = "embassy-hal-common"
name = "embassy-hal-internal"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

View File

@ -0,0 +1,16 @@
# embassy-macros
An [Embassy](https://embassy.dev) project.
Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport
everything you need to use them effectively.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
<http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.

View File

@ -1,5 +1,6 @@
#![no_std]
#![allow(clippy::new_without_default)]
#![doc = include_str!("../README.md")]
// This mod MUST go first, so that the others see its macros.
pub(crate) mod fmt;

View File

@ -116,6 +116,7 @@ macro_rules! impl_peripheral {
#[inline]
unsafe fn clone_unchecked(&self) -> Self::P {
#[allow(clippy::needless_update)]
$type { ..*self }
}
}

View File

@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
}
}
impl<'b, T: Deref> Peripheral for T
impl<'b, T: DerefMut> Peripheral for T
where
T::Target: Peripheral,
{

View File

@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi"
[features]
stm32wl = ["dep:embassy-stm32"]
time = []
time = ["embassy-time", "lorawan-device"]
defmt = ["dep:defmt", "lorawan-device/defmt"]
[dependencies]
@ -20,15 +20,15 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true }
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embedded-hal = { version = "0.2", features = ["unproven"] }
bit_field = { version = "0.10" }
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
lora-phy = { version = "1" }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
[patch.crates-io]
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}

View File

@ -1,6 +1,6 @@
[package]
name = "embassy-macros"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "macros for creating the entry point and tasks for embassy-executor"

View File

@ -0,0 +1,41 @@
[package]
name = "embassy-net-adin1110"
version = "0.1.0"
description = "embassy-net driver for the ADIN1110 ethernet chip"
keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
heapless = "0.7.16"
defmt = { version = "0.3", optional = true }
log = { version = "0.4.4", default-features = false, optional = true }
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embedded-hal-bus = { version = "=0.1.0-rc.1", features = ["async"] }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.0" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
bitfield = "0.14.0"
[dev-dependencies]
# reenable when https://github.com/dbrgn/embedded-hal-mock/pull/86 is merged.
#embedded-hal-mock = { git = "https://github.com/dbrgn/embedded-hal-mock", branch = "1-alpha", features = ["embedded-hal-async", "eh1"] }] }
embedded-hal-mock = { git = "https://github.com/newAM/embedded-hal-mock", branch = "eh1-rc.1", features = ["embedded-hal-async", "eh1"] }
crc = "3.0.1"
env_logger = "0.10"
critical-section = { version = "1.1.1", features = ["std"] }
futures-test = "0.3.17"
[features]
default = [ ]
defmt = [ "dep:defmt" ]
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-adin1110-v$VERSION/embassy-net-adin1110/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-adin1110/src/"
target = "thumbv7em-none-eabi"

View File

@ -0,0 +1,55 @@
# SPE ADIN1110 `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the `Analog ADIN1110` SPI SPE ethernet chips.
## What is SPE or Single Pair Ethernet / 10 BASE-T1L
SPE stands for Single Pair Ethernet. As the names implies, SPE uses differential signalling with 2 wires (a twisted-pair) in a cable as the physical medium.
SPE is full-duplex - it can transmit and receive ethernet packets at the same time. SPE is still ethernet, only the physical layer is different.
SPE also supports [`PoDL (Power over Data Line)`](https://www.ti.com/lit/an/snla395/snla395.pdf), power delivery from 0.5 up to 50 Watts, similar to [`PoE`](https://en.wikipedia.org/wiki/Power_over_Ethernet), but an additional hardware and handshake protocol are needed.
SPE has many link speeds but only `10 BASE-T1L` is able to reach cable lengths up to 1000 meters in `2.4 Vpp` transmit amplitude.
Currently in 2023, none of the standards are compatible with each other.
Thus `10 BASE-T1L` won't work with a `10 BASE-T1S`, `100 BASE-T1` or any standard `x BASE-T`.
In the industry SPE is also called [`APL (Advanced Physical Layer)`](https://www.ethernet-apl.org), and is based on the `10 BASE-T1L` standard.
APL can be used in [`intrinsic safety applications/explosion hazardous areas`](https://en.wikipedia.org/wiki/Electrical_equipment_in_hazardous_areas) which has its own name and standard called [`2-WISE (2-wire intrinsically safe ethernet) IEC TS 60079-47:2021`](https://webstore.iec.ch/publication/64292).
`10 BASE-T1L` and `ADIN1110` are designed to support intrinsic safety applications. The power supply energy is fixed and PDoL is not supported.
## Supported SPI modes
`ADIN1110` supports two SPI modes. `Generic` and [`OPEN Alliance 10BASE-T1x MAC-PHY serial interface`](https://opensig.org/download/document/OPEN_Alliance_10BASET1x_MAC-PHY_Serial_Interface_V1.1.pdf)
Both modes support with and without additional CRC.
Currently only `Generic` SPI with or without CRC is supported.
*NOTE:* SPI Mode is selected by the hardware pins `SPI_CFG0` and `SPI_CFG1`. Software can't detect nor change the mode.
## Hardware
- Tested on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html) with an `STM32L4S5QII3P`, see [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) dor an example.
- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit`](https://www.sparkfun.com/products/19628), supporting multiple microcontrollers. **Make sure to check if it's a microcontroller that is supported by Embassy!**
## Other SPE chips
* [`Analog ADIN2111`](https://www.analog.com/en/products/adin2111.html) 2 Port SPI version. Can work with this driver.
* [`Analog ADIN1100`](https://www.analog.com/en/products/adin1100.html) RGMII version.
## Testing
ADIN1110 library can tested on the host with a mock SPI driver.
$ `cargo test --target x86_64-unknown-linux-gnu`
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -0,0 +1,358 @@
pub const CRC32R_LOOKUP_TABLE: [u32; 256] = [
0x0000_0000,
0x7707_3096,
0xEE0E_612C,
0x9909_51BA,
0x076D_C419,
0x706A_F48F,
0xE963_A535,
0x9E64_95A3,
0x0EDB_8832,
0x79DC_B8A4,
0xE0D5_E91E,
0x97D2_D988,
0x09B6_4C2B,
0x7EB1_7CBD,
0xE7B8_2D07,
0x90BF_1D91,
0x1DB7_1064,
0x6AB0_20F2,
0xF3B9_7148,
0x84BE_41DE,
0x1ADA_D47D,
0x6DDD_E4EB,
0xF4D4_B551,
0x83D3_85C7,
0x136C_9856,
0x646B_A8C0,
0xFD62_F97A,
0x8A65_C9EC,
0x1401_5C4F,
0x6306_6CD9,
0xFA0F_3D63,
0x8D08_0DF5,
0x3B6E_20C8,
0x4C69_105E,
0xD560_41E4,
0xA267_7172,
0x3C03_E4D1,
0x4B04_D447,
0xD20D_85FD,
0xA50A_B56B,
0x35B5_A8FA,
0x42B2_986C,
0xDBBB_C9D6,
0xACBC_F940,
0x32D8_6CE3,
0x45DF_5C75,
0xDCD6_0DCF,
0xABD1_3D59,
0x26D9_30AC,
0x51DE_003A,
0xC8D7_5180,
0xBFD0_6116,
0x21B4_F4B5,
0x56B3_C423,
0xCFBA_9599,
0xB8BD_A50F,
0x2802_B89E,
0x5F05_8808,
0xC60C_D9B2,
0xB10B_E924,
0x2F6F_7C87,
0x5868_4C11,
0xC161_1DAB,
0xB666_2D3D,
0x76DC_4190,
0x01DB_7106,
0x98D2_20BC,
0xEFD5_102A,
0x71B1_8589,
0x06B6_B51F,
0x9FBF_E4A5,
0xE8B8_D433,
0x7807_C9A2,
0x0F00_F934,
0x9609_A88E,
0xE10E_9818,
0x7F6A_0DBB,
0x086D_3D2D,
0x9164_6C97,
0xE663_5C01,
0x6B6B_51F4,
0x1C6C_6162,
0x8565_30D8,
0xF262_004E,
0x6C06_95ED,
0x1B01_A57B,
0x8208_F4C1,
0xF50F_C457,
0x65B0_D9C6,
0x12B7_E950,
0x8BBE_B8EA,
0xFCB9_887C,
0x62DD_1DDF,
0x15DA_2D49,
0x8CD3_7CF3,
0xFBD4_4C65,
0x4DB2_6158,
0x3AB5_51CE,
0xA3BC_0074,
0xD4BB_30E2,
0x4ADF_A541,
0x3DD8_95D7,
0xA4D1_C46D,
0xD3D6_F4FB,
0x4369_E96A,
0x346E_D9FC,
0xAD67_8846,
0xDA60_B8D0,
0x4404_2D73,
0x3303_1DE5,
0xAA0A_4C5F,
0xDD0D_7CC9,
0x5005_713C,
0x2702_41AA,
0xBE0B_1010,
0xC90C_2086,
0x5768_B525,
0x206F_85B3,
0xB966_D409,
0xCE61_E49F,
0x5EDE_F90E,
0x29D9_C998,
0xB0D0_9822,
0xC7D7_A8B4,
0x59B3_3D17,
0x2EB4_0D81,
0xB7BD_5C3B,
0xC0BA_6CAD,
0xEDB8_8320,
0x9ABF_B3B6,
0x03B6_E20C,
0x74B1_D29A,
0xEAD5_4739,
0x9DD2_77AF,
0x04DB_2615,
0x73DC_1683,
0xE363_0B12,
0x9464_3B84,
0x0D6D_6A3E,
0x7A6A_5AA8,
0xE40E_CF0B,
0x9309_FF9D,
0x0A00_AE27,
0x7D07_9EB1,
0xF00F_9344,
0x8708_A3D2,
0x1E01_F268,
0x6906_C2FE,
0xF762_575D,
0x8065_67CB,
0x196C_3671,
0x6E6B_06E7,
0xFED4_1B76,
0x89D3_2BE0,
0x10DA_7A5A,
0x67DD_4ACC,
0xF9B9_DF6F,
0x8EBE_EFF9,
0x17B7_BE43,
0x60B0_8ED5,
0xD6D6_A3E8,
0xA1D1_937E,
0x38D8_C2C4,
0x4FDF_F252,
0xD1BB_67F1,
0xA6BC_5767,
0x3FB5_06DD,
0x48B2_364B,
0xD80D_2BDA,
0xAF0A_1B4C,
0x3603_4AF6,
0x4104_7A60,
0xDF60_EFC3,
0xA867_DF55,
0x316E_8EEF,
0x4669_BE79,
0xCB61_B38C,
0xBC66_831A,
0x256F_D2A0,
0x5268_E236,
0xCC0C_7795,
0xBB0B_4703,
0x2202_16B9,
0x5505_262F,
0xC5BA_3BBE,
0xB2BD_0B28,
0x2BB4_5A92,
0x5CB3_6A04,
0xC2D7_FFA7,
0xB5D0_CF31,
0x2CD9_9E8B,
0x5BDE_AE1D,
0x9B64_C2B0,
0xEC63_F226,
0x756A_A39C,
0x026D_930A,
0x9C09_06A9,
0xEB0E_363F,
0x7207_6785,
0x0500_5713,
0x95BF_4A82,
0xE2B8_7A14,
0x7BB1_2BAE,
0x0CB6_1B38,
0x92D2_8E9B,
0xE5D5_BE0D,
0x7CDC_EFB7,
0x0BDB_DF21,
0x86D3_D2D4,
0xF1D4_E242,
0x68DD_B3F8,
0x1FDA_836E,
0x81BE_16CD,
0xF6B9_265B,
0x6FB0_77E1,
0x18B7_4777,
0x8808_5AE6,
0xFF0F_6A70,
0x6606_3BCA,
0x1101_0B5C,
0x8F65_9EFF,
0xF862_AE69,
0x616B_FFD3,
0x166C_CF45,
0xA00A_E278,
0xD70D_D2EE,
0x4E04_8354,
0x3903_B3C2,
0xA767_2661,
0xD060_16F7,
0x4969_474D,
0x3E6E_77DB,
0xAED1_6A4A,
0xD9D6_5ADC,
0x40DF_0B66,
0x37D8_3BF0,
0xA9BC_AE53,
0xDEBB_9EC5,
0x47B2_CF7F,
0x30B5_FFE9,
0xBDBD_F21C,
0xCABA_C28A,
0x53B3_9330,
0x24B4_A3A6,
0xBAD0_3605,
0xCDD7_0693,
0x54DE_5729,
0x23D9_67BF,
0xB366_7A2E,
0xC461_4AB8,
0x5D68_1B02,
0x2A6F_2B94,
0xB40B_BE37,
0xC30C_8EA1,
0x5A05_DF1B,
0x2D02_EF8D,
];
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub struct ETH_FSC(pub u32);
impl ETH_FSC {
pub const CRC32_OK: u32 = 0x2144_df1c;
#[must_use]
pub fn new(data: &[u8]) -> Self {
let fsc = data.iter().fold(u32::MAX, |crc, byte| {
let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
}) ^ u32::MAX;
Self(fsc)
}
#[must_use]
pub fn update(self, data: &[u8]) -> Self {
let fsc = data.iter().fold(self.0 ^ u32::MAX, |crc, byte| {
let idx = u8::try_from(crc & 0xFF).unwrap() ^ byte;
CRC32R_LOOKUP_TABLE[usize::from(idx)] ^ (crc >> 8)
}) ^ u32::MAX;
Self(fsc)
}
#[must_use]
pub fn crc_ok(&self) -> bool {
self.0 == Self::CRC32_OK
}
#[must_use]
pub fn hton_bytes(&self) -> [u8; 4] {
self.0.to_le_bytes()
}
#[must_use]
pub fn hton(&self) -> u32 {
self.0.to_le()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn crc32_ethernet_frame() {
let packet_a = &[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xff, 0x06, 0x00, 0x01, 0x08, 0x00,
0x06, 0x04, 0x00, 0x01, 0x00, 0xe0, 0x4c, 0x68, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xc0, 0xa8, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x65, 0x90, 0x3d,
];
let packet_b = &[
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
];
// Packet A
let own_crc = ETH_FSC::new(&packet_a[0..60]);
let crc_bytes = own_crc.hton_bytes();
println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
assert_eq!(&crc_bytes, &packet_a[60..64]);
let own_crc = ETH_FSC::new(packet_a);
println!("{:08x}", own_crc.0);
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
// Packet B
let own_crc = ETH_FSC::new(&packet_b[0..60]);
let crc_bytes = own_crc.hton_bytes();
println!("{:08x} {:02x?}", own_crc.0, crc_bytes);
assert_eq!(&crc_bytes, &packet_b[60..64]);
let own_crc = ETH_FSC::new(packet_b);
println!("{:08x}", own_crc.0);
assert_eq!(own_crc.0, ETH_FSC::CRC32_OK);
}
#[test]
fn crc32_update() {
let full_data = &[
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0x00, 0xe0, 0x4c, 0x68, 0xee, 0xee, 0xdd, 0x06, 0x00, 0x01, 0x08, 0x00,
0x06, 0x04, 0x00, 0x02, 0x00, 0xe0, 0x4c, 0x68, 0x09, 0xde, 0xc0, 0xa8, 0x01, 0x02, 0x12, 0x34, 0x56, 0x78,
0x9a, 0xbc, 0xc0, 0xa8, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x3d, 0x67, 0x7c,
];
let (part_a, part_b) = full_data.split_at(16);
let crc_partially = ETH_FSC::new(part_a).update(part_b);
let crc_full = ETH_FSC::new(full_data);
assert_eq!(crc_full.0, crc_partially.0);
}
}

View File

@ -0,0 +1,53 @@
/// CRC-8/ITU
const CRC8X_TABLE: [u8; 256] = [
0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e,
0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb,
0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8,
0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6,
0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d,
0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50,
0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95,
0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec,
0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f,
0x58, 0x4d, 0x4a, 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a,
0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e,
0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7,
0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc,
0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3,
];
/// Calculate the crc of a pease of data.
pub fn crc8(data: &[u8]) -> u8 {
data.iter().fold(0, |crc, &byte| CRC8X_TABLE[usize::from(byte ^ crc)])
}
#[cfg(test)]
mod tests {
use ::crc::{Crc, CRC_8_SMBUS};
use super::crc8;
#[test]
fn spi_header_crc8() {
let data = &[0x80, 0x00];
let c = Crc::<u8>::new(&CRC_8_SMBUS);
let mut dig = c.digest();
dig.update(data);
let sw_crc = dig.finalize();
let own_crc = crc8(data);
assert_eq!(own_crc, sw_crc);
assert_eq!(own_crc, 182);
let data = &[0x80, 0x01];
let mut dig = c.digest();
dig.update(data);
let sw_crc = dig.finalize();
let own_crc = crc8(data);
assert_eq!(own_crc, sw_crc);
assert_eq!(own_crc, 177);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,175 @@
/// PHY Address: (0..=0x1F), 5-bits long.
#[allow(dead_code)]
type PhyAddr = u8;
/// PHY Register: (0..=0x1F), 5-bits long.
#[allow(dead_code)]
type RegC22 = u8;
/// PHY Register Clause 45.
#[allow(dead_code)]
type RegC45 = u16;
/// PHY Register Value
#[allow(dead_code)]
type RegVal = u16;
#[allow(dead_code)]
const REG13: RegC22 = 13;
#[allow(dead_code)]
const REG14: RegC22 = 14;
#[allow(dead_code)]
const PHYADDR_MASK: u8 = 0x1f;
#[allow(dead_code)]
const DEV_MASK: u8 = 0x1f;
#[allow(dead_code)]
#[repr(u16)]
enum Reg13Op {
Addr = 0b00 << 14,
Write = 0b01 << 14,
PostReadIncAddr = 0b10 << 14,
Read = 0b11 << 14,
}
/// `MdioBus` trait
/// Driver needs to implement the Clause 22
/// Optional Clause 45 is the device supports this.
///
/// Claus 45 methodes are bases on <https://www.ieee802.org/3/efm/public/nov02/oam/pannell_oam_1_1102.pdf>
pub trait MdioBus {
type Error;
/// Read, Clause 22
async fn read_cl22(&mut self, phy_id: PhyAddr, reg: RegC22) -> Result<RegVal, Self::Error>;
/// Write, Clause 22
async fn write_cl22(&mut self, phy_id: PhyAddr, reg: RegC22, reg_val: RegVal) -> Result<(), Self::Error>;
/// Read, Clause 45
/// This is the default implementation.
/// Many hardware these days support direct Clause 45 operations.
/// Implement this function when your hardware supports it.
async fn read_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45)) -> Result<RegVal, Self::Error> {
// Write FN
let val = (Reg13Op::Addr as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
self.write_cl22(phy_id, REG13, val).await?;
// Write Addr
self.write_cl22(phy_id, REG14, regc45.1).await?;
// Write FN
let val = (Reg13Op::Read as RegVal) | RegVal::from(regc45.0 & DEV_MASK);
self.write_cl22(phy_id, REG13, val).await?;
// Write Addr
self.read_cl22(phy_id, REG14).await
}
/// Write, Clause 45
/// This is the default implementation.
/// Many hardware these days support direct Clause 45 operations.
/// Implement this function when your hardware supports it.
async fn write_cl45(&mut self, phy_id: PhyAddr, regc45: (u8, RegC45), reg_val: RegVal) -> Result<(), Self::Error> {
let dev_addr = RegVal::from(regc45.0 & DEV_MASK);
let reg = regc45.1;
// Write FN
let val = (Reg13Op::Addr as RegVal) | dev_addr;
self.write_cl22(phy_id, REG13, val).await?;
// Write Addr
self.write_cl22(phy_id, REG14, reg).await?;
// Write FN
let val = (Reg13Op::Write as RegVal) | dev_addr;
self.write_cl22(phy_id, REG13, val).await?;
// Write Addr
self.write_cl22(phy_id, REG14, reg_val).await
}
}
// #[cfg(test)]
// mod tests {
// use core::convert::Infallible;
// use super::{MdioBus, PhyAddr, RegC22, RegVal};
// #[derive(Debug, PartialEq, Eq)]
// enum A {
// Read(PhyAddr, RegC22),
// Write(PhyAddr, RegC22, RegVal),
// }
// struct MockMdioBus(Vec<A>);
// impl MockMdioBus {
// pub fn clear(&mut self) {
// self.0.clear();
// }
// }
// impl MdioBus for MockMdioBus {
// type Error = Infallible;
// fn write_cl22(
// &mut self,
// phy_id: super::PhyAddr,
// reg: super::RegC22,
// reg_val: super::RegVal,
// ) -> Result<(), Self::Error> {
// self.0.push(A::Write(phy_id, reg, reg_val));
// Ok(())
// }
// fn read_cl22(
// &mut self,
// phy_id: super::PhyAddr,
// reg: super::RegC22,
// ) -> Result<super::RegVal, Self::Error> {
// self.0.push(A::Read(phy_id, reg));
// Ok(0)
// }
// }
// #[test]
// fn read_test() {
// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
// mdiobus.clear();
// mdiobus.read_cl22(0x01, 0x00).unwrap();
// assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]);
// mdiobus.clear();
// mdiobus.read_cl45(0x01, (0xBB, 0x1234)).unwrap();
// assert_eq!(
// mdiobus.0,
// vec![
// #[allow(clippy::identity_op)]
// A::Write(0x01, 13, (0b00 << 14) | 27),
// A::Write(0x01, 14, 0x1234),
// A::Write(0x01, 13, (0b11 << 14) | 27),
// A::Read(0x01, 14)
// ]
// );
// }
// #[test]
// fn write_test() {
// let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
// mdiobus.clear();
// mdiobus.write_cl22(0x01, 0x00, 0xABCD).unwrap();
// assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]);
// mdiobus.clear();
// mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).unwrap();
// assert_eq!(
// mdiobus.0,
// vec![
// A::Write(0x01, 13, 27),
// A::Write(0x01, 14, 0x1234),
// A::Write(0x01, 13, (0b01 << 14) | 27),
// A::Write(0x01, 14, 0xABCD)
// ]
// );
// }
// }

View File

@ -0,0 +1,142 @@
use crate::mdio::MdioBus;
#[allow(dead_code, non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u8)]
/// Clause 22 Registers
pub enum RegsC22 {
/// MII Control Register
CONTROL = 0x00,
/// MII Status Register
STATUS = 0x01,
/// PHY Identifier 1 Register
PHY_ID1 = 0x02,
/// PHY Identifier 2 Register.
PHY_ID2 = 0x03,
}
/// Clause 45 Registers
#[allow(non_snake_case, dead_code)]
pub mod RegsC45 {
/// Device Address: 0x01
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DA1 {
/// PMA/PMD Control 1 Register
PMA_PMD_CNTRL1 = 0x0000,
/// PMA/PMD Status 1 Register
PMA_PMD_STAT1 = 0x0001,
/// MSE Value Register
MSE_VAL = 0x830B,
}
impl DA1 {
#[must_use]
pub fn into(self) -> (u8, u16) {
(0x01, self as u16)
}
}
/// Device Address: 0x03
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DA3 {
/// PCS Control 1 Register
PCS_CNTRL1 = 0x0000,
/// PCS Status 1 Register
PCS_STAT1 = 0x0001,
/// PCS Status 2 Register
PCS_STAT2 = 0x0008,
}
impl DA3 {
#[must_use]
pub fn into(self) -> (u8, u16) {
(0x03, self as u16)
}
}
/// Device Address: 0x07
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DA7 {
/// Extra Autonegotiation Status Register
AN_STATUS_EXTRA = 0x8001,
}
impl DA7 {
#[must_use]
pub fn into(self) -> (u8, u16) {
(0x07, self as u16)
}
}
/// Device Address: 0x1E
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DA1E {
/// System Interrupt Status Register
CRSM_IRQ_STATUS = 0x0010,
/// System Interrupt Mask Register
CRSM_IRQ_MASK = 0x0020,
/// Pin Mux Configuration 1 Register
DIGIO_PINMUX = 0x8c56,
/// LED Control Register.
LED_CNTRL = 0x8C82,
/// LED Polarity Register
LED_POLARITY = 0x8C83,
}
impl DA1E {
#[must_use]
pub fn into(self) -> (u8, u16) {
(0x1e, self as u16)
}
}
/// Device Address: 0x1F
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[repr(u16)]
pub enum DA1F {
/// PHY Subsystem Interrupt Status Register
PHY_SYBSYS_IRQ_STATUS = 0x0011,
/// PHY Subsystem Interrupt Mask Register
PHY_SYBSYS_IRQ_MASK = 0x0021,
}
impl DA1F {
#[must_use]
pub fn into(self) -> (u8, u16) {
(0x1f, self as u16)
}
}
}
pub struct Phy10BaseT1x(u8);
impl Default for Phy10BaseT1x {
fn default() -> Self {
Self(0x01)
}
}
impl Phy10BaseT1x {
/// Get the both parts of the PHYID.
pub async fn get_id<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u32, MDE>
where
MDIOBUS: MdioBus<Error = MDE>,
MDE: core::fmt::Debug,
{
let mut phyid = u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID1 as u8).await?) << 16;
phyid |= u32::from(mdiobus.read_cl22(self.0, RegsC22::PHY_ID2 as u8).await?);
Ok(phyid)
}
/// Get the Mean Squared Error Value.
pub async fn get_sqi<MDIOBUS, MDE>(&self, mdiobus: &mut MDIOBUS) -> Result<u16, MDE>
where
MDIOBUS: MdioBus<Error = MDE>,
MDE: core::fmt::Debug,
{
mdiobus.read_cl45(self.0, RegsC45::DA1::MSE_VAL.into()).await
}
}

View File

@ -0,0 +1,408 @@
use bitfield::{bitfield, bitfield_bitrange, bitfield_fields};
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u16)]
/// SPI REGISTER DETAILS
/// Table 38.
pub enum SpiRegisters {
IDVER = 0x00,
PHYID = 0x01,
CAPABILITY = 0x02,
RESET = 0x03,
CONFIG0 = 0x04,
CONFIG2 = 0x06,
STATUS0 = 0x08,
STATUS1 = 0x09,
IMASK0 = 0x0C,
IMASK1 = 0x0D,
MDIO_ACC = 0x20,
TX_FSIZE = 0x30,
TX = 0x31,
TX_SPACE = 0x32,
FIFO_CLR = 0x36,
ADDR_FILT_UPR0 = 0x50,
ADDR_FILT_LWR0 = 0x51,
ADDR_FILT_UPR1 = 0x52,
ADDR_FILT_LWR1 = 0x53,
ADDR_MSK_LWR0 = 0x70,
ADDR_MSK_UPR0 = 0x71,
ADDR_MSK_LWR1 = 0x72,
ADDR_MSK_UPR1 = 0x73,
RX_FSIZE = 0x90,
RX = 0x91,
}
impl From<SpiRegisters> for u16 {
fn from(val: SpiRegisters) -> Self {
val as u16
}
}
impl From<u16> for SpiRegisters {
fn from(value: u16) -> Self {
match value {
0x00 => Self::IDVER,
0x01 => Self::PHYID,
0x02 => Self::CAPABILITY,
0x03 => Self::RESET,
0x04 => Self::CONFIG0,
0x06 => Self::CONFIG2,
0x08 => Self::STATUS0,
0x09 => Self::STATUS1,
0x0C => Self::IMASK0,
0x0D => Self::IMASK1,
0x20 => Self::MDIO_ACC,
0x30 => Self::TX_FSIZE,
0x31 => Self::TX,
0x32 => Self::TX_SPACE,
0x36 => Self::FIFO_CLR,
0x50 => Self::ADDR_FILT_UPR0,
0x51 => Self::ADDR_FILT_LWR0,
0x52 => Self::ADDR_FILT_UPR1,
0x53 => Self::ADDR_FILT_LWR1,
0x70 => Self::ADDR_MSK_LWR0,
0x71 => Self::ADDR_MSK_UPR0,
0x72 => Self::ADDR_MSK_LWR1,
0x73 => Self::ADDR_MSK_UPR1,
0x90 => Self::RX_FSIZE,
0x91 => Self::RX,
e => panic!("Unknown value {e}"),
}
}
}
// Register definitions
bitfield! {
/// Status0 Register bits
pub struct Status0(u32);
impl Debug;
u32;
/// Control Data Protection Error
pub cdpe, _ : 12;
/// Transmit Frame Check Squence Error
pub txfcse, _: 11;
/// Transmit Time Stamp Capture Available C
pub ttscac, _ : 10;
/// Transmit Time Stamp Capture Available B
pub ttscab, _ : 9;
/// Transmit Time Stamp Capture Available A
pub ttscaa, _ : 8;
/// PHY Interrupt for Port 1
pub phyint, _ : 7;
/// Reset Complete
pub resetc, _ : 6;
/// Header error
pub hdre, _ : 5;
/// Loss of Frame Error
pub lofe, _ : 4;
/// Receiver Buffer Overflow Error
pub rxboe, _ : 3;
/// Host Tx FIFO Under Run Error
pub txbue, _ : 2;
/// Host Tx FIFO Overflow
pub txboe, _ : 1;
/// Transmit Protocol Error
pub txpe, _ : 0;
}
bitfield! {
/// Status1 Register bits
pub struct Status1(u32);
impl Debug;
u32;
/// ECC Error on Reading the Frame Size from a Tx FIFO
pub tx_ecc_err, set_tx_ecc_err: 12;
/// ECC Error on Reading the Frame Size from an Rx FIFO
pub rx_ecc_err, set_rx_ecc_err : 11;
/// Detected an Error on an SPI Transaction
pub spi_err, set_spi_err: 10;
/// Rx MAC Interframe Gap Error
pub p1_rx_ifg_err, set_p1_rx_ifg_err : 8;
/// Port1 Rx Ready High Priority
pub p1_rx_rdy_hi, set_p1_rx_rdy_hi : 5;
/// Port 1 Rx FIFO Contains Data
pub p1_rx_rdy, set_p1_rx_rdy : 4;
/// Tx Ready
pub tx_rdy, set_tx_rdy : 3;
/// Link Status Changed
pub link_change, set_link_change : 1;
/// Port 1 Link Status
pub p1_link_status, _ : 0;
}
bitfield! {
/// Config0 Register bits
pub struct Config0(u32);
impl Debug;
u32;
/// Configuration Synchronization
pub sync, set_sync : 15;
/// Transmit Frame Check Sequence Validation Enable
pub txfcsve, set_txfcsve : 14;
/// !CS Align Receive Frame Enable
pub csarfe, set_csarfe : 13;
/// Zero Align Receive Frame Enable
pub zarfe, set_zarfe : 12;
/// Transmit Credit Threshold
pub tcxthresh, set_tcxthresh : 11, 10;
/// Transmit Cut Through Enable
pub txcte, set_txcte : 9;
/// Receive Cut Through Enable
pub rxcte, set_rxcte : 8;
/// Frame Time Stamp Enable
pub ftse, set_ftse : 7;
/// Receive Frame Time Stamp Select
pub ftss, set_ftss : 6;
/// Enable Control Data Read Write Protection
pub prote, set_prote : 5;
/// Enable TX Data Chunk Sequence and Retry
pub seqe, set_seqe : 4;
/// Chunk Payload Selector (N).
pub cps, set_cps : 2, 0;
}
bitfield! {
/// Config2 Register bits
pub struct Config2(u32);
impl Debug;
u32;
/// Assert TX_RDY When the Tx FIFO is Empty
pub tx_rdy_on_empty, set_tx_rdy_on_empty : 8;
/// Determines If the SFD is Detected in the PHY or MAC
pub sdf_detect_src, set_sdf_detect_src : 7;
/// Statistics Clear on Reading
pub stats_clr_on_rd, set_stats_clr_on_rd : 6;
/// Enable CRC Append
pub crc_append, set_crc_append : 5;
/// Admit Frames with IFG Errors on Port 1 (P1)
pub p1_rcv_ifg_err_frm, set_p1_rcv_ifg_err_frm : 4;
/// Forward Frames Not Matching Any MAC Address to the Host
pub p1_fwd_unk2host, set_p1_fwd_unk2host : 2;
/// SPI to MDIO Bridge MDC Clock Speed
pub mspeed, set_mspeed : 0;
}
bitfield! {
/// IMASK0 Register bits
pub struct IMask0(u32);
impl Debug;
u32;
/// Control Data Protection Error Mask
pub cppem, set_cppem : 12;
/// Transmit Frame Check Sequence Error Mask
pub txfcsem, set_txfcsem : 11;
/// Transmit Time Stamp Capture Available C Mask
pub ttscacm, set_ttscacm : 10;
/// Transmit Time Stamp Capture Available B Mask
pub ttscabm, set_ttscabm : 9;
/// Transmit Time Stamp Capture Available A Mask
pub ttscaam, set_ttscaam : 8;
/// Physical Layer Interrupt Mask
pub phyintm, set_phyintm : 7;
/// RESET Complete Mask
pub resetcm, set_resetcm : 6;
/// Header Error Mask
pub hdrem, set_hdrem : 5;
/// Loss of Frame Error Mask
pub lofem, set_lofem : 4;
/// Receive Buffer Overflow Error Mask
pub rxboem, set_rxboem : 3;
/// Transmit Buffer Underflow Error Mask
pub txbuem, set_txbuem : 2;
/// Transmit Buffer Overflow Error Mask
pub txboem, set_txboem : 1;
/// Transmit Protocol Error Mask
pub txpem, set_txpem : 0;
}
bitfield! {
/// IMASK1 Register bits
pub struct IMask1(u32);
impl Debug;
u32;
/// Mask Bit for TXF_ECC_ERR
pub tx_ecc_err_mask, set_tx_ecc_err_mask : 12;
/// Mask Bit for RXF_ECC_ERR
pub rx_ecc_err_mask, set_rx_ecc_err_mask : 11;
/// Mask Bit for SPI_ERR
/// This field is only used with the generic SPI protocol
pub spi_err_mask, set_spi_err_mask : 10;
/// Mask Bit for RX_IFG_ERR
pub p1_rx_ifg_err_mask, set_p1_rx_ifg_err_mask : 8;
/// Mask Bit for P1_RX_RDY
/// This field is only used with the generic SPI protocol
pub p1_rx_rdy_mask, set_p1_rx_rdy_mask : 4;
/// Mask Bit for TX_FRM_DONE
/// This field is only used with the generic SPI protocol
pub tx_rdy_mask, set_tx_rdy_mask : 3;
/// Mask Bit for LINK_CHANGE
pub link_change_mask, set_link_change_mask : 1;
}
/// LED Functions
#[repr(u8)]
pub enum LedFunc {
LinkupTxRxActicity = 0,
LinkupTxActicity,
LinkupRxActicity,
LinkupOnly,
TxRxActivity,
TxActivity,
RxActivity,
LinkupRxEr,
LinkupRxTxEr,
RxEr,
RxTxEr,
TxSop,
RxSop,
On,
Off,
Blink,
TxLevel2P4,
TxLevel1P0,
Master,
Slave,
IncompatiableLinkCfg,
AnLinkGood,
AnComplete,
TsTimer,
LocRcvrStatus,
RemRcvrStatus,
Clk25Ref,
TxTCLK,
Clk120MHz,
}
impl From<LedFunc> for u8 {
fn from(val: LedFunc) -> Self {
val as u8
}
}
impl From<u8> for LedFunc {
fn from(value: u8) -> Self {
match value {
0 => LedFunc::LinkupTxRxActicity,
1 => LedFunc::LinkupTxActicity,
2 => LedFunc::LinkupRxActicity,
3 => LedFunc::LinkupOnly,
4 => LedFunc::TxRxActivity,
5 => LedFunc::TxActivity,
6 => LedFunc::RxActivity,
7 => LedFunc::LinkupRxEr,
8 => LedFunc::LinkupRxTxEr,
9 => LedFunc::RxEr,
10 => LedFunc::RxTxEr,
11 => LedFunc::TxSop,
12 => LedFunc::RxSop,
13 => LedFunc::On,
14 => LedFunc::Off,
15 => LedFunc::Blink,
16 => LedFunc::TxLevel2P4,
17 => LedFunc::TxLevel1P0,
18 => LedFunc::Master,
19 => LedFunc::Slave,
20 => LedFunc::IncompatiableLinkCfg,
21 => LedFunc::AnLinkGood,
22 => LedFunc::AnComplete,
23 => LedFunc::TsTimer,
24 => LedFunc::LocRcvrStatus,
25 => LedFunc::RemRcvrStatus,
26 => LedFunc::Clk25Ref,
27 => LedFunc::TxTCLK,
28 => LedFunc::Clk120MHz,
e => panic!("Invalid value {e}"),
}
}
}
/// LED Control Register
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct LedCntrl(pub u16);
bitfield_bitrange! {struct LedCntrl(u16)}
impl LedCntrl {
bitfield_fields! {
u8;
/// LED 0 Pin Function
pub from into LedFunc, led0_function, set_led0_function: 4, 0;
/// LED 0 Mode Selection
pub led0_mode, set_led0_mode: 5;
/// Qualify Certain LED 0 Options with Link Status.
pub led0_link_st_qualify, set_led0_link_st_qualify: 6;
/// LED 0 Enable
pub led0_en, set_led0_en: 7;
/// LED 1 Pin Function
pub from into LedFunc, led1_function, set_led1_function: 12, 8;
/// /// LED 1 Mode Selection
pub led1_mode, set_led1_mode: 13;
/// Qualify Certain LED 1 Options with Link Status.
pub led1_link_st_qualify, set_led1_link_st_qualify: 14;
/// LED 1 Enable
pub led1_en, set_led1_en: 15;
}
pub fn new() -> Self {
LedCntrl(0)
}
}
// LED Polarity
#[repr(u8)]
pub enum LedPol {
AutoSense = 0,
ActiveHigh,
ActiveLow,
}
impl From<LedPol> for u8 {
fn from(val: LedPol) -> Self {
val as u8
}
}
impl From<u8> for LedPol {
fn from(value: u8) -> Self {
match value {
0 => LedPol::AutoSense,
1 => LedPol::ActiveHigh,
2 => LedPol::ActiveLow,
e => panic!("Invalid value {e}"),
}
}
}
/// LED Control Register
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct LedPolarity(pub u16);
bitfield_bitrange! {struct LedPolarity(u16)}
impl LedPolarity {
bitfield_fields! {
u8;
/// LED 1 Polarity
pub from into LedPol, led1_polarity, set_led1_polarity: 3, 2;
/// LED 0 Polarity
pub from into LedPol, led0_polarity, set_led0_polarity: 1, 0;
}
}
/// SPI Header
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct SpiHeader(pub u16);
bitfield_bitrange! {struct SpiHeader(u16)}
impl SpiHeader {
bitfield_fields! {
u16;
/// Mask Bit for TXF_ECC_ERR
pub control, set_control : 15;
pub full_duplex, set_full_duplex : 14;
/// Read or Write to register
pub write, set_write : 13;
/// Registers ID/addr
pub from into SpiRegisters, addr, set_addr: 11, 0;
}
}

View File

@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
- [`embassy-usb`](https://github.com/embassy-rs/embassy/tree/main/embassy-usb) for Ethernet-over-USB (CDC NCM) support.
- [`embassy-net-w5500`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-w5500) for Wiznet W5500 SPI Ethernet MAC+PHY chip.
- [`embassy-net-wiznet`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-wiznet) for Wiznet SPI Ethernet MAC+PHY chips.
- [`embassy-net-esp-hosted`](https://github.com/embassy-rs/embassy/tree/main/embassy-net-esp-hosted) for using ESP32 chips with the [`esp-hosted`](https://github.com/espressif/esp-hosted) firmware as WiFi adapters for another non-ESP32 MCU.

View File

@ -8,6 +8,7 @@ use core::cell::RefCell;
use core::mem::MaybeUninit;
use core::task::{Context, Poll};
use driver::HardwareAddress;
pub use embassy_net_driver as driver;
use embassy_net_driver::{Capabilities, LinkState, Medium};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
@ -42,7 +43,7 @@ struct StateInner<'d, const MTU: usize> {
struct Shared {
link_state: LinkState,
waker: WakerRegistration,
ethernet_address: [u8; 6],
hardware_address: driver::HardwareAddress,
}
pub struct Runner<'d, const MTU: usize> {
@ -73,6 +74,18 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
)
}
pub fn borrow_split(&mut self) -> (StateRunner<'_>, RxRunner<'_, MTU>, TxRunner<'_, MTU>) {
(
StateRunner { shared: self.shared },
RxRunner {
rx_chan: self.rx_chan.borrow(),
},
TxRunner {
tx_chan: self.tx_chan.borrow(),
},
)
}
pub fn state_runner(&self) -> StateRunner<'d> {
StateRunner { shared: self.shared }
}
@ -85,10 +98,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
});
}
pub fn set_ethernet_address(&mut self, address: [u8; 6]) {
pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.ethernet_address = address;
s.hardware_address = address;
s.waker.wake();
});
}
@ -150,7 +163,15 @@ impl<'d> StateRunner<'d> {
pub fn set_ethernet_address(&self, address: [u8; 6]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.ethernet_address = address;
s.hardware_address = driver::HardwareAddress::Ethernet(address);
s.waker.wake();
});
}
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
self.shared.lock(|s| {
let s = &mut *s.borrow_mut();
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
s.waker.wake();
});
}
@ -206,11 +227,15 @@ impl<'d, const MTU: usize> TxRunner<'d, MTU> {
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
state: &'d mut State<MTU, N_RX, N_TX>,
ethernet_address: [u8; 6],
hardware_address: driver::HardwareAddress,
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.medium = Medium::Ethernet;
caps.medium = match &hardware_address {
HardwareAddress::Ethernet(_) => Medium::Ethernet,
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
HardwareAddress::Ip => Medium::Ip,
};
// safety: this is a self-referential struct, however:
// - it can't move while the `'d` borrow is active.
@ -222,7 +247,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
shared: Mutex::new(RefCell::new(Shared {
link_state: LinkState::Down,
ethernet_address,
hardware_address,
waker: WakerRegistration::new(),
})),
});
@ -289,8 +314,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
self.caps.clone()
}
fn ethernet_address(&self) -> [u8; 6] {
self.shared.lock(|s| s.borrow().ethernet_address)
fn hardware_address(&self) -> driver::HardwareAddress {
self.shared.lock(|s| s.borrow().hardware_address)
}
fn link_state(&mut self, cx: &mut Context) -> LinkState {

View File

@ -21,4 +21,4 @@ target = "thumbv7em-none-eabi"
features = ["defmt"]
[dependencies]
defmt = { version = "0.3", optional = true }
defmt = { version = "0.3", optional = true }

View File

@ -4,6 +4,18 @@
use core::task::Context;
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HardwareAddress {
/// A six-octet Ethernet address
Ethernet([u8; 6]),
/// An eight-octet IEEE802.15.4 address
Ieee802154([u8; 8]),
/// Indicates that a Driver is IP-native, and has no hardware address
Ip,
}
/// Main `embassy-net` driver API.
///
/// This is essentially an interface for sending and receiving raw network frames.
@ -51,8 +63,8 @@ pub trait Driver {
/// Get a description of device capabilities.
fn capabilities(&self) -> Capabilities;
/// Get the device's Ethernet address.
fn ethernet_address(&self) -> [u8; 6];
/// Get the device's hardware address.
fn hardware_address(&self) -> HardwareAddress;
}
impl<T: ?Sized + Driver> Driver for &mut T {
@ -75,8 +87,8 @@ impl<T: ?Sized + Driver> Driver for &mut T {
fn link_state(&mut self, cx: &mut Context) -> LinkState {
T::link_state(self, cx)
}
fn ethernet_address(&self) -> [u8; 6] {
T::ethernet_address(self)
fn hardware_address(&self) -> HardwareAddress {
T::hardware_address(self)
}
}
@ -164,6 +176,9 @@ pub enum Medium {
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
Ip,
/// IEEE 802_15_4 medium
Ieee802154,
}
impl Default for Medium {

View File

@ -0,0 +1,23 @@
[package]
name = "embassy-net-enc28j60"
version = "0.1.0"
description = "embassy-net driver for the ENC28J60 ethernet chip"
keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
target = "thumbv7em-none-eabi"

View File

@ -0,0 +1,19 @@
# `embassy-net-enc28j60`
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -0,0 +1,69 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
ERDPTL = 0x00,
ERDPTH = 0x01,
EWRPTL = 0x02,
EWRPTH = 0x03,
ETXSTL = 0x04,
ETXSTH = 0x05,
ETXNDL = 0x06,
ETXNDH = 0x07,
ERXSTL = 0x08,
ERXSTH = 0x09,
ERXNDL = 0x0a,
ERXNDH = 0x0b,
ERXRDPTL = 0x0c,
ERXRDPTH = 0x0d,
ERXWRPTL = 0x0e,
ERXWRPTH = 0x0f,
EDMASTL = 0x10,
EDMASTH = 0x11,
EDMANDL = 0x12,
EDMANDH = 0x13,
EDMADSTL = 0x14,
EDMADSTH = 0x15,
EDMACSL = 0x16,
EDMACSH = 0x17,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::ERDPTL => true,
Register::ERDPTH => true,
Register::EWRPTL => true,
Register::EWRPTH => true,
Register::ETXSTL => true,
Register::ETXSTH => true,
Register::ETXNDL => true,
Register::ETXNDH => true,
Register::ERXSTL => true,
Register::ERXSTH => true,
Register::ERXNDL => true,
Register::ERXNDH => true,
Register::ERXRDPTL => true,
Register::ERXRDPTH => true,
Register::ERXWRPTL => true,
Register::ERXWRPTH => true,
Register::EDMASTL => true,
Register::EDMASTH => true,
Register::EDMANDL => true,
Register::EDMANDH => true,
Register::EDMADSTL => true,
Register::EDMADSTH => true,
Register::EDMACSL => true,
Register::EDMACSH => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank0(self)
}
}

View File

@ -0,0 +1,84 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
EHT0 = 0x00,
EHT1 = 0x01,
EHT2 = 0x02,
EHT3 = 0x03,
EHT4 = 0x04,
EHT5 = 0x05,
EHT6 = 0x06,
EHT7 = 0x07,
EPMM0 = 0x08,
EPMM1 = 0x09,
EPMM2 = 0x0a,
EPMM3 = 0x0b,
EPMM4 = 0x0c,
EPMM5 = 0x0d,
EPMM6 = 0x0e,
EPMM7 = 0x0f,
EPMCSL = 0x10,
EPMCSH = 0x11,
EPMOL = 0x14,
EPMOH = 0x15,
ERXFCON = 0x18,
EPKTCNT = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::EHT0 => true,
Register::EHT1 => true,
Register::EHT2 => true,
Register::EHT3 => true,
Register::EHT4 => true,
Register::EHT5 => true,
Register::EHT6 => true,
Register::EHT7 => true,
Register::EPMM0 => true,
Register::EPMM1 => true,
Register::EPMM2 => true,
Register::EPMM3 => true,
Register::EPMM4 => true,
Register::EPMM5 => true,
Register::EPMM6 => true,
Register::EPMM7 => true,
Register::EPMCSL => true,
Register::EPMCSH => true,
Register::EPMOL => true,
Register::EPMOH => true,
Register::ERXFCON => true,
Register::EPKTCNT => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank1(self)
}
}
register!(ERXFCON, 0b1010_0001, u8, {
#[doc = "Broadcast Filter Enable bit"]
bcen @ 0,
#[doc = "Multicast Filter Enable bit"]
mcen @ 1,
#[doc = "Hash Table Filter Enable bit"]
hten @ 2,
#[doc = "Magic Packet™ Filter Enable bit"]
mpen @ 3,
#[doc = "Pattern Match Filter Enable bit"]
pmen @ 4,
#[doc = "Post-Filter CRC Check Enable bit"]
crcen @ 5,
#[doc = "AND/OR Filter Select bit"]
andor @ 6,
#[doc = "Unicast Filter Enable bit"]
ucen @ 7,
});

View File

@ -0,0 +1,86 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
MACON1 = 0x00,
MACON3 = 0x02,
MACON4 = 0x03,
MABBIPG = 0x04,
MAIPGL = 0x06,
MAIPGH = 0x07,
MACLCON1 = 0x08,
MACLCON2 = 0x09,
MAMXFLL = 0x0a,
MAMXFLH = 0x0b,
MICMD = 0x12,
MIREGADR = 0x14,
MIWRL = 0x16,
MIWRH = 0x17,
MIRDL = 0x18,
MIRDH = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::MACON1 => false,
Register::MACON3 => false,
Register::MACON4 => false,
Register::MABBIPG => false,
Register::MAIPGL => false,
Register::MAIPGH => false,
Register::MACLCON1 => false,
Register::MACLCON2 => false,
Register::MAMXFLL => false,
Register::MAMXFLH => false,
Register::MICMD => false,
Register::MIREGADR => false,
Register::MIWRL => false,
Register::MIWRH => false,
Register::MIRDL => false,
Register::MIRDH => false,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank2(self)
}
}
register!(MACON1, 0, u8, {
#[doc = "Enable packets to be received by the MAC"]
marxen @ 0,
#[doc = "Control frames will be discarded after being processed by the MAC"]
passall @ 1,
#[doc = "Inhibit transmissions when pause control frames are received"]
rxpaus @ 2,
#[doc = "Allow the MAC to transmit pause control frames"]
txpaus @ 3,
});
register!(MACON3, 0, u8, {
#[doc = "MAC will operate in Full-Duplex mode"]
fuldpx @ 0,
#[doc = "The type/length field of transmitted and received frames will be checked"]
frmlnen @ 1,
#[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
hfrmen @ 2,
#[doc = "No proprietary header is present"]
phdren @ 3,
#[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
txcrcen @ 4,
#[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
padcfg @ 5..7,
});
register!(MICMD, 0, u8, {
#[doc = "MII Read Enable bit"]
miird @ 0,
#[doc = "MII Scan Enable bit"]
miiscan @ 1,
});

View File

@ -0,0 +1,53 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
MAADR5 = 0x00,
MAADR6 = 0x01,
MAADR3 = 0x02,
MAADR4 = 0x03,
MAADR1 = 0x04,
MAADR2 = 0x05,
EBSTSD = 0x06,
EBSTCON = 0x07,
EBSTCSL = 0x08,
EBSTCSH = 0x09,
MISTAT = 0x0a,
EREVID = 0x12,
ECOCON = 0x15,
EFLOCON = 0x17,
EPAUSL = 0x18,
EPAUSH = 0x19,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::MAADR5 => false,
Register::MAADR6 => false,
Register::MAADR3 => false,
Register::MAADR4 => false,
Register::MAADR1 => false,
Register::MAADR2 => false,
Register::EBSTSD => true,
Register::EBSTCON => true,
Register::EBSTCSL => true,
Register::EBSTCSH => true,
Register::MISTAT => false,
Register::EREVID => true,
Register::ECOCON => true,
Register::EFLOCON => true,
Register::EPAUSL => true,
Register::EPAUSH => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Bank3(self)
}
}

View File

@ -0,0 +1,106 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
ECON1 = 0x1f,
ECON2 = 0x1e,
EIE = 0x1b,
EIR = 0x1c,
ESTAT = 0x1d,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
pub(crate) fn is_eth_register(&self) -> bool {
match *self {
Register::ECON1 => true,
Register::ECON2 => true,
Register::EIE => true,
Register::EIR => true,
Register::ESTAT => true,
}
}
}
impl Into<super::Register> for Register {
fn into(self) -> super::Register {
super::Register::Common(self)
}
}
register!(EIE, 0, u8, {
#[doc = "Receive Error Interrupt Enable bit"]
rxerie @ 0,
#[doc = "Transmit Error Interrupt Enable bit"]
txerie @ 1,
#[doc = "Transmit Enable bit"]
txie @ 3,
#[doc = "Link Status Change Interrupt Enable bit"]
linkie @ 4,
#[doc = "DMA Interrupt Enable bit"]
dmaie @ 5,
#[doc = "Receive Packet Pending Interrupt Enable bit"]
pktie @ 6,
#[doc = "Global INT Interrupt Enable bit"]
intie @ 7,
});
register!(EIR, 0, u8, {
#[doc = "Receive Error Interrupt Flag bit"]
rxerif @ 0,
#[doc = "Transmit Error Interrupt Flag bit"]
txerif @ 1,
#[doc = "Transmit Interrupt Flag bit"]
txif @ 3,
#[doc = "Link Change Interrupt Flag bit"]
linkif @ 4,
#[doc = "DMA Interrupt Flag bit"]
dmaif @ 5,
#[doc = "Receive Packet Pending Interrupt Flag bit"]
pktif @ 6,
});
register!(ESTAT, 0, u8, {
#[doc = "Clock Ready bit"]
clkrdy @ 0,
#[doc = "Transmit Abort Error bit"]
txabrt @ 1,
#[doc = "Receive Busy bit"]
rxbusy @ 2,
#[doc = "Late Collision Error bit"]
latecol @ 4,
#[doc = "Ethernet Buffer Error Status bit"]
bufer @ 6,
#[doc = "INT Interrupt Flag bit"]
int @ 7,
});
register!(ECON2, 0b1000_0000, u8, {
#[doc = "Voltage Regulator Power Save Enable bit"]
vrps @ 3,
#[doc = "Power Save Enable bit"]
pwrsv @ 5,
#[doc = "Packet Decrement bit"]
pktdec @ 6,
#[doc = "Automatic Buffer Pointer Increment Enable bit"]
autoinc @ 7,
});
register!(ECON1, 0, u8, {
#[doc = "Bank Select bits"]
bsel @ 0..1,
#[doc = "Receive Enable bi"]
rxen @ 2,
#[doc = "Transmit Request to Send bit"]
txrts @ 3,
#[doc = "DMA Checksum Enable bit"]
csumen @ 4,
#[doc = "DMA Start and Busy Status bit"]
dmast @ 5,
#[doc = "Receive Logic Reset bit"]
rxrst @ 6,
#[doc = "Transmit Logic Reset bit"]
txrst @ 7,
});

View File

@ -0,0 +1,225 @@
#![macro_use]
#![allow(unused_macros)]
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
macro_rules! unreachable {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::unreachable!($($x)*);
#[cfg(feature = "defmt")]
::defmt::unreachable!($($x)*);
}
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}

View File

@ -0,0 +1,30 @@
register!(RxStatus, 0, u32, {
#[doc = "Indicates length of the received frame"]
byte_count @ 0..15,
#[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
long_event @ 16,
#[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
carrier_event @ 18,
#[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
crc_error @ 20,
#[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
length_check_error @ 21,
#[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
length_out_of_range @ 22,
#[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
received_ok @ 23,
#[doc = "Indicates packet received had a valid Multicast address"]
multicast @ 24,
#[doc = "Indicates packet received had a valid Broadcast address."]
broadcast @ 25,
#[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
dribble_nibble @ 26,
#[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
receive_control_frame @ 27,
#[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
receive_pause_control_frame @ 28,
#[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
receive_unknown_opcode @ 29,
#[doc = "Current frame was recognized as a VLAN tagged frame"]
receive_vlan_type_detected @ 30,
});

View File

@ -0,0 +1,717 @@
#![no_std]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
// must go first.
mod fmt;
#[macro_use]
mod macros;
mod bank0;
mod bank1;
mod bank2;
mod bank3;
mod common;
mod header;
mod phy;
mod traits;
use core::cmp;
use core::convert::TryInto;
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
use embassy_time::Duration;
use embedded_hal::digital::OutputPin;
use embedded_hal::spi::{Operation, SpiDevice};
use traits::U16Ext;
// Total buffer size (see section 3.2)
const BUF_SZ: u16 = 8 * 1024;
// Maximum frame length
const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
// Size of the Frame check sequence (32-bit CRC)
const CRC_SZ: u16 = 4;
// define the boundaries of the TX and RX buffers
// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
// says: we place the RX buffer at address 0 and the TX buffer after it
const RXST: u16 = 0x0000;
const RXND: u16 = 0x19ff;
const TXST: u16 = 0x1a00;
const _TXND: u16 = 0x1fff;
const MTU: usize = 1514; // 1500 IP + 14 ethernet header
/// ENC28J60 embassy-net driver
pub struct Enc28j60<S, O> {
mac_addr: [u8; 6],
spi: S,
rst: Option<O>,
bank: Bank,
// address of the next packet in buffer memory
next_packet: u16,
}
impl<S, O> Enc28j60<S, O>
where
S: SpiDevice,
O: OutputPin,
{
/// Create a new ENC28J60 driver instance.
///
/// The RST pin is optional. If None, reset will be done with a SPI
/// soft reset command, instead of via the RST pin.
pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
let mut res = Self {
mac_addr,
spi,
rst,
bank: Bank::Bank0,
next_packet: RXST,
};
res.init();
res
}
fn init(&mut self) {
if let Some(rst) = &mut self.rst {
rst.set_low().unwrap();
embassy_time::block_for(Duration::from_millis(5));
rst.set_high().unwrap();
embassy_time::block_for(Duration::from_millis(5));
} else {
embassy_time::block_for(Duration::from_millis(5));
self.soft_reset();
embassy_time::block_for(Duration::from_millis(5));
}
debug!(
"enc28j60: erevid {=u8:x}",
self.read_control_register(bank3::Register::EREVID)
);
debug!("enc28j60: waiting for clk");
while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
debug!("enc28j60: clk ok");
if self.read_control_register(bank3::Register::EREVID) == 0 {
panic!("ErevidIsZero");
}
// disable CLKOUT output
self.write_control_register(bank3::Register::ECOCON, 0);
self.init_rx();
// TX start
// "It is recommended that an even address be used for ETXST"
debug_assert_eq!(TXST % 2, 0);
self.write_control_register(bank0::Register::ETXSTL, TXST.low());
self.write_control_register(bank0::Register::ETXSTH, TXST.high());
// TX end is set in `transmit`
// MAC initialization (see section 6.5)
// 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
self.write_control_register(
bank2::Register::MACON1,
bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
);
// 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
self.write_control_register(
bank2::Register::MACON3,
bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
);
// 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
// received or transmitted
self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
// 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
// Use recommended value of 0x12
self.write_control_register(bank2::Register::MABBIPG, 0x12);
// 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
// Use recommended value of 0x12
self.write_control_register(bank2::Register::MAIPGL, 0x12);
self.write_control_register(bank2::Register::MAIPGH, 0x0c);
// 9. Program the local MAC address into the MAADR1:MAADR6 registers
self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
// Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
// Globally enable interrupts
//self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
// Set the per packet control byte; we'll always use the value 0
self.write_buffer_memory(Some(TXST), &mut [0]);
// Enable reception
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
}
fn init_rx(&mut self) {
// RX start
// "It is recommended that the ERXST Pointer be programmed with an even address"
self.write_control_register(bank0::Register::ERXSTL, RXST.low());
self.write_control_register(bank0::Register::ERXSTH, RXST.high());
// RX read pointer
// NOTE Errata #14 so we are using an *odd* address here instead of ERXST
self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
// RX end
self.write_control_register(bank0::Register::ERXNDL, RXND.low());
self.write_control_register(bank0::Register::ERXNDH, RXND.high());
// decrease the packet count to 0
while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
}
self.next_packet = RXST;
}
fn reset_rx(&mut self) {
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst());
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst());
self.init_rx();
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
}
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
/// return `None` on subsequent invocations
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
if self.pending_packets() == 0 {
// Errata #6: we can't rely on PKTIF so we check PKTCNT
return None;
}
let curr_packet = self.next_packet;
// read out the first 6 bytes
let mut temp_buf = [0; 6];
self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
// next packet pointer
let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
// status vector
let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
let len_with_crc = status.byte_count() as u16;
if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND {
warn!("RX buffer corrupted, resetting RX logic to recover...");
self.reset_rx();
return None;
}
let len = len_with_crc - CRC_SZ;
self.read_buffer_memory(None, &mut buf[..len as usize]);
// update ERXRDPT
// due to Errata #14 we must write an odd address to ERXRDPT
// we know that ERXST = 0, that ERXND is odd and that next_packet is even
let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
RXND
} else {
self.next_packet - 1
};
// "To move ERXRDPT, the host controller must write to ERXRDPTL first."
self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
// decrease the packet count
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
self.next_packet = next_packet;
Some(&mut buf[..len as usize])
}
fn wait_tx_ready(&mut self) {
for _ in 0u32..10000 {
if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
return;
}
}
// work around errata #12 by resetting the transmit logic before every new
// transmission
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
//self.bit_field_clear(common::Register::EIR, {
// let mask = common::EIR::mask();
// mask.txerif() | mask.txif()
//});
}
/// Starts the transmission of `bytes`
///
/// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
/// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
/// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
///
/// NOTE This method will flush any previous transmission that's in progress
///
/// # Panics
///
/// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
/// or greater than the transmit buffer
pub fn transmit(&mut self, bytes: &[u8]) {
assert!(bytes.len() <= self.mtu() as usize);
self.wait_tx_ready();
// NOTE the plus one is to not overwrite the per packet control byte
let wrpt = TXST + 1;
// 1. ETXST was set during initialization
// 2. write the frame to the IC memory
self.write_buffer_memory(Some(wrpt), bytes);
let txnd = wrpt + bytes.len() as u16 - 1;
// 3. Set the end address of the transmit buffer
self.write_control_register(bank0::Register::ETXNDL, txnd.low());
self.write_control_register(bank0::Register::ETXNDH, txnd.high());
// 4. reset interrupt flag
//self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
// 5. start transmission
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
// Wait until transmission finishes
//while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
/*
// read the transmit status vector
let mut tx_stat = [0; 7];
self.read_buffer_memory(None, &mut tx_stat);
let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
if stat.txabrt() == 1 {
// work around errata #12 by reading the transmit status vector
if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
panic!("LateCollision")
} else {
panic!("TransmitAbort")
}
}*/
}
/// Get whether the link is up
pub fn is_link_up(&mut self) -> bool {
let bits = self.read_phy_register(phy::Register::PHSTAT2);
phy::PHSTAT2(bits).lstat() == 1
}
/// Returns the interface Maximum Transmission Unit (MTU)
///
/// The value returned by this function will never exceed 1514 bytes. The actual value depends
/// on the memory assigned to the transmission buffer when initializing the device
pub fn mtu(&self) -> u16 {
cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
}
/* Miscellaneous */
/// Returns the number of packets that have been received but have not been processed yet
pub fn pending_packets(&mut self) -> u8 {
self.read_control_register(bank1::Register::EPKTCNT)
}
/// Adjusts the receive filter to *accept* these packet types
pub fn accept(&mut self, packets: &[Packet]) {
let mask = bank1::ERXFCON::mask();
let mut val = 0;
for packet in packets {
match packet {
Packet::Broadcast => val |= mask.bcen(),
Packet::Multicast => val |= mask.mcen(),
Packet::Unicast => val |= mask.ucen(),
}
}
self.bit_field_set(bank1::Register::ERXFCON, val)
}
/// Adjusts the receive filter to *ignore* these packet types
pub fn ignore(&mut self, packets: &[Packet]) {
let mask = bank1::ERXFCON::mask();
let mut val = 0;
for packet in packets {
match packet {
Packet::Broadcast => val |= mask.bcen(),
Packet::Multicast => val |= mask.mcen(),
Packet::Unicast => val |= mask.ucen(),
}
}
self.bit_field_clear(bank1::Register::ERXFCON, val)
}
/* Private */
/* Read */
fn read_control_register<R>(&mut self, register: R) -> u8
where
R: Into<Register>,
{
self._read_control_register(register.into())
}
fn _read_control_register(&mut self, register: Register) -> u8 {
self.change_bank(register);
if register.is_eth_register() {
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
self.spi.transfer_in_place(&mut buffer).unwrap();
buffer[1]
} else {
// MAC, MII regs need a dummy byte.
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
self.spi.transfer_in_place(&mut buffer).unwrap();
buffer[2]
}
}
fn read_phy_register(&mut self, register: phy::Register) -> u16 {
// set PHY register address
self.write_control_register(bank2::Register::MIREGADR, register.addr());
// start read operation
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
// wait until the read operation finishes
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
let l = self.read_control_register(bank2::Register::MIRDL);
let h = self.read_control_register(bank2::Register::MIRDH);
(l as u16) | (h as u16) << 8
}
/* Write */
fn _write_control_register(&mut self, register: Register, value: u8) {
self.change_bank(register);
let buffer = [Instruction::WCR.opcode() | register.addr(), value];
self.spi.write(&buffer).unwrap();
}
fn write_control_register<R>(&mut self, register: R, value: u8)
where
R: Into<Register>,
{
self._write_control_register(register.into(), value)
}
fn write_phy_register(&mut self, register: phy::Register, value: u16) {
// set PHY register address
self.write_control_register(bank2::Register::MIREGADR, register.addr());
self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
// this starts the write operation
self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
// wait until the write operation finishes
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
}
/* RMW */
fn modify_control_register<R, F>(&mut self, register: R, f: F)
where
F: FnOnce(u8) -> u8,
R: Into<Register>,
{
self._modify_control_register(register.into(), f)
}
fn _modify_control_register<F>(&mut self, register: Register, f: F)
where
F: FnOnce(u8) -> u8,
{
let r = self._read_control_register(register);
self._write_control_register(register, f(r))
}
/* Auxiliary */
fn change_bank(&mut self, register: Register) {
let bank = register.bank();
if let Some(bank) = bank {
if self.bank == bank {
// already on the register bank
return;
}
// change bank
self.bank = bank;
match bank {
Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
}
} else {
// common register
}
}
/* Primitive operations */
fn bit_field_clear<R>(&mut self, register: R, mask: u8)
where
R: Into<Register>,
{
self._bit_field_clear(register.into(), mask)
}
fn _bit_field_clear(&mut self, register: Register, mask: u8) {
debug_assert!(register.is_eth_register());
self.change_bank(register);
self.spi
.write(&[Instruction::BFC.opcode() | register.addr(), mask])
.unwrap();
}
fn bit_field_set<R>(&mut self, register: R, mask: u8)
where
R: Into<Register>,
{
self._bit_field_set(register.into(), mask)
}
fn _bit_field_set(&mut self, register: Register, mask: u8) {
debug_assert!(register.is_eth_register());
self.change_bank(register);
self.spi
.write(&[Instruction::BFS.opcode() | register.addr(), mask])
.unwrap();
}
fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
if let Some(addr) = addr {
self.write_control_register(bank0::Register::ERDPTL, addr.low());
self.write_control_register(bank0::Register::ERDPTH, addr.high());
}
self.spi
.transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
.unwrap();
}
fn soft_reset(&mut self) {
self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
}
fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
if let Some(addr) = addr {
self.write_control_register(bank0::Register::EWRPTL, addr.low());
self.write_control_register(bank0::Register::EWRPTH, addr.high());
}
self.spi
.transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
.unwrap();
}
}
#[derive(Clone, Copy, PartialEq)]
enum Bank {
Bank0,
Bank1,
Bank2,
Bank3,
}
#[derive(Clone, Copy)]
enum Instruction {
/// Read Control Register
RCR = 0b000_00000,
/// Read Buffer Memory
RBM = 0b001_11010,
/// Write Control Register
WCR = 0b010_00000,
/// Write Buffer Memory
WBM = 0b011_11010,
/// Bit Field Set
BFS = 0b100_00000,
/// Bit Field Clear
BFC = 0b101_00000,
/// System Reset Command
SRC = 0b111_11111,
}
impl Instruction {
fn opcode(&self) -> u8 {
*self as u8
}
}
#[derive(Clone, Copy)]
enum Register {
Bank0(bank0::Register),
Bank1(bank1::Register),
Bank2(bank2::Register),
Bank3(bank3::Register),
Common(common::Register),
}
impl Register {
fn addr(&self) -> u8 {
match *self {
Register::Bank0(r) => r.addr(),
Register::Bank1(r) => r.addr(),
Register::Bank2(r) => r.addr(),
Register::Bank3(r) => r.addr(),
Register::Common(r) => r.addr(),
}
}
fn bank(&self) -> Option<Bank> {
Some(match *self {
Register::Bank0(_) => Bank::Bank0,
Register::Bank1(_) => Bank::Bank1,
Register::Bank2(_) => Bank::Bank2,
Register::Bank3(_) => Bank::Bank3,
Register::Common(_) => return None,
})
}
fn is_eth_register(&self) -> bool {
match *self {
Register::Bank0(r) => r.is_eth_register(),
Register::Bank1(r) => r.is_eth_register(),
Register::Bank2(r) => r.is_eth_register(),
Register::Bank3(r) => r.is_eth_register(),
Register::Common(r) => r.is_eth_register(),
}
}
}
/// Packet type, used to configure receive filters
#[non_exhaustive]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum Packet {
/// Broadcast packets
Broadcast,
/// Multicast packets
Multicast,
/// Unicast packets
Unicast,
}
static mut TX_BUF: [u8; MTU] = [0; MTU];
static mut RX_BUF: [u8; MTU] = [0; MTU];
impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
where
S: SpiDevice,
O: OutputPin,
{
type RxToken<'a> = RxToken<'a>
where
Self: 'a;
type TxToken<'a> = TxToken<'a, S, O>
where
Self: 'a;
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let rx_buf = unsafe { &mut RX_BUF };
let tx_buf = unsafe { &mut TX_BUF };
if let Some(pkt) = self.receive(rx_buf) {
let n = pkt.len();
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
} else {
cx.waker().wake_by_ref();
None
}
}
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
let tx_buf = unsafe { &mut TX_BUF };
Some(TxToken { buf: tx_buf, eth: self })
}
fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
cx.waker().wake_by_ref();
match self.is_link_up() {
true => LinkState::Up,
false => LinkState::Down,
}
}
fn capabilities(&self) -> Capabilities {
let mut caps = Capabilities::default();
caps.max_transmission_unit = MTU;
caps.medium = Medium::Ethernet;
caps
}
fn hardware_address(&self) -> HardwareAddress {
HardwareAddress::Ethernet(self.mac_addr)
}
}
/// embassy-net RX token.
pub struct RxToken<'a> {
buf: &'a mut [u8],
}
impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
fn consume<R, F>(self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(self.buf)
}
}
/// embassy-net TX token.
pub struct TxToken<'a, S, O>
where
S: SpiDevice,
O: OutputPin,
{
eth: &'a mut Enc28j60<S, O>,
buf: &'a mut [u8],
}
impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
where
S: SpiDevice,
O: OutputPin,
{
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
assert!(len <= self.buf.len());
let r = f(&mut self.buf[..len]);
self.eth.transmit(&self.buf[..len]);
r
}
}

View File

@ -0,0 +1,89 @@
macro_rules! register {
($REGISTER:ident, $reset_value:expr, $uxx:ty, {
$(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
}) => {
#[derive(Clone, Copy)]
pub(crate) struct $REGISTER<MODE> {
bits: $uxx,
_mode: ::core::marker::PhantomData<MODE>,
}
impl $REGISTER<super::traits::Mask> {
#[allow(dead_code)]
pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
$REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
}
$(
#[allow(dead_code)]
pub(crate) fn $bitfield(&self) -> $uxx {
use super::traits::OffsetSize;
let size = $range.size();
let offset = $range.offset();
((1 << size) - 1) << offset
}
)+
}
impl ::core::default::Default for $REGISTER<super::traits::W> {
fn default() -> Self {
$REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
}
}
#[allow(non_snake_case)]
#[allow(dead_code)]
pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
$REGISTER { bits, _mode: ::core::marker::PhantomData }
}
impl $REGISTER<super::traits::R> {
#[allow(dead_code)]
pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
$REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
}
$(
#[$($attr)*]
#[allow(dead_code)]
pub(crate) fn $bitfield(&self) -> $uxx {
use super::traits::OffsetSize;
let offset = $range.offset();
let size = $range.size();
let mask = (1 << size) - 1;
(self.bits >> offset) & mask
}
)+
}
impl $REGISTER<super::traits::W> {
#[allow(dead_code)]
pub(crate) fn bits(self) -> $uxx {
self.bits
}
$(
#[$($attr)*]
#[allow(dead_code)]
pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
use super::traits::OffsetSize;
let offset = $range.offset();
let size = $range.size();
let mask = (1 << size) - 1;
debug_assert!(bits <= mask);
bits &= mask;
self.bits &= !(mask << offset);
self.bits |= bits << offset;
self
}
)+
}
}
}

View File

@ -0,0 +1,35 @@
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub enum Register {
PHCON1 = 0x00,
PHSTAT1 = 0x01,
PHID1 = 0x02,
PHID2 = 0x03,
PHCON2 = 0x10,
PHSTAT2 = 0x11,
PHIE = 0x12,
PHIR = 0x13,
PHLCON = 0x14,
}
impl Register {
pub(crate) fn addr(&self) -> u8 {
*self as u8
}
}
register!(PHCON2, 0, u16, {
#[doc = "PHY Half-Duplex Loopback Disable bit"]
hdldis @ 8,
#[doc = "Jabber Correction Disable bit"]
jabber @ 10,
#[doc = "Twisted-Pair Transmitter Disable bit"]
txdis @ 13,
#[doc = "PHY Force Linkup bit"]
frclnk @ 14,
});
register!(PHSTAT2, 0, u16, {
#[doc = "Link Status bit"]
lstat @ 10,
});

View File

@ -0,0 +1,57 @@
use core::ops::Range;
pub(crate) trait OffsetSize {
fn offset(self) -> u8;
fn size(self) -> u8;
}
impl OffsetSize for u8 {
fn offset(self) -> u8 {
self
}
fn size(self) -> u8 {
1
}
}
impl OffsetSize for Range<u8> {
fn offset(self) -> u8 {
self.start
}
fn size(self) -> u8 {
self.end - self.start
}
}
pub(crate) trait U16Ext {
fn from_parts(low: u8, high: u8) -> Self;
fn low(self) -> u8;
fn high(self) -> u8;
}
impl U16Ext for u16 {
fn from_parts(low: u8, high: u8) -> u16 {
((high as u16) << 8) + low as u16
}
fn low(self) -> u8 {
(self & 0xff) as u8
}
fn high(self) -> u8 {
(self >> 8) as u8
}
}
#[derive(Clone, Copy)]
pub struct Mask;
#[derive(Clone, Copy)]
pub struct R;
#[derive(Clone, Copy)]
pub struct W;

View File

@ -7,14 +7,20 @@ edition = "2021"
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embassy-time = { version = "0.1.0", path = "../embassy-time" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
heapless = "0.7.16"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]

View File

@ -1,14 +1,16 @@
use ch::driver::LinkState;
use defmt::Debug2Format;
use embassy_net_driver_channel as ch;
use heapless::String;
use crate::ioctl::Shared;
use crate::proto::{self, CtrlMsg};
#[derive(Debug)]
pub struct Error {
pub status: u32,
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
Failed(u32),
Timeout,
Internal,
}
pub struct Control<'a> {
@ -24,55 +26,78 @@ enum WifiMode {
ApSta = 3,
}
macro_rules! ioctl {
($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {
let mut msg = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::$req_variant as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::$req_variant($req)),
};
$self.ioctl(&mut msg).await?;
let Some(proto::CtrlMsgPayload::$resp_variant($resp)) = msg.payload else {
warn!("unexpected response variant");
return Err(Error::Internal);
};
if $resp.resp != 0 {
return Err(Error::Failed($resp.resp));
}
};
}
impl<'a> Control<'a> {
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
Self { state_ch, shared }
}
pub async fn init(&mut self) {
pub async fn init(&mut self) -> Result<(), Error> {
debug!("wait for init event...");
self.shared.init_wait().await;
debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await;
debug!("set heartbeat");
self.set_heartbeat(10).await?;
let mac_addr = self.get_mac_addr().await;
debug!("set wifi mode");
self.set_wifi_mode(WifiMode::Sta as _).await?;
let mac_addr = self.get_mac_addr().await?;
debug!("mac addr: {:02x}", mac_addr);
self.state_ch.set_ethernet_address(mac_addr);
Ok(())
}
pub async fn join(&mut self, ssid: &str, password: &str) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqConnectAp as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
})),
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
let req = proto::CtrlMsgReqConnectAp {
ssid: String::from(ssid),
pwd: String::from(password),
bssid: String::new(),
listen_interval: 3,
is_wpa3_supported: false,
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
debug!("======= {:?}", Debug2Format(&resp));
assert_eq!(resp.resp, 0);
ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
self.state_ch.set_link_state(LinkState::Up);
Ok(())
}
async fn get_mac_addr(&mut self) -> [u8; 6] {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
proto::CtrlMsgReqGetMacAddress {
mode: WifiMode::Sta as _,
},
)),
pub async fn disconnect(&mut self) -> Result<(), Error> {
let req = proto::CtrlMsgReqGetStatus {};
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
self.state_ch.set_link_state(LinkState::Down);
Ok(())
}
/// duration in seconds, clamped to [10, 3600]
async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration };
ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp);
Ok(())
}
async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> {
let req = proto::CtrlMsgReqGetMacAddress {
mode: WifiMode::Sta as _,
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
// WHY IS THIS A STRING? WHYYYY
fn nibble_from_hex(b: u8) -> u8 {
@ -86,30 +111,32 @@ impl<'a> Control<'a> {
let mac = resp.mac.as_bytes();
let mut res = [0; 6];
assert_eq!(mac.len(), 17);
if mac.len() != 17 {
warn!("unexpected MAC respnse length");
return Err(Error::Internal);
}
for (i, b) in res.iter_mut().enumerate() {
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
}
res
Ok(res)
}
async fn set_wifi_mode(&mut self, mode: u32) {
let req = proto::CtrlMsg {
msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
msg_type: proto::CtrlMsgType::Req as _,
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
};
let resp = self.ioctl(req).await;
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
assert_eq!(resp.resp, 0);
async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
let req = proto::CtrlMsgReqSetMode { mode };
ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);
Ok(())
}
async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
debug!("ioctl req: {:?}", &req);
async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
debug!("ioctl req: {:?}", &msg);
let mut buf = [0u8; 128];
let req_len = noproto::write(&req, &mut buf).unwrap();
let req_len = noproto::write(msg, &mut buf).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
struct CancelOnDrop<'a>(&'a Shared);
@ -131,9 +158,12 @@ impl<'a> Control<'a> {
ioctl.defuse();
let res = noproto::read(&buf[..resp_len]).unwrap();
debug!("ioctl resp: {:?}", &res);
*msg = noproto::read(&buf[..resp_len]).map_err(|_| {
warn!("failed to serialize control request");
Error::Internal
})?;
debug!("ioctl resp: {:?}", msg);
res
Ok(())
}
}

View File

@ -1,17 +1,15 @@
#![no_std]
use control::Control;
use embassy_futures::select::{select3, Either3};
use embassy_futures::select::{select4, Either4};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embassy_time::{Duration, Instant, Timer};
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_async::digital::Wait;
use embedded_hal_async::spi::SpiDevice;
use ioctl::Shared;
use proto::CtrlMsg;
use crate::ioctl::PendingIoctl;
use crate::proto::CtrlMsgPayload;
use crate::ioctl::{PendingIoctl, Shared};
use crate::proto::{CtrlMsg, CtrlMsgPayload};
mod proto;
@ -21,6 +19,8 @@ mod fmt;
mod control;
mod ioctl;
pub use control::*;
const MTU: usize = 1514;
macro_rules! impl_bytes {
@ -95,6 +95,7 @@ enum InterfaceType {
}
const MAX_SPI_BUFFER_SIZE: usize = 1600;
const HEARTBEAT_MAX_GAP: Duration = Duration::from_secs(20);
pub struct State {
shared: Shared,
@ -124,17 +125,19 @@ where
IN: InputPin + Wait,
OUT: OutputPin,
{
let (ch_runner, device) = ch::new(&mut state.ch, [0; 6]);
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ethernet([0; 6]));
let state_ch = ch_runner.state_runner();
let mut runner = Runner {
ch: ch_runner,
state_ch,
shared: &state.shared,
next_seq: 1,
handshake,
ready,
reset,
spi,
heartbeat_deadline: Instant::now() + HEARTBEAT_MAX_GAP,
};
runner.init().await;
@ -143,9 +146,11 @@ where
pub struct Runner<'a, SPI, IN, OUT> {
ch: ch::Runner<'a, MTU>,
state_ch: ch::StateRunner<'a>,
shared: &'a Shared,
next_seq: u16,
heartbeat_deadline: Instant,
spi: SPI,
handshake: IN,
@ -177,9 +182,10 @@ where
let ioctl = self.shared.ioctl_wait_pending();
let tx = self.ch.tx_buf();
let ev = async { self.ready.wait_for_high().await.unwrap() };
let hb = Timer::at(self.heartbeat_deadline);
match select3(ioctl, tx, ev).await {
Either3::First(PendingIoctl { buf, req_len }) => {
match select4(ioctl, tx, ev, hb).await {
Either4::First(PendingIoctl { buf, req_len }) => {
tx_buf[12..24].copy_from_slice(b"\x01\x08\x00ctrlResp\x02");
tx_buf[24..26].copy_from_slice(&(req_len as u16).to_le_bytes());
tx_buf[26..][..req_len].copy_from_slice(&unsafe { &*buf }[..req_len]);
@ -198,7 +204,7 @@ where
header.checksum = checksum(&tx_buf[..26 + req_len]);
tx_buf[0..12].copy_from_slice(&header.to_bytes());
}
Either3::Second(packet) => {
Either4::Second(packet) => {
tx_buf[12..][..packet.len()].copy_from_slice(packet);
let mut header = PayloadHeader {
@ -217,9 +223,12 @@ where
self.ch.tx_done();
}
Either3::Third(()) => {
Either4::Third(()) => {
tx_buf[..PayloadHeader::SIZE].fill(0);
}
Either4::Fourth(()) => {
panic!("heartbeat from esp32 stopped")
}
}
if tx_buf[0] != 0 {
@ -308,21 +317,26 @@ where
}
}
fn handle_event(&self, data: &[u8]) {
fn handle_event(&mut self, data: &[u8]) {
let Ok(event) = noproto::read::<CtrlMsg>(data) else {
warn!("failed to parse event");
return
return;
};
debug!("event: {:?}", &event);
let Some(payload) = &event.payload else {
warn!("event without payload?");
return
return;
};
match payload {
CtrlMsgPayload::EventEspInit(_) => self.shared.init_done(),
CtrlMsgPayload::EventHeartbeat(_) => self.heartbeat_deadline = Instant::now() + HEARTBEAT_MAX_GAP,
CtrlMsgPayload::EventStationDisconnectFromAp(e) => {
info!("disconnected, code {}", e.resp);
self.state_ch.set_link_state(LinkState::Down);
}
_ => {}
}
}

View File

@ -0,0 +1,28 @@
[package]
name = "embassy-net-ppp"
version = "0.1.0"
description = "embassy-net driver for PPP over Serial"
keywords = ["embedded", "ppp", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[features]
defmt = ["dep:defmt", "ppproto/defmt"]
log = ["dep:log", "ppproto/log"]
[dependencies]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
embedded-io-async = { version = "0.5.0" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
ppproto = { version = "0.1.1"}
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-ppp/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]

19
embassy-net-ppp/README.md Normal file
View File

@ -0,0 +1,19 @@
# `embassy-net-ppp`
[`embassy-net`](https://crates.io/crates/embassy-net) integration for PPP over Serial.
## Interoperability
This crate can run on any executor.
It supports any serial port implementing [`embedded-io-async`](https://crates.io/crates/embedded-io-async).
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

257
embassy-net-ppp/src/fmt.rs Normal file
View File

@ -0,0 +1,257 @@
#![macro_use]
#![allow(unused_macros)]
use core::fmt::{Debug, Display, LowerHex};
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You may not enable both `defmt` and `log` features.");
macro_rules! assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert!($($x)*);
}
};
}
macro_rules! assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_eq!($($x)*);
}
};
}
macro_rules! assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::assert_ne!($($x)*);
}
};
}
macro_rules! debug_assert {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert!($($x)*);
}
};
}
macro_rules! debug_assert_eq {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_eq!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_eq!($($x)*);
}
};
}
macro_rules! debug_assert_ne {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::debug_assert_ne!($($x)*);
#[cfg(feature = "defmt")]
::defmt::debug_assert_ne!($($x)*);
}
};
}
macro_rules! todo {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::todo!($($x)*);
#[cfg(feature = "defmt")]
::defmt::todo!($($x)*);
}
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unreachable {
($($x:tt)*) => {
::core::unreachable!($($x)*)
};
}
#[cfg(feature = "defmt")]
macro_rules! unreachable {
($($x:tt)*) => {
::defmt::unreachable!($($x)*);
};
}
macro_rules! panic {
($($x:tt)*) => {
{
#[cfg(not(feature = "defmt"))]
::core::panic!($($x)*);
#[cfg(feature = "defmt")]
::defmt::panic!($($x)*);
}
};
}
macro_rules! trace {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::trace!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::trace!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! debug {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::debug!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::debug!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! info {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::info!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::info!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! warn {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::warn!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::warn!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
macro_rules! error {
($s:literal $(, $x:expr)* $(,)?) => {
{
#[cfg(feature = "log")]
::log::error!($s $(, $x)*);
#[cfg(feature = "defmt")]
::defmt::error!($s $(, $x)*);
#[cfg(not(any(feature = "log", feature="defmt")))]
let _ = ($( & $x ),*);
}
};
}
#[cfg(feature = "defmt")]
macro_rules! unwrap {
($($x:tt)*) => {
::defmt::unwrap!($($x)*)
};
}
#[cfg(not(feature = "defmt"))]
macro_rules! unwrap {
($arg:expr) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
}
}
};
($arg:expr, $($msg:expr),+ $(,)? ) => {
match $crate::fmt::Try::into_result($arg) {
::core::result::Result::Ok(t) => t,
::core::result::Result::Err(e) => {
::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct NoneError;
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
}
impl<T> Try for Option<T> {
type Ok = T;
type Error = NoneError;
#[inline]
fn into_result(self) -> Result<T, NoneError> {
self.ok_or(NoneError)
}
}
impl<T, E> Try for Result<T, E> {
type Ok = T;
type Error = E;
#[inline]
fn into_result(self) -> Self {
self
}
}
pub struct Bytes<'a>(pub &'a [u8]);
impl<'a> Debug for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> Display for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
impl<'a> LowerHex for Bytes<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:#02x?}", self.0)
}
}
#[cfg(feature = "defmt")]
impl<'a> defmt::Format for Bytes<'a> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{:02x}", self.0)
}
}

180
embassy-net-ppp/src/lib.rs Normal file
View File

@ -0,0 +1,180 @@
#![no_std]
#![warn(missing_docs)]
#![doc = include_str!("../README.md")]
// must be first
mod fmt;
use core::convert::Infallible;
use core::mem::MaybeUninit;
use embassy_futures::select::{select, Either};
use embassy_net_driver_channel as ch;
use embassy_net_driver_channel::driver::LinkState;
use embedded_io_async::{BufRead, Write, WriteAllError};
use ppproto::pppos::{BufferFullError, PPPoS, PPPoSAction};
pub use ppproto::{Config, Ipv4Status};
const MTU: usize = 1500;
/// Type alias for the embassy-net driver.
pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
/// Internal state for the embassy-net integration.
pub struct State<const N_RX: usize, const N_TX: usize> {
ch_state: ch::State<MTU, N_RX, N_TX>,
}
impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
/// Create a new `State`.
pub const fn new() -> Self {
Self {
ch_state: ch::State::new(),
}
}
}
/// Background runner for the driver.
///
/// You must call `.run()` in a background task for the driver to operate.
pub struct Runner<'d> {
ch: ch::Runner<'d, MTU>,
}
/// Error returned by [`Runner::run`].
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RunError<E> {
/// Reading from the serial port failed.
Read(E),
/// Writing to the serial port failed.
Write(E),
/// Writing to the serial port wrote zero bytes, indicating it can't accept more data.
WriteZero,
/// Writing to the serial got EOF.
Eof,
}
impl<E> From<WriteAllError<E>> for RunError<E> {
fn from(value: WriteAllError<E>) -> Self {
match value {
WriteAllError::Other(e) => Self::Write(e),
WriteAllError::WriteZero => Self::WriteZero,
}
}
}
impl<'d> Runner<'d> {
/// You must call this in a background task for the driver to operate.
///
/// If reading/writing to the underlying serial port fails, the link state
/// is set to Down and the error is returned.
///
/// It is allowed to cancel this function's future (i.e. drop it). This will terminate
/// the PPP connection and set the link state to Down.
///
/// After this function returns or is canceled, you can call it again to establish
/// a new PPP connection.
pub async fn run<RW: BufRead + Write>(
&mut self,
mut rw: RW,
config: ppproto::Config<'_>,
mut on_ipv4_up: impl FnMut(Ipv4Status),
) -> Result<Infallible, RunError<RW::Error>> {
let mut ppp = PPPoS::new(config);
ppp.open().unwrap();
let (state_chan, mut rx_chan, mut tx_chan) = self.ch.borrow_split();
state_chan.set_link_state(LinkState::Down);
let _ondrop = OnDrop::new(|| state_chan.set_link_state(LinkState::Down));
let mut rx_buf = [0; 2048];
let mut tx_buf = [0; 2048];
let mut needs_poll = true;
let mut was_up = false;
loop {
let rx_fut = async {
let buf = rx_chan.rx_buf().await;
let rx_data = match needs_poll {
true => &[][..],
false => match rw.fill_buf().await {
Ok(rx_data) if rx_data.len() == 0 => return Err(RunError::Eof),
Ok(rx_data) => rx_data,
Err(e) => return Err(RunError::Read(e)),
},
};
Ok((buf, rx_data))
};
let tx_fut = tx_chan.tx_buf();
match select(rx_fut, tx_fut).await {
Either::First(r) => {
needs_poll = false;
let (buf, rx_data) = r?;
let n = ppp.consume(rx_data, &mut rx_buf);
rw.consume(n);
match ppp.poll(&mut tx_buf, &mut rx_buf) {
PPPoSAction::None => {}
PPPoSAction::Received(rg) => {
let pkt = &rx_buf[rg];
buf[..pkt.len()].copy_from_slice(pkt);
rx_chan.rx_done(pkt.len());
}
PPPoSAction::Transmit(n) => rw.write_all(&tx_buf[..n]).await?,
}
let status = ppp.status();
match status.phase {
ppproto::Phase::Open => {
if !was_up {
on_ipv4_up(status.ipv4.unwrap());
}
was_up = true;
state_chan.set_link_state(LinkState::Up);
}
_ => {
was_up = false;
state_chan.set_link_state(LinkState::Down);
}
}
}
Either::Second(pkt) => {
match ppp.send(pkt, &mut tx_buf) {
Ok(n) => rw.write_all(&tx_buf[..n]).await?,
Err(BufferFullError) => unreachable!(),
}
tx_chan.tx_done();
}
}
}
}
}
/// Create a PPP embassy-net driver instance.
///
/// This returns two structs:
/// - a `Device` that you must pass to the `embassy-net` stack.
/// - a `Runner`. You must call `.run()` on it in a background task.
pub fn new<'a, const N_RX: usize, const N_TX: usize>(state: &'a mut State<N_RX, N_TX>) -> (Device<'a>, Runner<'a>) {
let (runner, device) = ch::new(&mut state.ch_state, ch::driver::HardwareAddress::Ip);
(device, Runner { ch: runner })
}
struct OnDrop<F: FnOnce()> {
f: MaybeUninit<F>,
}
impl<F: FnOnce()> OnDrop<F> {
fn new(f: F) -> Self {
Self { f: MaybeUninit::new(f) }
}
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
unsafe { self.f.as_ptr().read()() }
}
}

View File

@ -0,0 +1,19 @@
[package]
name = "embassy-net-tuntap"
version = "0.1.0"
description = "embassy-net driver for Linux TUN/TAP interfaces."
keywords = ["embedded", "tuntap", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
async-io = "1.6.0"
log = "0.4.14"
libc = "0.2.101"
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-tuntap-v$VERSION/embassy-net-tuntap/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-tuntap/src/"
target = "thumbv7em-none-eabi"

View File

@ -0,0 +1,17 @@
# `embassy-net` integration for Linux TUN/TAP interfaces.
[`embassy-net`](https://crates.io/crates/embassy-net) integration for for Linux TUN (IP medium) and TAP (Ethernet medium) interfaces.
## Interoperability
This crate can run on any executor.
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -4,7 +4,7 @@ use std::os::unix::io::{AsRawFd, RawFd};
use std::task::Context;
use async_io::Async;
use embassy_net_driver::{self, Capabilities, Driver, LinkState};
use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState};
use log::*;
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
@ -19,6 +19,7 @@ const ETHERNET_HEADER_LEN: usize = 14;
#[repr(C)]
#[derive(Debug)]
#[allow(non_camel_case_types)]
struct ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
@ -180,8 +181,8 @@ impl Driver for TunTapDevice {
LinkState::Up
}
fn ethernet_address(&self) -> [u8; 6] {
[0x02, 0x03, 0x04, 0x05, 0x06, 0x07]
fn hardware_address(&self) -> HardwareAddress {
HardwareAddress::Ethernet([0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
}
}

View File

@ -1,21 +0,0 @@
[package]
name = "embassy-net-w5500"
version = "0.1.0"
description = "embassy-net driver for the W5500 ethernet chip"
keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-alpha.10" }
embedded-hal-async = { version = "=0.2.0-alpha.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
embassy-time = { version = "0.1.0" }
embassy-futures = { version = "0.1.0" }
defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-w5500-v$VERSION/embassy-net-w5500/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-w5500/src/"
target = "thumbv7em-none-eabi"

View File

@ -1,7 +0,0 @@
# WIZnet W5500 `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode.
Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async)
See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module.

View File

@ -1,131 +0,0 @@
use embedded_hal_async::spi::SpiDevice;
use crate::socket;
use crate::spi::SpiInterface;
pub const MODE: u16 = 0x00;
pub const MAC: u16 = 0x09;
pub const SOCKET_INTR: u16 = 0x18;
pub const PHY_CFG: u16 = 0x2E;
#[repr(u8)]
pub enum RegisterBlock {
Common = 0x00,
Socket0 = 0x01,
TxBuf = 0x02,
RxBuf = 0x03,
}
/// W5500 in MACRAW mode
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct W5500<SPI> {
bus: SpiInterface<SPI>,
}
impl<SPI: SpiDevice> W5500<SPI> {
/// Create and initialize the W5500 driver
pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<W5500<SPI>, SPI::Error> {
let mut bus = SpiInterface(spi);
// Reset device
bus.write_frame(RegisterBlock::Common, MODE, &[0x80]).await?;
// Enable interrupt pin
bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01]).await?;
// Enable receive interrupt
bus.write_frame(
RegisterBlock::Socket0,
socket::SOCKET_INTR_MASK,
&[socket::Interrupt::Receive as u8],
)
.await?;
// Set MAC address
bus.write_frame(RegisterBlock::Common, MAC, &mac_addr).await?;
// Set the raw socket RX/TX buffer sizes to 16KB
bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16])
.await?;
bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16])
.await?;
// MACRAW mode with MAC filtering.
let mode: u8 = (1 << 2) | (1 << 7);
bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode]).await?;
socket::command(&mut bus, socket::Command::Open).await?;
Ok(Self { bus })
}
/// Read bytes from the RX buffer. Returns the number of bytes read.
async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result<usize, SPI::Error> {
let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
let read_buffer = if rx_size > buffer.len() + offset as usize {
buffer
} else {
&mut buffer[..rx_size - offset as usize]
};
let read_ptr = socket::get_rx_read_ptr(&mut self.bus).await?.wrapping_add(offset);
self.bus.read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer).await?;
socket::set_rx_read_ptr(&mut self.bus, read_ptr.wrapping_add(read_buffer.len() as u16)).await?;
Ok(read_buffer.len())
}
/// Read an ethernet frame from the device. Returns the number of bytes read.
pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result<usize, SPI::Error> {
let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
if rx_size == 0 {
return Ok(0);
}
socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?;
// First two bytes gives the size of the received ethernet frame
let expected_frame_size: usize = {
let mut frame_bytes = [0u8; 2];
assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2);
u16::from_be_bytes(frame_bytes) as usize - 2
};
// Read the ethernet frame
let read_buffer = if frame.len() > expected_frame_size {
&mut frame[..expected_frame_size]
} else {
frame
};
let recvd_frame_size = self.read_bytes(read_buffer, 2).await?;
// Register RX as completed
socket::command(&mut self.bus, socket::Command::Receive).await?;
// If the whole frame wasn't read, drop it
if recvd_frame_size < expected_frame_size {
Ok(0)
} else {
Ok(recvd_frame_size)
}
}
/// Write an ethernet frame to the device. Returns number of bytes written
pub async fn write_frame(&mut self, frame: &[u8]) -> Result<usize, SPI::Error> {
while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {}
let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?;
self.bus.write_frame(RegisterBlock::TxBuf, write_ptr, frame).await?;
socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?;
socket::command(&mut self.bus, socket::Command::Send).await?;
Ok(frame.len())
}
pub async fn is_link_up(&mut self) -> bool {
let mut link = [0];
self.bus
.read_frame(RegisterBlock::Common, PHY_CFG, &mut link)
.await
.ok();
link[0] & 1 == 1
}
}

View File

@ -1,80 +0,0 @@
use embedded_hal_async::spi::SpiDevice;
use crate::device::RegisterBlock;
use crate::spi::SpiInterface;
pub const MODE: u16 = 0x00;
pub const COMMAND: u16 = 0x01;
pub const RXBUF_SIZE: u16 = 0x1E;
pub const TXBUF_SIZE: u16 = 0x1F;
pub const TX_FREE_SIZE: u16 = 0x20;
pub const TX_DATA_WRITE_PTR: u16 = 0x24;
pub const RECVD_SIZE: u16 = 0x26;
pub const RX_DATA_READ_PTR: u16 = 0x28;
pub const SOCKET_INTR_MASK: u16 = 0x2C;
#[repr(u8)]
pub enum Command {
Open = 0x01,
Send = 0x20,
Receive = 0x40,
}
pub const INTR: u16 = 0x02;
#[repr(u8)]
pub enum Interrupt {
Receive = 0b00100_u8,
}
pub async fn reset_interrupt<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, code: Interrupt) -> Result<(), SPI::Error> {
let data = [code as u8];
bus.write_frame(RegisterBlock::Socket0, INTR, &data).await
}
pub async fn get_tx_write_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data)
.await?;
Ok(u16::from_be_bytes(data))
}
pub async fn set_tx_write_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data).await
}
pub async fn get_rx_read_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data)
.await?;
Ok(u16::from_be_bytes(data))
}
pub async fn set_rx_read_ptr<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, ptr: u16) -> Result<(), SPI::Error> {
let data = ptr.to_be_bytes();
bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data).await
}
pub async fn command<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>, command: Command) -> Result<(), SPI::Error> {
let data = [command as u8];
bus.write_frame(RegisterBlock::Socket0, COMMAND, &data).await
}
pub async fn get_rx_size<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
loop {
// Wait until two sequential reads are equal
let mut res0 = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0).await?;
let mut res1 = [0u8; 2];
bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1).await?;
if res0 == res1 {
break Ok(u16::from_be_bytes(res0));
}
}
}
pub async fn get_tx_free_size<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
let mut data = [0; 2];
bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data).await?;
Ok(u16::from_be_bytes(data))
}

View File

@ -1,28 +0,0 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
use crate::device::RegisterBlock;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiInterface<SPI>(pub SPI);
impl<SPI: SpiDevice> SpiInterface<SPI> {
pub async fn read_frame(&mut self, block: RegisterBlock, address: u16, data: &mut [u8]) -> Result<(), SPI::Error> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3];
let operations = &mut [
Operation::Write(&address_phase),
Operation::Write(&control_phase),
Operation::TransferInPlace(data),
];
self.0.transaction(operations).await
}
pub async fn write_frame(&mut self, block: RegisterBlock, address: u16, data: &[u8]) -> Result<(), SPI::Error> {
let address_phase = address.to_be_bytes();
let control_phase = [(block as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &[&address_phase[..], &control_phase, &data_phase];
self.0.write_transaction(operations).await
}
}

View File

@ -0,0 +1,22 @@
[package]
name = "embassy-net-wiznet"
version = "0.1.0"
description = "embassy-net driver for WIZnet SPI Ethernet chips"
keywords = ["embedded", "wiznet", "embassy-net", "embedded-hal-async", "ethernet", "async"]
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
embedded-hal = { version = "1.0.0-rc.1" }
embedded-hal-async = { version = "=1.0.0-rc.1" }
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel" }
embassy-time = { version = "0.1.2", path = "../embassy-time" }
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
defmt = { version = "0.3", optional = true }
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-wiznet-v$VERSION/embassy-net-wiznet/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-wiznet/src/"
target = "thumbv7em-none-eabi"
features = ["defmt"]

View File

@ -0,0 +1,27 @@
# WIZnet `embassy-net` integration
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode.
See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module.
## Supported chips
- W5500
- W5100S
## Interoperability
This crate can run on any executor.
It supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async).
## License
This work is licensed under either of
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

View File

@ -0,0 +1,48 @@
mod w5500;
pub use w5500::W5500;
mod w5100s;
pub use w5100s::W5100S;
pub(crate) mod sealed {
use embedded_hal_async::spi::SpiDevice;
pub trait Chip {
type Address;
const COMMON_MODE: Self::Address;
const COMMON_MAC: Self::Address;
const COMMON_SOCKET_INTR: Self::Address;
const COMMON_PHY_CFG: Self::Address;
const SOCKET_MODE: Self::Address;
const SOCKET_COMMAND: Self::Address;
const SOCKET_RXBUF_SIZE: Self::Address;
const SOCKET_TXBUF_SIZE: Self::Address;
const SOCKET_TX_FREE_SIZE: Self::Address;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address;
const SOCKET_RECVD_SIZE: Self::Address;
const SOCKET_RX_DATA_READ_PTR: Self::Address;
const SOCKET_INTR_MASK: Self::Address;
const SOCKET_INTR: Self::Address;
const SOCKET_MODE_VALUE: u8;
const BUF_SIZE: u16;
const AUTO_WRAP: bool;
fn rx_addr(addr: u16) -> Self::Address;
fn tx_addr(addr: u16) -> Self::Address;
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error>;
async fn bus_write<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &[u8],
) -> Result<(), SPI::Error>;
}
}
pub trait Chip: sealed::Chip {}

View File

@ -0,0 +1,61 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
const SOCKET_BASE: u16 = 0x400;
const TX_BASE: u16 = 0x4000;
const RX_BASE: u16 = 0x6000;
pub enum W5100S {}
impl super::Chip for W5100S {}
impl super::sealed::Chip for W5100S {
type Address = u16;
const COMMON_MODE: Self::Address = 0x00;
const COMMON_MAC: Self::Address = 0x09;
const COMMON_SOCKET_INTR: Self::Address = 0x16;
const COMMON_PHY_CFG: Self::Address = 0x3c;
const SOCKET_MODE: Self::Address = SOCKET_BASE + 0x00;
const SOCKET_COMMAND: Self::Address = SOCKET_BASE + 0x01;
const SOCKET_RXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1E;
const SOCKET_TXBUF_SIZE: Self::Address = SOCKET_BASE + 0x1F;
const SOCKET_TX_FREE_SIZE: Self::Address = SOCKET_BASE + 0x20;
const SOCKET_TX_DATA_WRITE_PTR: Self::Address = SOCKET_BASE + 0x24;
const SOCKET_RECVD_SIZE: Self::Address = SOCKET_BASE + 0x26;
const SOCKET_RX_DATA_READ_PTR: Self::Address = SOCKET_BASE + 0x28;
const SOCKET_INTR_MASK: Self::Address = SOCKET_BASE + 0x2C;
const SOCKET_INTR: Self::Address = SOCKET_BASE + 0x02;
const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 6);
const BUF_SIZE: u16 = 0x2000;
const AUTO_WRAP: bool = false;
fn rx_addr(addr: u16) -> Self::Address {
RX_BASE + addr
}
fn tx_addr(addr: u16) -> Self::Address {
TX_BASE + addr
}
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error> {
spi.transaction(&mut [
Operation::Write(&[0x0F, (address >> 8) as u8, address as u8]),
Operation::Read(data),
])
.await
}
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {
spi.transaction(&mut [
Operation::Write(&[0xF0, (address >> 8) as u8, address as u8]),
Operation::Write(data),
])
.await
}
}

View File

@ -0,0 +1,72 @@
use embedded_hal_async::spi::{Operation, SpiDevice};
#[repr(u8)]
pub enum RegisterBlock {
Common = 0x00,
Socket0 = 0x01,
TxBuf = 0x02,
RxBuf = 0x03,
}
pub enum W5500 {}
impl super::Chip for W5500 {}
impl super::sealed::Chip for W5500 {
type Address = (RegisterBlock, u16);
const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00);
const COMMON_MAC: Self::Address = (RegisterBlock::Common, 0x09);
const COMMON_SOCKET_INTR: Self::Address = (RegisterBlock::Common, 0x18);
const COMMON_PHY_CFG: Self::Address = (RegisterBlock::Common, 0x2E);
const SOCKET_MODE: Self::Address = (RegisterBlock::Socket0, 0x00);
const SOCKET_COMMAND: Self::Address = (RegisterBlock::Socket0, 0x01);
const SOCKET_RXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1E);
const SOCKET_TXBUF_SIZE: Self::Address = (RegisterBlock::Socket0, 0x1F);
const SOCKET_TX_FREE_SIZE: Self::Address = (RegisterBlock::Socket0, 0x20);
const SOCKET_TX_DATA_WRITE_PTR: Self::Address = (RegisterBlock::Socket0, 0x24);
const SOCKET_RECVD_SIZE: Self::Address = (RegisterBlock::Socket0, 0x26);
const SOCKET_RX_DATA_READ_PTR: Self::Address = (RegisterBlock::Socket0, 0x28);
const SOCKET_INTR_MASK: Self::Address = (RegisterBlock::Socket0, 0x2C);
const SOCKET_INTR: Self::Address = (RegisterBlock::Socket0, 0x02);
const SOCKET_MODE_VALUE: u8 = (1 << 2) | (1 << 7);
const BUF_SIZE: u16 = 0x4000;
const AUTO_WRAP: bool = true;
fn rx_addr(addr: u16) -> Self::Address {
(RegisterBlock::RxBuf, addr)
}
fn tx_addr(addr: u16) -> Self::Address {
(RegisterBlock::TxBuf, addr)
}
async fn bus_read<SPI: SpiDevice>(
spi: &mut SPI,
address: Self::Address,
data: &mut [u8],
) -> Result<(), SPI::Error> {
let address_phase = address.1.to_be_bytes();
let control_phase = [(address.0 as u8) << 3];
let operations = &mut [
Operation::Write(&address_phase),
Operation::Write(&control_phase),
Operation::TransferInPlace(data),
];
spi.transaction(operations).await
}
async fn bus_write<SPI: SpiDevice>(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error> {
let address_phase = address.1.to_be_bytes();
let control_phase = [(address.0 as u8) << 3 | 0b0000_0100];
let data_phase = data;
let operations = &mut [
Operation::Write(&address_phase[..]),
Operation::Write(&control_phase),
Operation::Write(&data_phase),
];
spi.transaction(operations).await
}
}

Some files were not shown because too many files have changed in this diff Show More