Compare commits
1129 Commits
embassy-ne
...
net-driver
Author | SHA1 | Date | |
---|---|---|---|
3cbc687424 | |||
51708c8ed1 | |||
10f08445e4 | |||
f24a1b62bb | |||
bbd12c9372 | |||
d94b9fe6fb | |||
b478640463 | |||
846f2fc6e4 | |||
683d5c3066 | |||
a3574e519a | |||
3e3317e8bd | |||
e7aeb9b29f | |||
7fd868ade9 | |||
6e6df22979 | |||
f7980885a5 | |||
5a1393aa0b | |||
40e4ca4751 | |||
1d810baa18 | |||
213b4c9dca | |||
889d482d2d | |||
ea0e83a7f9 | |||
f0497039ed | |||
aff77d2b65 | |||
a7c6999670 | |||
18e96898ea | |||
870dcc5970 | |||
5c5e681819 | |||
f54753beaa | |||
b24520579a | |||
31d4516516 | |||
66e62e9994 | |||
eeedaf2e76 | |||
1fc35c753b | |||
cd92bc3145 | |||
4a156df7a1 | |||
c46e758e2c | |||
8a10948ce9 | |||
7045c53170 | |||
0621e957a0 | |||
a818f33658 | |||
3bfbf2697f | |||
7559f9e583 | |||
c8fdbe19f9 | |||
2e50bf667a | |||
ba62037642 | |||
824556c9c8 | |||
f7c0ec66d2 | |||
69bb455c60 | |||
575db17264 | |||
0aa99e66f7 | |||
3264941c1b | |||
a57d383b1d | |||
9a7fda87b0 | |||
adc810d24b | |||
75baf186f3 | |||
a6bbb130c5 | |||
d1ac6d2824 | |||
636958ff70 | |||
7f6fe93f55 | |||
e9b1904db8 | |||
02d2c06b23 | |||
debda2f52c | |||
97ca0e77bf | |||
66e399b5c6 | |||
01eb1a7339 | |||
f0d6ee69bc | |||
65f81a1f57 | |||
ecdd7c0e2f | |||
d7d79f3068 | |||
f65a96c541 | |||
eb368f77a4 | |||
57ccc1051a | |||
ac84631a2a | |||
70a91945fc | |||
32b89eeba1 | |||
063e6f96da | |||
4cec4b0548 | |||
e6c47c3718 | |||
2f7c2750d1 | |||
032b1f2d59 | |||
f76d50e837 | |||
ee5ea7aa06 | |||
251d004708 | |||
cd68f85501 | |||
b6c0ddb7df | |||
ee93bbf1d4 | |||
7b1cd42936 | |||
1cd3ae9bd5 | |||
bfcca79c1e | |||
c283e2d1b9 | |||
4a43cd3982 | |||
0dda888c95 | |||
b91d1eaca0 | |||
5a19d18b9c | |||
ed7b6056a6 | |||
322f9cb153 | |||
3ec4e6320a | |||
233aa1b53a | |||
21915a9a3f | |||
d0d0ceec6a | |||
9be61a2967 | |||
0cfa8d1bb5 | |||
cd12c9cbce | |||
eff73d6dfa | |||
3616d68aaa | |||
904f1cfc7a | |||
e62bf6a9fd | |||
7526b8edba | |||
c3ef98a73d | |||
6186fe0807 | |||
c4cff0b79b | |||
2e5ab7981e | |||
9d311121f2 | |||
25c3754cd2 | |||
85c6f23dcb | |||
9c6a2d9cbd | |||
b67b179933 | |||
d4ed8e5779 | |||
3e054a6f0d | |||
c6d53e7bce | |||
3bf8e4de5f | |||
9090a78df4 | |||
022d870d61 | |||
534eb960e9 | |||
7a2b8481f7 | |||
710874021a | |||
3a8e0d4a27 | |||
b217d147de | |||
68c4820dde | |||
f30fc949ff | |||
75c1a9a3aa | |||
4075ac84e1 | |||
f01609036f | |||
930b8f3495 | |||
38e7709a24 | |||
65ed19aae2 | |||
be9c05fbc9 | |||
42176b1a3a | |||
e1a0635ca3 | |||
b887b7516e | |||
c6513f93fe | |||
59f706ee2f | |||
ba7d74ac16 | |||
0324cee0ca | |||
8ac5c1a963 | |||
e7ab044935 | |||
0c4cddfafb | |||
e561e673c2 | |||
09dfcd6b66 | |||
bb8a1b7f1f | |||
b012a67df7 | |||
40f30fa4cd | |||
f197aa9419 | |||
80a740bc72 | |||
ad524373ed | |||
9badf740d8 | |||
6e901846df | |||
c0a6c78a14 | |||
58280048e3 | |||
00824af82b | |||
adf9ffb109 | |||
bc203ebe4b | |||
e042b3056d | |||
9dc927250c | |||
b856d760f4 | |||
a742a80171 | |||
90ade1b311 | |||
a931dfd9ad | |||
eff61648fe | |||
9228a6334b | |||
3c9dfbbc9d | |||
ce0add61f8 | |||
bf6e06060b | |||
eb12114345 | |||
4b252586e5 | |||
5f6a915a32 | |||
923f1851ee | |||
bd267a6479 | |||
f3aa0cfe5a | |||
6ea5aa347d | |||
8c13126cff | |||
81da9ca621 | |||
729338875c | |||
f98c8886b2 | |||
a1036e111e | |||
2af97e7595 | |||
2008030a8b | |||
480328d07e | |||
05218a52e6 | |||
d2a2734752 | |||
5ad34404af | |||
a7b1e51650 | |||
70005c3956 | |||
93adbb9922 | |||
e80df91e02 | |||
5cb58754d4 | |||
5c83179071 | |||
49608714aa | |||
f116ca83e0 | |||
6e5f3f9515 | |||
5924cc8b49 | |||
0d49aaf8ca | |||
5dd9e9b3b7 | |||
d42cfda2db | |||
d1f4511cd1 | |||
fa8d5da4a5 | |||
7bc57ca3f7 | |||
7ad102695d | |||
4999de6f82 | |||
9b8bdad403 | |||
3c3fb0a529 | |||
dc4c27aead | |||
74eb519810 | |||
61dc36b1f2 | |||
55a0a15be2 | |||
fc8f96fea5 | |||
08175f191a | |||
8da987cfbb | |||
1be5f193c3 | |||
4a632e08b7 | |||
6cdee93934 | |||
5c8c1b2146 | |||
2addfc4b86 | |||
92df87781d | |||
a35d149cb1 | |||
c52320f467 | |||
2f9b59c5cf | |||
ce91fb2bfc | |||
95b3d9eb3b | |||
f1e7205055 | |||
6627824c02 | |||
d62533879c | |||
e1a8caffa4 | |||
dffdb9268b | |||
22e820b743 | |||
aa14f18d3f | |||
39f1b26a39 | |||
e70143ef8f | |||
d6654bf8b5 | |||
322a4a8401 | |||
ee5591017b | |||
b1b32f079b | |||
e8a462768e | |||
ce0d787781 | |||
fa2e63f74b | |||
23f3889167 | |||
a72a91ab06 | |||
b9ef831ff7 | |||
e538aeacc8 | |||
6b3745fc47 | |||
6200d22438 | |||
901d31e8f8 | |||
79146c4bd5 | |||
d9eae79f3e | |||
3d1b4116ec | |||
901f0257bd | |||
fec4194ae6 | |||
f866735802 | |||
20ea76c19c | |||
e1951f3ddf | |||
8b5fb254c0 | |||
624f786203 | |||
a57e48459e | |||
c604d8a8f1 | |||
be0c52bf5e | |||
44bb405683 | |||
e8587e2fba | |||
5d8817d109 | |||
c79a84a98a | |||
96edbd84fb | |||
dc400a0539 | |||
04b09a2acb | |||
481d2998ef | |||
4b695120fc | |||
bd9021ca1d | |||
6e63c8d4bf | |||
70acc093dd | |||
f7c2db245f | |||
2543bcafaf | |||
9f2fc04caa | |||
f32bfafa61 | |||
e03239e88d | |||
7d5e62d4a7 | |||
65d36f9f99 | |||
347ff18644 | |||
1e80fd81f7 | |||
52e269edbb | |||
c849620cd6 | |||
6f83acc010 | |||
2b7b7a917d | |||
f1488864eb | |||
7cf327130e | |||
02b0523199 | |||
47175198c0 | |||
4b20e6c258 | |||
83b4c01273 | |||
e313ca4ae8 | |||
ad64d7b20b | |||
5626a28af2 | |||
766ae724cb | |||
00b9f9acef | |||
d46920dce6 | |||
e1ed492577 | |||
2405aff11f | |||
a402aed3d1 | |||
c573959a95 | |||
561696dfad | |||
b1abaa380f | |||
e640933e2f | |||
183824fbdd | |||
5d0c40f61d | |||
fdfe12fe1c | |||
4bfbcd6c72 | |||
0da793e5de | |||
0dcb34fc7d | |||
feaeee1e83 | |||
a6ef314be1 | |||
daeb497045 | |||
88eb5cca71 | |||
cd128c20fa | |||
087ef918bf | |||
75cae09e79 | |||
bbe1d96045 | |||
3ddc9cd110 | |||
de2773afdd | |||
2930be5e17 | |||
09f087e53e | |||
044b837caa | |||
ad0a306ea5 | |||
b5fffbf66f | |||
537e2f3b44 | |||
80d7193b5b | |||
8315cf064e | |||
a2a26f489b | |||
f27620cc0b | |||
2911774849 | |||
6da75ea285 | |||
aa2fa29b89 | |||
c28a6bdd0b | |||
5a158b94bd | |||
c091ed2f63 | |||
45e9e51bdc | |||
7c77d2bd94 | |||
f76d94098d | |||
07233ca5b4 | |||
9fb14379c3 | |||
309c3d6b47 | |||
f1f4943ca5 | |||
1b20ba27b1 | |||
f5e96a9d60 | |||
e8b961232b | |||
85a9f5816f | |||
7bcc7e8962 | |||
b118c4cc1b | |||
f090a38dde | |||
dcd1a91966 | |||
49847ff432 | |||
b9d4b18f14 | |||
3a1ed823f8 | |||
9c647dd0e8 | |||
582ef90994 | |||
5e381d49da | |||
af59fa0a7e | |||
4907ccaa4d | |||
2c38dd17b9 | |||
d36e7abb71 | |||
e11db9fa59 | |||
f877a5889d | |||
db54edf56c | |||
4072a16af6 | |||
44a5c32ea4 | |||
b9889ad3b5 | |||
527bdc57b9 | |||
1133cbf90e | |||
77c357e744 | |||
521970535e | |||
93d4cfe7c1 | |||
8413a89752 | |||
db717d9c81 | |||
808fa9dce6 | |||
ceb13def19 | |||
5cf494113f | |||
8edb7bb012 | |||
8900f5f52b | |||
8201979d71 | |||
2d9f50addc | |||
18da91e252 | |||
28e2570533 | |||
26e0823c35 | |||
ceca8b4336 | |||
e308286f3c | |||
268da2fcde | |||
a992d4895d | |||
1bae34d37c | |||
aac42e3209 | |||
08415e001e | |||
d6a1b567ee | |||
a47fb42962 | |||
70a4a193c5 | |||
2132afb48b | |||
0e9131fd14 | |||
40a18b075d | |||
f17f09057d | |||
11a78fb1e4 | |||
48154e18bf | |||
cf2d4eca7c | |||
3e0b752bef | |||
6070d61d8c | |||
a4f8d82ef5 | |||
c27b0296fe | |||
336ae54a56 | |||
d6a1118406 | |||
9de08d56a0 | |||
26740bb3ef | |||
4550452f43 | |||
08410432b5 | |||
3cf3caa3ab | |||
c21ad04c2e | |||
d097c99719 | |||
af7c93abba | |||
c356585a59 | |||
0d3ff34d80 | |||
bb2d6c8542 | |||
a05afc5426 | |||
50116e5b27 | |||
a80acf686e | |||
6770d8e8a6 | |||
7307098780 | |||
f4601af2a4 | |||
fd22f4fac5 | |||
7622d2eb61 | |||
7573160077 | |||
7e18e5e0c1 | |||
9d76a6e933 | |||
f502271940 | |||
e2f8bf19ea | |||
1180e1770d | |||
49ba9c3da2 | |||
ce662766be | |||
a03b6be693 | |||
87d3086533 | |||
d19e1c1dd1 | |||
8a55adbfd8 | |||
6dc56d2b35 | |||
394503ab69 | |||
bfb4cf775a | |||
274f63a879 | |||
615882ebd6 | |||
6e38b07642 | |||
1eb03dc41a | |||
a2656f402b | |||
3466c9cfa9 | |||
3295ec94e5 | |||
c5732ce285 | |||
2e6f4237f2 | |||
ac11635b0b | |||
9dd58660c3 | |||
9baa3bafb0 | |||
360286e67c | |||
0c66636d00 | |||
9d8c527308 | |||
1d87ec9cc3 | |||
b74645e259 | |||
56351cedcb | |||
0823d9dc93 | |||
c10fb7c1c4 | |||
a2ce3aa1a1 | |||
3769447382 | |||
2c6b428ed4 | |||
83a976cf4b | |||
a76ff53fb3 | |||
f69e8459c9 | |||
891f1758bc | |||
ae174fd0e0 | |||
5c936d33d4 | |||
36ec9bcc1d | |||
5adc80f6d9 | |||
98f55fa54d | |||
416ecc73d8 | |||
27dfced285 | |||
21681d8b4e | |||
989c98f316 | |||
fdb2c4946a | |||
5e613d9abb | |||
40b576584e | |||
aa7752020e | |||
6c165f8dc0 | |||
975f2f23c0 | |||
0eeefd3dbf | |||
de01fe352b | |||
70662ec4ba | |||
a4d78a6552 | |||
f503417f4c | |||
6b8b145266 | |||
e07f943562 | |||
70a5221b2e | |||
b315c28d4e | |||
e025693914 | |||
05ee02b593 | |||
4098a61ef0 | |||
1db00f5439 | |||
7fc17bc150 | |||
6e616a6fe6 | |||
d02886786e | |||
2db4d01198 | |||
a36ee75d19 | |||
1f63bf4153 | |||
fd739250ea | |||
71c4e7e4d2 | |||
2c80784fe6 | |||
538001a4bc | |||
e981cd4968 | |||
91bb3aae3f | |||
e08dbcd027 | |||
5c27265a21 | |||
2c36199dea | |||
13a0be6289 | |||
9f928010a8 | |||
88146eb53e | |||
326e78757b | |||
f77a7fe4bf | |||
cbc92dce05 | |||
531f51d0eb | |||
f28ab18d7b | |||
3bf6081eb5 | |||
fb942e6675 | |||
10ea068027 | |||
4caa8497fc | |||
48085939e7 | |||
7f7256050c | |||
4b6538c8a8 | |||
db6f9afa2e | |||
59a5e84df5 | |||
13f0501673 | |||
94de1a5353 | |||
db71887817 | |||
1e430f7413 | |||
2897670f24 | |||
ca738d6c99 | |||
d33246b072 | |||
54e2e17520 | |||
3023e70ccf | |||
f8299d10f7 | |||
8339423a2f | |||
295542f4d3 | |||
d812cc5745 | |||
623f37a273 | |||
a026db3f57 | |||
c2d601abef | |||
2303382dfd | |||
aacf14b62a | |||
100200d021 | |||
b5748524f8 | |||
6d3377e6a6 | |||
cc8961034e | |||
cda4047310 | |||
f033089625 | |||
2a6b743b9e | |||
ecc305bbfe | |||
9c94eac919 | |||
00aadf6085 | |||
83f224e140 | |||
e987259716 | |||
fc04d2a33c | |||
1d4d11ba25 | |||
5f5e3bcd18 | |||
4b6045d446 | |||
ca588f901f | |||
681165e84c | |||
0a7c061ddc | |||
1ded213ae9 | |||
0e9524de62 | |||
e082cd0cda | |||
10bf33dcac | |||
46ddf7013a | |||
577d644e22 | |||
2cf6a59114 | |||
e19f7d9a76 | |||
bed1f07c15 | |||
a2c718f61c | |||
0568738f77 | |||
a6d22e199a | |||
1ea4c58c39 | |||
faab2d0d53 | |||
6d35bcc3d9 | |||
7d6edd7b15 | |||
9e3266b745 | |||
7bff2ebab3 | |||
048bdf6968 | |||
b3212ae383 | |||
b436aad2a0 | |||
31c5bdfa29 | |||
906ab06a6e | |||
a5484cd119 | |||
c39671266e | |||
8878ce046c | |||
5bfddfc9b6 | |||
7148397771 | |||
8c12453544 | |||
f723982bec | |||
0a73c84df0 | |||
977ae5e3e4 | |||
2687008242 | |||
6ef888d73e | |||
67ca88d5a0 | |||
17e9a8ebe1 | |||
0032f8a2ec | |||
2d980068c0 | |||
6babd5752e | |||
5d4da78c94 | |||
cc400aa178 | |||
7b3cb2ce04 | |||
afd8be416e | |||
5bc0175be9 | |||
eb05a18c45 | |||
97da34595c | |||
2ea17d2783 | |||
8754a1d378 | |||
d327b626e3 | |||
f48d13a16a | |||
4c11fffc90 | |||
7f97efd922 | |||
78bb261246 | |||
73942f50cb | |||
3ebb93e47d | |||
5329f234ba | |||
2cc82ef660 | |||
b948e37769 | |||
91b10dd799 | |||
94fa95c699 | |||
62e66cdda3 | |||
1cb76e0d99 | |||
ef7523e5b7 | |||
065b0f34af | |||
2a4ebdc150 | |||
bb275f7e25 | |||
4f453d7ed6 | |||
8a9f49c163 | |||
19b1e32c2c | |||
c80c323634 | |||
dd5a886830 | |||
df6952648e | |||
f26dd54f63 | |||
ffe9688952 | |||
11b66a73b4 | |||
1d4b941d52 | |||
76276c326a | |||
a436bd068f | |||
c367b84ee5 | |||
46f671ae42 | |||
96e0ace89e | |||
8655ba110c | |||
7d24e433d8 | |||
098fcb14b5 | |||
c114ea024a | |||
b1ef009c6b | |||
b6b4448045 | |||
cb3644856d | |||
03576b9e83 | |||
ea9f887ee1 | |||
f7f75167ac | |||
253b28deba | |||
4af1cf88d2 | |||
2c1402843a | |||
0fd9d7400b | |||
bd58b5002a | |||
890f29ccfe | |||
e4f3979ec8 | |||
07c3600127 | |||
da4f15d944 | |||
fbec797d64 | |||
9954346143 | |||
3a51e2d9ca | |||
986a63ebb8 | |||
4c4b12c307 | |||
f6007869bf | |||
454a7cbf4c | |||
ec6bd27df6 | |||
6ab0d71d92 | |||
d5e66f6f87 | |||
bce250bbdc | |||
fbf50cdae8 | |||
675b7fb605 | |||
0727f8690c | |||
55ff397c0c | |||
b1ec460b9a | |||
f9d251cd5c | |||
b658f10db9 | |||
3a3f3b492f | |||
b69861013a | |||
a0c69ffe02 | |||
c1da2c0219 | |||
611e82711a | |||
baef856206 | |||
858d520882 | |||
7d8e3951ba | |||
3b43b00867 | |||
95262ad559 | |||
43e2edfbda | |||
fb49e03eda | |||
c312884692 | |||
bc156afbb2 | |||
8a9622ec2e | |||
0705152105 | |||
8d71fbd032 | |||
820852be28 | |||
fcb77f3f96 | |||
32fdd4c787 | |||
6a73ab1afa | |||
6fc5c608f8 | |||
b7114fb951 | |||
d375c46590 | |||
c40b944da6 | |||
e31545860e | |||
47b8e04b1c | |||
2ab9a07b64 | |||
5d5cd23715 | |||
77844e2055 | |||
28eb3b95b0 | |||
8b837fae07 | |||
d49f40dd5c | |||
477a90b8e3 | |||
a34331ae5f | |||
b555af1c5d | |||
ae608cf2fa | |||
28618d12a1 | |||
66c1712118 | |||
7b3d7a3826 | |||
d7031fbe92 | |||
02fcb07aa9 | |||
66faba2df7 | |||
766b5fc6f6 | |||
a2fd7108ff | |||
630372b183 | |||
3c5f011245 | |||
e80db42061 | |||
7e269f6f17 | |||
30358c435e | |||
55e07712e5 | |||
293edc5772 | |||
dc5acc687f | |||
a40daa923b | |||
9dfda46e0c | |||
4d60c715e6 | |||
2c96fe917d | |||
b42a7ebd8c | |||
9b1bdc3099 | |||
bcaef1de18 | |||
33778d3772 | |||
a6b8f3d994 | |||
b166ed6b78 | |||
54d31c98fe | |||
48eac0b146 | |||
0d8a9b1e7a | |||
ef3b1f46a9 | |||
5e4f65fe1f | |||
5fcebd28f4 | |||
a1fce1b554 | |||
bbc8424a5b | |||
036bc669cd | |||
26cc0e634d | |||
1b0f4ee653 | |||
f3ad0c6ade | |||
4da9743317 | |||
dca1777a2f | |||
4a9df60a7b | |||
2c6fcdbd3f | |||
e6d4043279 | |||
5c2ba3b212 | |||
ebc173ea75 | |||
b394cc3394 | |||
6caf627262 | |||
3d68d42132 | |||
bdd59b8988 | |||
9f55228be0 | |||
83ff3cbc69 | |||
4afdce4ec5 | |||
71fcea159f | |||
036e00113e | |||
958cace36d | |||
2568c714c8 | |||
83ab8e057a | |||
0ddabf0423 | |||
b4d0f24bf9 | |||
5a2f61a031 | |||
5b4c099afc | |||
c3357f884a | |||
eff2d71f28 | |||
42b21fd7ae | |||
027801db60 | |||
ad85beb677 | |||
780569c08a | |||
ffa0c08140 | |||
c38c85ef1f | |||
6c6bd11c1a | |||
4999b045df | |||
105aa8f452 | |||
d8f02e151b | |||
3aef5999d5 | |||
3c3a1d89b5 | |||
e8d3e86591 | |||
d6c5c1772c | |||
b65406791a | |||
f3237d7a2c | |||
56b21ad429 | |||
538cf2bc24 | |||
d8420ed5a0 | |||
04ed45941a | |||
55fb1d5126 | |||
4f791799a9 | |||
d2127f6b82 | |||
1d815f4ba0 | |||
aef93246b4 | |||
6b1d802caa | |||
6f30e92c7a | |||
39c1cc9f00 | |||
73057ee241 | |||
a8a491212b | |||
2f18770e27 | |||
087e649bc2 | |||
fd9b6487e1 | |||
603c4cb4fa | |||
8bed573b88 | |||
2a004251a7 | |||
8064f4bfe0 | |||
6256a6c57c | |||
bae31ebce7 | |||
e0ce7fcde7 | |||
a9f6e30bcd | |||
0d7b005252 | |||
fcbfd224a7 | |||
eb097b9d03 | |||
7ed9e29326 | |||
bdc4aa4a3b | |||
5bb5654d84 | |||
a8d3bcbb75 | |||
ec787d3518 | |||
c7c701b3e3 | |||
e495d606ec | |||
28136579e9 | |||
cc414e63d3 | |||
fd47445d75 | |||
d39404cdda | |||
29acc46501 | |||
cffb819e61 | |||
b344c843c4 | |||
e3cc0d168c | |||
cbc8871a0b | |||
e97b14c068 | |||
5a8704b4d8 | |||
6dd2fc5941 | |||
69c0a89aa5 | |||
937a63ce28 | |||
b57ba84da5 | |||
c3ba08ffb6 | |||
c52d1d11f9 | |||
d752a3f980 | |||
973b152375 | |||
3690af9bea | |||
f81ee103bf | |||
b124222649 | |||
8d8c642845 | |||
d5f9d17b7c | |||
036e6ae30c | |||
38b5d1ee2b | |||
146c744223 | |||
0ced8400d0 | |||
2e4f89068a | |||
f9dd751b6b | |||
6b6acc256d | |||
91338adc15 | |||
1d4e1092c4 | |||
0f1ff77fcc | |||
e947aa0153 | |||
44c8db2911 | |||
93864610ce | |||
bbd2563e13 | |||
a6543cef16 | |||
2815540167 | |||
8f1ea85938 | |||
3ee3f0e21c | |||
13acca624f | |||
c54ae73d49 | |||
858ddf6777 | |||
a5f2152077 | |||
a56ef685f3 | |||
62ab0bf2e7 | |||
77e34c5e8a | |||
270d1d59a0 | |||
3c41784de8 | |||
1425dda0a7 | |||
a60d92cfbb | |||
9f898c460f | |||
2a0fe73045 | |||
8d50f8a3d3 | |||
622fcb0e10 | |||
7fc138c91e | |||
5b32db7564 | |||
bd60f003e0 | |||
4883fdd154 | |||
18b9b6c780 | |||
fbcc587eca | |||
fbe30b2453 | |||
d42dff45de | |||
8e230bf6ec | |||
603ea9f310 | |||
a56b3e9a44 | |||
0378366e29 | |||
80ce6d1fb7 | |||
ba8e5d8589 | |||
192cdc2f85 | |||
64f8a779ca | |||
5693ed1178 | |||
19c6c698b5 | |||
224fbc8125 | |||
e5b4641f9e | |||
c83552eadc | |||
4db63677f6 | |||
f4d6a23f92 | |||
2cdd593290 | |||
c675208b8a | |||
e9445ec72d | |||
899a68325c | |||
c80c232a72 | |||
83ff626c47 | |||
809d3476aa | |||
4d1d125f41 | |||
4d6b3c57b1 | |||
a3d4ae85b0 | |||
02d57afd51 | |||
28254842db | |||
3382ca1a54 | |||
07a9a4ffd8 | |||
36ff688fab | |||
3df2c71e6c | |||
ca1d4179a7 | |||
890d113b85 | |||
7555a1e302 | |||
be7fbe50d7 | |||
2c01f277c2 | |||
c333d855fc | |||
42de1c3a06 | |||
a1d3bc30fa | |||
98576c17b6 | |||
27a3d2cd0b | |||
10f5966787 | |||
48957dce87 | |||
fc901f9856 | |||
13964c7fca | |||
a1cc3f2c60 | |||
15a6e04887 | |||
6bf4717b0a | |||
f581831b86 | |||
6f02403184 | |||
d040871f7a | |||
8f23b6faa6 | |||
1d2c47273d | |||
55ac480cb0 | |||
e4ad1aa542 | |||
6d8a5c6c20 | |||
6b5df4523a | |||
7b34f5e866 | |||
fe1e7c4d76 | |||
34217ea797 | |||
a0515ca7ac | |||
28b419d65e | |||
7c465465c1 | |||
e95a7dc555 | |||
582006c75c | |||
c7ec45a004 | |||
88d1976e81 | |||
cd592cb055 | |||
0b63af3313 | |||
25197308e3 | |||
cf278ea1b6 | |||
4db4200c37 | |||
758862f4b1 | |||
3705b4f40d | |||
1f63fdbb15 | |||
d11a94e2a7 | |||
d6dd5ea5d3 | |||
8a146a50ec | |||
17d5e1c470 | |||
975a780efe | |||
c3774607a5 | |||
bb24cfd1e8 | |||
48b37aa2bf | |||
0bde4992ea | |||
7ec7d1bbcc | |||
0628dd997f | |||
283ec756a9 | |||
5b076cb0dd | |||
3bae533066 | |||
7b36fe049d | |||
4b3fda4f96 | |||
56ca179475 | |||
460cdc9e0f | |||
f90b170dad | |||
68792bb918 | |||
3f0c8bafb0 | |||
588c0479f5 | |||
d979841f17 | |||
dff9bd9711 | |||
eccd2ecebf | |||
ed86fc175f | |||
132327a40d | |||
a615a70eda | |||
a2501bd5c1 | |||
ff2daaff67 | |||
d5a4457b5e | |||
6d402fe393 | |||
466a391b52 | |||
a93714327e | |||
029b156563 | |||
55a5e9b3a5 | |||
d8c7c3fc4b | |||
f192f44018 | |||
b81c14f442 | |||
f54e1cea90 | |||
fbddfcbfb7 | |||
67b14e6e7a | |||
6f4172fbc1 | |||
c6e2f4a90b | |||
91c1d17f16 | |||
29f3d5b68d | |||
4aca7c8811 | |||
8a811cfcf7 | |||
bf4493dbdf | |||
c1bf5aee24 | |||
735d676a72 | |||
37c103b5f3 | |||
05c524a7db | |||
758a2c528f | |||
69b4e898b3 | |||
b0da6318f3 | |||
972cdd4265 | |||
f9aebfce01 | |||
151557fec3 | |||
7d68ca1f3b | |||
4b63829110 | |||
e196387e69 | |||
f8d608093f | |||
ffeb40ff43 | |||
47305c2bf2 | |||
c421b7f5f0 | |||
d137286981 | |||
864202a23a | |||
a77fb0f630 | |||
a42ac86f1b | |||
c6cd69887c | |||
d1711036db | |||
8313b7315a | |||
d7ecf6f593 | |||
082147939d | |||
67c4d165c7 | |||
fb3e6a2b40 | |||
8ee2f50b8c | |||
46a4600952 | |||
27992d9c07 | |||
a0dc87d64e | |||
1255d8a8ce | |||
7d3eb6463a | |||
ab7fcf1d5b | |||
2c5146f19f | |||
3341b53eb4 | |||
70c05c62e4 | |||
eb57bb298f | |||
953c745ed8 | |||
ce73c29246 | |||
9c4df46c46 | |||
2a035a24a6 | |||
10c0174903 | |||
d26a247a32 | |||
a101d9078d | |||
b2f843a4ce | |||
40d25da793 | |||
b956d5d06c | |||
0845eab8a0 | |||
582c721aec | |||
0c4180cdd0 | |||
d9824dfd64 | |||
a088c4bee6 | |||
8359d8c020 | |||
1869fe02ba | |||
e3e8d82933 | |||
a96f30edf4 | |||
af15b49bfe | |||
60b2f075dc | |||
0c49e6747c | |||
99b4ea7c1d | |||
c9b9be5b81 | |||
2e6b813225 | |||
aceba1c03f | |||
8141d53d94 | |||
21a8653195 | |||
3aedbce5c2 | |||
d372df7ddb | |||
6e13f5b387 | |||
c07854fed8 | |||
8c4997c5fc | |||
3252eaa060 | |||
348019e37f | |||
b9eb3dfad7 | |||
71513ccb39 | |||
cdb3fb059f | |||
93caf97a04 | |||
bca2c54948 | |||
81cbb0fc32 | |||
c69f2929c0 | |||
4d23ea554b | |||
d6fde756a8 | |||
2432cece38 | |||
fef338f5c2 | |||
24e186e684 | |||
71afa40a69 | |||
89fbb02979 | |||
76a334bd7c | |||
f47a148f51 | |||
5ecf9ec7bc | |||
d896f80405 | |||
2900ab79e7 | |||
14eae9ca06 | |||
64154fec8c | |||
ed97e61dbe | |||
029713eca0 | |||
3d26573c6b | |||
0963b5f92c | |||
530f192acc | |||
a46f33b214 |
41
.gitattributes
vendored
Normal file
41
.gitattributes
vendored
Normal 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
|
4
.github/bors.toml
vendored
4
.github/bors.toml
vendored
@ -1,4 +0,0 @@
|
|||||||
status = [
|
|
||||||
"all",
|
|
||||||
]
|
|
||||||
delete_merged_branches = true
|
|
4
.github/ci/build-stable.sh
vendored
4
.github/ci/build-stable.sh
vendored
@ -8,6 +8,10 @@ export RUSTUP_HOME=/ci/cache/rustup
|
|||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
|
5
.github/ci/build.sh
vendored
5
.github/ci/build.sh
vendored
@ -11,8 +11,13 @@ if [ -f /ci/secrets/teleprobe-token.txt ]; then
|
|||||||
echo Got teleprobe token!
|
echo Got teleprobe token!
|
||||||
export TELEPROBE_HOST=https://teleprobe.embassy.dev
|
export TELEPROBE_HOST=https://teleprobe.embassy.dev
|
||||||
export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)
|
export TELEPROBE_TOKEN=$(cat /ci/secrets/teleprobe-token.txt)
|
||||||
|
export TELEPROBE_CACHE=/ci/cache/teleprobe_cache.json
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
|
17
.github/ci/crlf.sh
vendored
Executable file
17
.github/ci/crlf.sh
vendored
Executable 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
61
.github/ci/doc.sh
vendored
@ -6,7 +6,7 @@ set -euo pipefail
|
|||||||
export RUSTUP_HOME=/ci/cache/rustup
|
export RUSTUP_HOME=/ci/cache/rustup
|
||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
export BUILDER_THREADS=6
|
export BUILDER_THREADS=4
|
||||||
export BUILDER_COMPRESS=true
|
export BUILDER_COMPRESS=true
|
||||||
|
|
||||||
# force rustup to download the toolchain before starting building.
|
# force rustup to download the toolchain before starting building.
|
||||||
@ -15,30 +15,41 @@ export BUILDER_COMPRESS=true
|
|||||||
# which makes rustup very sad
|
# which makes rustup very sad
|
||||||
rustc --version > /dev/null
|
rustc --version > /dev/null
|
||||||
|
|
||||||
docserver-builder -i ./embassy-stm32 -o crates/embassy-stm32/git.zup
|
docserver-builder -i ./embassy-boot/boot -o webroot/crates/embassy-boot/git.zup
|
||||||
docserver-builder -i ./embassy-boot/boot -o crates/embassy-boot/git.zup
|
docserver-builder -i ./embassy-boot/nrf -o webroot/crates/embassy-boot-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-boot/nrf -o 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/rp -o crates/embassy-boot-rp/git.zup
|
docserver-builder -i ./embassy-boot/stm32 -o webroot/crates/embassy-boot-stm32/git.zup
|
||||||
docserver-builder -i ./embassy-boot/stm32 -o crates/embassy-boot-stm32/git.zup
|
docserver-builder -i ./embassy-embedded-hal -o webroot/crates/embassy-embedded-hal/git.zup
|
||||||
docserver-builder -i ./embassy-embedded-hal -o crates/embassy-embedded-hal/git.zup
|
docserver-builder -i ./embassy-executor -o webroot/crates/embassy-executor/git.zup
|
||||||
docserver-builder -i ./embassy-executor -o crates/embassy-executor/git.zup
|
docserver-builder -i ./embassy-futures -o webroot/crates/embassy-futures/git.zup
|
||||||
docserver-builder -i ./embassy-futures -o crates/embassy-futures/git.zup
|
docserver-builder -i ./embassy-lora -o webroot/crates/embassy-lora/git.zup
|
||||||
docserver-builder -i ./embassy-lora -o crates/embassy-lora/git.zup
|
docserver-builder -i ./embassy-net -o webroot/crates/embassy-net/git.zup
|
||||||
docserver-builder -i ./embassy-net -o 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 -o 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-net-driver-channel -o crates/embassy-net-driver-channel/git.zup
|
docserver-builder -i ./embassy-nrf -o webroot/crates/embassy-nrf/git.zup
|
||||||
docserver-builder -i ./embassy-nrf -o crates/embassy-nrf/git.zup
|
docserver-builder -i ./embassy-rp -o webroot/crates/embassy-rp/git.zup
|
||||||
docserver-builder -i ./embassy-rp -o crates/embassy-rp/git.zup
|
docserver-builder -i ./embassy-sync -o webroot/crates/embassy-sync/git.zup
|
||||||
docserver-builder -i ./embassy-sync -o crates/embassy-sync/git.zup
|
docserver-builder -i ./embassy-time -o webroot/crates/embassy-time/git.zup
|
||||||
docserver-builder -i ./embassy-time -o crates/embassy-time/git.zup
|
docserver-builder -i ./embassy-usb -o webroot/crates/embassy-usb/git.zup
|
||||||
docserver-builder -i ./embassy-usb -o crates/embassy-usb/git.zup
|
docserver-builder -i ./embassy-usb-driver -o webroot/crates/embassy-usb-driver/git.zup
|
||||||
docserver-builder -i ./embassy-usb-driver -o crates/embassy-usb-driver/git.zup
|
docserver-builder -i ./embassy-usb-logger -o webroot/crates/embassy-usb-logger/git.zup
|
||||||
docserver-builder -i ./embassy-usb-logger -o crates/embassy-usb-logger/git.zup
|
docserver-builder -i ./cyw43 -o webroot/crates/cyw43/git.zup
|
||||||
docserver-builder -i ./cyw43 -o crates/cyw43/git.zup
|
docserver-builder -i ./cyw43-pio -o webroot/crates/cyw43-pio/git.zup
|
||||||
docserver-builder -i ./cyw43-pio -o crates/cyw43-pio/git.zup
|
docserver-builder -i ./embassy-net-wiznet -o webroot/crates/embassy-net-wiznet/git.zup
|
||||||
docserver-builder -i ./embassy-net-w5500 -o crates/embassy-net-w5500/git.zup
|
docserver-builder -i ./embassy-net-enc28j60 -o webroot/crates/embassy-net-enc28j60/git.zup
|
||||||
docserver-builder -i ./embassy-stm32-wpan -o crates/embassy-stm32-wpan/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
|
export KUBECONFIG=/ci/secrets/kubeconfig.yml
|
||||||
POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
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
|
||||||
|
8
.github/ci/test.sh
vendored
8
.github/ci/test.sh
vendored
@ -8,12 +8,16 @@ export RUSTUP_HOME=/ci/cache/rustup
|
|||||||
export CARGO_HOME=/ci/cache/cargo
|
export CARGO_HOME=/ci/cache/cargo
|
||||||
export CARGO_TARGET_DIR=/ci/cache/target
|
export CARGO_TARGET_DIR=/ci/cache/target
|
||||||
|
|
||||||
|
# needed for "dumb HTTP" transport support
|
||||||
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
hashtime save /ci/cache/filetime.json
|
hashtime save /ci/cache/filetime.json
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-embedded-hal/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-time/Cargo.toml --features generic-queue
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
cargo test --manifest-path ./embassy-boot/boot/Cargo.toml
|
||||||
@ -28,3 +32,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,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,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-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
18
.vscode/settings.json
vendored
@ -6,16 +6,21 @@
|
|||||||
"rust-analyzer.check.allTargets": false,
|
"rust-analyzer.check.allTargets": false,
|
||||||
"rust-analyzer.check.noDefaultFeatures": true,
|
"rust-analyzer.check.noDefaultFeatures": true,
|
||||||
"rust-analyzer.cargo.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.target": "thumbv8m.main-none-eabihf",
|
||||||
"rust-analyzer.cargo.features": [
|
"rust-analyzer.cargo.features": [
|
||||||
///"nightly",
|
// Uncomment if the example has a "nightly" feature.
|
||||||
|
"nightly",
|
||||||
],
|
],
|
||||||
"rust-analyzer.linkedProjects": [
|
"rust-analyzer.linkedProjects": [
|
||||||
// Declare for the target you wish to develop
|
// Uncomment ONE line for the chip you want to work on.
|
||||||
// "embassy-executor/Cargo.toml",
|
// This makes rust-analyzer work on the example crate and all its dependencies.
|
||||||
// "embassy-sync/Cargo.toml",
|
"examples/nrf52840/Cargo.toml",
|
||||||
"examples/stm32wl/Cargo.toml",
|
// "examples/nrf52840-rtic/Cargo.toml",
|
||||||
// "examples/nrf5340/Cargo.toml",
|
// "examples/nrf5340/Cargo.toml",
|
||||||
// "examples/nrf-rtos-trace/Cargo.toml",
|
// "examples/nrf-rtos-trace/Cargo.toml",
|
||||||
// "examples/rp/Cargo.toml",
|
// "examples/rp/Cargo.toml",
|
||||||
@ -25,6 +30,7 @@
|
|||||||
// "examples/stm32f1/Cargo.toml",
|
// "examples/stm32f1/Cargo.toml",
|
||||||
// "examples/stm32f2/Cargo.toml",
|
// "examples/stm32f2/Cargo.toml",
|
||||||
// "examples/stm32f3/Cargo.toml",
|
// "examples/stm32f3/Cargo.toml",
|
||||||
|
// "examples/stm32f334/Cargo.toml",
|
||||||
// "examples/stm32f4/Cargo.toml",
|
// "examples/stm32f4/Cargo.toml",
|
||||||
// "examples/stm32f7/Cargo.toml",
|
// "examples/stm32f7/Cargo.toml",
|
||||||
// "examples/stm32g0/Cargo.toml",
|
// "examples/stm32g0/Cargo.toml",
|
||||||
|
15
README.md
15
README.md
@ -33,6 +33,7 @@ The <a href="https://docs.embassy.dev/embassy-net/">embassy-net</a> network stac
|
|||||||
|
|
||||||
- **Bluetooth** -
|
- **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/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** -
|
- **LoRa** -
|
||||||
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
|
<a href="https://docs.embassy.dev/embassy-lora/">embassy-lora</a> supports LoRa networking.
|
||||||
@ -61,9 +62,9 @@ async fn blink(pin: AnyPin) {
|
|||||||
loop {
|
loop {
|
||||||
// Timekeeping is globally available, no need to mess with hardware timers.
|
// Timekeeping is globally available, no need to mess with hardware timers.
|
||||||
led.set_high();
|
led.set_high();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
led.set_low();
|
led.set_low();
|
||||||
Timer::after(Duration::from_millis(150)).await;
|
Timer::after_millis(150).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +112,12 @@ cargo install probe-rs --features cli
|
|||||||
cd examples/nrf52840
|
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
|
- Run the example
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
@ -119,6 +126,8 @@ For example:
|
|||||||
cargo run --release --bin blinky
|
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
|
## 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/)
|
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.
|
at your option.
|
||||||
|
|
||||||
|
[1]: https://github.com/embassy-rs/embassy/wiki/Getting-Started
|
||||||
|
[2]: https://github.com/embassy-rs/embassy/wiki/Running-the-Examples
|
||||||
|
154
ci.sh
154
ci.sh
@ -1,13 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -euo pipefail
|
set -eo pipefail
|
||||||
|
|
||||||
export RUSTFLAGS=-Dwarnings
|
export RUSTFLAGS=-Dwarnings
|
||||||
export DEFMT_LOG=trace,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info
|
||||||
|
if [[ -z "${CARGO_TARGET_DIR}" ]]; then
|
||||||
# needed by wifi examples
|
export CARGO_TARGET_DIR=target_ci
|
||||||
export WIFI_NETWORK=x
|
fi
|
||||||
export WIFI_PASSWORD=x
|
|
||||||
|
|
||||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||||
|
|
||||||
@ -23,21 +22,33 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
--- 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-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
|
||||||
--- build --release --manifest-path embassy-sync/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,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 \
|
||||||
--- 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,dhcpv4-hostname \
|
||||||
--- 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 \
|
||||||
--- 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,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 \
|
||||||
--- 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,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,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,nrf52810,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nightly,nrf52811,gpiote,time-driver-rtc1 \
|
||||||
@ -57,44 +68,69 @@ 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,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 \
|
||||||
--- 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,intrinsics \
|
||||||
--- 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-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 \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt,exti,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,nightly,defmt \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f401ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f405zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f407zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f411ce,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f412zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f413vh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32f415zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f417zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f423zh,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f427zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f429zi,log,exti,time-driver-any,unstable-traits,embedded-sdmmc,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f437zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f439zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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,stm32f446ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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,stm32f469zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f479zi,defmt,exti,time-driver-any,unstable-traits,embedded-sdmmc,time \
|
||||||
--- 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 thumbv7em-none-eabi --features nightly,stm32f730i8,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h753zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h735zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32h7b3ai,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32l422cb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wb15cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l072cz,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l073cz,defmt,exti,time-driver-any,unstable-traits,low-power,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h503rb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32h562ag,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \
|
||||||
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
--- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \
|
||||||
@ -119,6 +155,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/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/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/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/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/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 \
|
--- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \
|
||||||
@ -132,9 +169,10 @@ cargo batch \
|
|||||||
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
--- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \
|
||||||
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \
|
||||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wb \
|
||||||
|
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \
|
||||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32wl \
|
||||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf \
|
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \
|
||||||
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf \
|
--- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \
|
||||||
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
|
--- build --release --manifest-path examples/boot/application/rp/Cargo.toml --target thumbv6m-none-eabi --features skip-include --out-dir out/examples/boot/rp \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
|
--- build --release --manifest-path examples/boot/application/stm32f3/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f3 \
|
||||||
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
|
--- build --release --manifest-path examples/boot/application/stm32f7/Cargo.toml --target thumbv7em-none-eabi --features skip-include --out-dir out/examples/boot/stm32f7 \
|
||||||
@ -157,12 +195,26 @@ cargo batch \
|
|||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/stm32wb55rg \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h563zi --out-dir out/tests/stm32h563zi \
|
||||||
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/stm32u585ai \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l073rz --out-dir out/tests/stm32l073rz \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l152re --out-dir out/tests/stm32l152re \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4a6zg --out-dir out/tests/stm32l4a6zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r5zi --out-dir out/tests/stm32l4r5zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze --out-dir out/tests/stm32l552ze \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f767zi --out-dir out/tests/stm32f767zi \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f207zg --out-dir out/tests/stm32f207zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303ze --out-dir out/tests/stm32f303ze \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l496zg --out-dir out/tests/stm32l496zg \
|
||||||
|
--- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55jc --out-dir out/tests/stm32wl55jc \
|
||||||
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
--- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \
|
||||||
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
--- build --release --manifest-path tests/nrf/Cargo.toml --target thumbv7em-none-eabi --out-dir out/tests/nrf52840-dk \
|
||||||
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
--- build --release --manifest-path tests/riscv32/Cargo.toml --target riscv32imac-unknown-none-elf \
|
||||||
$BUILD_EXTRA
|
$BUILD_EXTRA
|
||||||
|
|
||||||
|
|
||||||
|
rm out/tests/stm32wb55rg/wpan_mac
|
||||||
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
rm out/tests/stm32f207zg/eth
|
||||||
|
|
||||||
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
||||||
echo No teleprobe token found, skipping running HIL tests
|
echo No teleprobe token found, skipping running HIL tests
|
||||||
exit
|
exit
|
||||||
|
64
ci_stable.sh
64
ci_stable.sh
@ -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-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,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 \
|
||||||
--- 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 \
|
||||||
--- 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 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 nrf52810,gpiote,time-driver-rtc1 \
|
||||||
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52811,gpiote,time-driver-rtc1 \
|
||||||
@ -36,38 +34,44 @@ 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,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 --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 \
|
||||||
|
--- 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 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 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 \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32g473cc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,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,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55vy,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wl55cc-cm4,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l4r9zi,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f303vc,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f411ce,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f410tb,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32f429zi,log,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32l072cz,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,time \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features stm32f217zg,defmt,exti,time-driver-any,unstable-traits,time \
|
||||||
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --no-default-features --out-dir out/examples/nrf52840 --bin raw_spawn \
|
||||||
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
--- build --release --manifest-path examples/stm32l0/Cargo.toml --target thumbv6m-none-eabi --no-default-features --out-dir out/examples/stm32l0 --bin raw_spawn \
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -2,4 +2,8 @@
|
|||||||
|
|
||||||
Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439
|
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
|
||||||
|
@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43;
|
|||||||
use embassy_rp::dma::Channel;
|
use embassy_rp::dma::Channel;
|
||||||
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
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::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
use pio_proc::pio_asm;
|
use pio_proc::pio_asm;
|
||||||
@ -88,8 +87,6 @@ where
|
|||||||
".wrap"
|
".wrap"
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&program.program);
|
|
||||||
|
|
||||||
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
||||||
pin_io.set_pull(Pull::None);
|
pin_io.set_pull(Pull::None);
|
||||||
pin_io.set_schmitt(true);
|
pin_io.set_schmitt(true);
|
||||||
@ -102,7 +99,8 @@ where
|
|||||||
pin_clk.set_slew_rate(SlewRate::Fast);
|
pin_clk.set_slew_rate(SlewRate::Fast);
|
||||||
|
|
||||||
let mut cfg = Config::default();
|
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_out_pins(&[&pin_io]);
|
||||||
cfg.set_in_pins(&[&pin_io]);
|
cfg.set_in_pins(&[&pin_io]);
|
||||||
cfg.set_set_pins(&[&pin_io]);
|
cfg.set_set_pins(&[&pin_io]);
|
||||||
@ -142,7 +140,7 @@ where
|
|||||||
sm,
|
sm,
|
||||||
irq,
|
irq,
|
||||||
dma: dma.into_ref(),
|
dma: dma.into_ref(),
|
||||||
wrap_target: relocated.wrap().target,
|
wrap_target: loaded_program.wrap.target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,11 +11,10 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time"}
|
embassy-time = { version = "0.1.5", path = "../embassy-time"}
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync"}
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync"}
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
|
||||||
embassy-net-driver-channel = { version = "0.1.0", path = "../embassy-net-driver-channel"}
|
embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
|
||||||
atomic-polyfill = "0.1.5"
|
|
||||||
|
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.17", optional = true }
|
log = { version = "0.4.17", optional = true }
|
||||||
@ -24,11 +23,11 @@ cortex-m = "0.7.6"
|
|||||||
cortex-m-rt = "0.7.0"
|
cortex-m-rt = "0.7.0"
|
||||||
futures = { version = "0.3.17", default-features = false, features = ["async-await", "cfg-target-has-atomic", "unstable"] }
|
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 }
|
num_enum = { version = "0.5.7", default-features = false }
|
||||||
|
|
||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
|
src_base = "https://github.com/embassy-rs/embassy/blob/cyw43-v$VERSION/cyw43/src/"
|
||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/cyw43/src/"
|
||||||
target = "thumbv6m-none-eabi"
|
target = "thumbv6m-none-eabi"
|
||||||
features = ["defmt", "firmware-logs"]
|
features = ["defmt", "firmware-logs"]
|
||||||
|
@ -30,7 +30,7 @@ TODO:
|
|||||||
### Example 2: Create an access point (IP and credentials in the code)
|
### Example 2: Create an access point (IP and credentials in the code)
|
||||||
- `cargo run --release --bin wifi_ap_tcp_server`
|
- `cargo run --release --bin wifi_ap_tcp_server`
|
||||||
### Example 3: Connect to an existing network and create a 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
|
After a few seconds, you should see that DHCP picks up an IP address like this
|
||||||
```
|
```
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use embassy_futures::yield_now;
|
use embassy_futures::yield_now;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::Timer;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
use embedded_hal_1::digital::OutputPin;
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
|
||||||
@ -51,9 +51,9 @@ where
|
|||||||
pub async fn init(&mut self) {
|
pub async fn init(&mut self) {
|
||||||
// Reset
|
// Reset
|
||||||
self.pwr.set_low().unwrap();
|
self.pwr.set_low().unwrap();
|
||||||
Timer::after(Duration::from_millis(20)).await;
|
Timer::after_millis(20).await;
|
||||||
self.pwr.set_high().unwrap();
|
self.pwr.set_high().unwrap();
|
||||||
Timer::after(Duration::from_millis(250)).await;
|
Timer::after_millis(250).await;
|
||||||
|
|
||||||
while self
|
while self
|
||||||
.read32_swapped(REG_BUS_TEST_RO)
|
.read32_swapped(REG_BUS_TEST_RO)
|
||||||
@ -102,7 +102,7 @@ where
|
|||||||
cmd_buf[0] = cmd;
|
cmd_buf[0] = cmd;
|
||||||
cmd_buf[1..][..buf.len()].copy_from_slice(buf);
|
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)]
|
#[allow(unused)]
|
||||||
|
@ -96,6 +96,7 @@ pub(crate) const IOCTL_CMD_UP: u32 = 2;
|
|||||||
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
|
pub(crate) const IOCTL_CMD_DOWN: u32 = 3;
|
||||||
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
|
pub(crate) const IOCTL_CMD_SET_SSID: u32 = 26;
|
||||||
pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
|
pub(crate) const IOCTL_CMD_SET_CHANNEL: u32 = 30;
|
||||||
|
pub(crate) const IOCTL_CMD_DISASSOC: u32 = 52;
|
||||||
pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
|
pub(crate) const IOCTL_CMD_ANTDIV: u32 = 64;
|
||||||
pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
|
pub(crate) const IOCTL_CMD_SET_AP: u32 = 118;
|
||||||
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
|
pub(crate) const IOCTL_CMD_SET_VAR: u32 = 263;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use core::cmp::{max, min};
|
use core::cmp::{max, min};
|
||||||
|
|
||||||
use ch::driver::LinkState;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_net_driver_channel::driver::{HardwareAddress, LinkState};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
use crate::consts::*;
|
use crate::consts::*;
|
||||||
@ -87,22 +87,22 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar("country", &country_info.to_bytes()).await;
|
self.set_iovar("country", &country_info.to_bytes()).await;
|
||||||
|
|
||||||
// set country takes some time, next ioctls fail if we don't wait.
|
// set country takes some time, next ioctls fail if we don't wait.
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set antenna to chip antenna
|
// Set antenna to chip antenna
|
||||||
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
self.ioctl_set_u32(IOCTL_CMD_ANTDIV, 0, 0).await;
|
||||||
|
|
||||||
self.set_iovar_u32("bus:txglom", 0).await;
|
self.set_iovar_u32("bus:txglom", 0).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
//self.set_iovar_u32("apsta", 1).await; // this crashes, also we already did it before...??
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
self.set_iovar_u32("ampdu_ba_wsize", 8).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
self.set_iovar_u32("ampdu_mpdu", 4).await;
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
//self.set_iovar_u32("ampdu_rx_factor", 0).await; // this crashes
|
||||||
|
|
||||||
//Timer::after(Duration::from_millis(100)).await;
|
//Timer::after_millis(100).await;
|
||||||
|
|
||||||
// evts
|
// evts
|
||||||
let mut evts = EventMask {
|
let mut evts = EventMask {
|
||||||
@ -121,23 +121,33 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
self.set_iovar("bsscfg:event_msgs", &evts.to_bytes()).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// set wifi up
|
// set wifi up
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
self.up().await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
self.ioctl_set_u32(110, 0, 1).await; // SET_GMODE = auto
|
||||||
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
self.ioctl_set_u32(142, 0, 0).await; // SET_BAND = any
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
self.state_ch.set_ethernet_address(mac_addr);
|
self.state_ch.set_hardware_address(HardwareAddress::Ethernet(mac_addr));
|
||||||
|
|
||||||
debug!("INIT DONE");
|
debug!("INIT DONE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the WiFi interface up.
|
||||||
|
async fn up(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interface down.
|
||||||
|
async fn down(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
pub async fn set_power_management(&mut self, mode: PowerManagementMode) {
|
||||||
// power save mode
|
// power save mode
|
||||||
let mode_num = mode.mode();
|
let mode_num = mode.mode();
|
||||||
@ -175,7 +185,7 @@ impl<'a> Control<'a> {
|
|||||||
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa2_eapver", 0, 0xFFFF_FFFF).await;
|
||||||
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
self.set_iovar_u32x2("bsscfg:sup_wpa_tmo", 0, 2500).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
len: passphrase.len() as _,
|
len: passphrase.len() as _,
|
||||||
@ -256,13 +266,13 @@ impl<'a> Control<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily set wifi down
|
// Temporarily set wifi down
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_DOWN, 0, &mut []).await;
|
self.down().await;
|
||||||
|
|
||||||
// Turn off APSTA mode
|
// Turn off APSTA mode
|
||||||
self.set_iovar_u32("apsta", 0).await;
|
self.set_iovar_u32("apsta", 0).await;
|
||||||
|
|
||||||
// Set wifi up again
|
// Set wifi up again
|
||||||
self.ioctl(IoctlType::Set, IOCTL_CMD_UP, 0, &mut []).await;
|
self.up().await;
|
||||||
|
|
||||||
// Turn on AP mode
|
// Turn on AP mode
|
||||||
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
|
self.ioctl_set_u32(IOCTL_CMD_SET_AP, 0, 1).await;
|
||||||
@ -287,7 +297,7 @@ impl<'a> Control<'a> {
|
|||||||
if security != Security::OPEN {
|
if security != Security::OPEN {
|
||||||
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
self.set_iovar_u32x2("bsscfg:wpa_auth", 0, 0x0084).await; // wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
// Set passphrase
|
// Set passphrase
|
||||||
let mut pfi = PassphraseInfo {
|
let mut pfi = PassphraseInfo {
|
||||||
@ -423,6 +433,11 @@ impl<'a> Control<'a> {
|
|||||||
events: &self.events,
|
events: &self.events,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Leave the wifi, with which we are currently associated.
|
||||||
|
pub async fn leave(&mut self) {
|
||||||
|
self.ioctl(IoctlType::Set, IOCTL_CMD_DISASSOC, 0, &mut []).await;
|
||||||
|
info!("Disassociated")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scanner<'a> {
|
pub struct Scanner<'a> {
|
||||||
|
@ -83,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +229,8 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Bytes<'a>(pub &'a [u8]);
|
#[allow(unused)]
|
||||||
|
pub(crate) struct Bytes<'a>(pub &'a [u8]);
|
||||||
|
|
||||||
impl<'a> Debug for Bytes<'a> {
|
impl<'a> Debug for Bytes<'a> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
@ -27,7 +27,7 @@ use ioctl::IoctlState;
|
|||||||
|
|
||||||
use crate::bus::Bus;
|
use crate::bus::Bus;
|
||||||
pub use crate::bus::SpiBusCyw43;
|
pub use crate::bus::SpiBusCyw43;
|
||||||
pub use crate::control::{Control, Error as ControlError};
|
pub use crate::control::{Control, Error as ControlError, Scanner};
|
||||||
pub use crate::runner::Runner;
|
pub use crate::runner::Runner;
|
||||||
pub use crate::structs::BssInfo;
|
pub use crate::structs::BssInfo;
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ where
|
|||||||
PWR: OutputPin,
|
PWR: OutputPin,
|
||||||
SPI: SpiBusCyw43,
|
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 state_ch = ch_runner.state_runner();
|
||||||
|
|
||||||
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
|
let mut runner = Runner::new(ch_runner, Bus::new(pwr, spi), &state.ioctl_state, &state.events);
|
||||||
|
@ -345,7 +345,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rx(&mut self, packet: &mut [u8]) {
|
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);
|
self.update_credit(&sdpcm_header);
|
||||||
|
|
||||||
@ -353,7 +355,9 @@ where
|
|||||||
|
|
||||||
match channel {
|
match channel {
|
||||||
CHANNEL_TYPE_CONTROL => {
|
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);
|
trace!(" {:?}", cdc_header);
|
||||||
|
|
||||||
if cdc_header.id == self.ioctl_id {
|
if cdc_header.id == self.ioctl_id {
|
||||||
@ -417,8 +421,12 @@ where
|
|||||||
let status = event_packet.msg.status;
|
let status = event_packet.msg.status;
|
||||||
let event_payload = match evt_type {
|
let event_payload = match evt_type {
|
||||||
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
Event::ESCAN_RESULT if status == EStatus::PARTIAL => {
|
||||||
let Some((_, bss_info)) = ScanResults::parse(evt_data) else { return };
|
let Some((_, bss_info)) = ScanResults::parse(evt_data) else {
|
||||||
let Some(bss_info) = BssInfo::parse(bss_info) else { return };
|
return;
|
||||||
|
};
|
||||||
|
let Some(bss_info) = BssInfo::parse(bss_info) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
events::Payload::BssInfo(*bss_info)
|
events::Payload::BssInfo(*bss_info)
|
||||||
}
|
}
|
||||||
Event::ESCAN_RESULT => events::Payload::None,
|
Event::ESCAN_RESULT => events::Payload::None,
|
||||||
@ -439,7 +447,9 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CHANNEL_TYPE_DATA => {
|
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)]));
|
trace!("rx pkt {:02x}", Bytes(&packet[..packet.len().min(48)]));
|
||||||
|
|
||||||
match self.ch.try_rx_buf() {
|
match self.ch.try_rx_buf() {
|
||||||
@ -545,14 +555,14 @@ where
|
|||||||
|
|
||||||
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
self.bus.bp_write8(base + AI_RESETCTRL_OFFSET, 0).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
|
|
||||||
self.bus
|
self.bus
|
||||||
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
.bp_write8(base + AI_IOCTRL_OFFSET, AI_IOCTRL_BIT_CLOCK_EN)
|
||||||
.await;
|
.await;
|
||||||
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
let _ = self.bus.bp_read8(base + AI_IOCTRL_OFFSET).await;
|
||||||
|
|
||||||
Timer::after(Duration::from_millis(1)).await;
|
Timer::after_millis(1).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn core_is_up(&mut self, core: Core) -> bool {
|
async fn core_is_up(&mut self, core: Core) -> bool {
|
||||||
|
@ -6,8 +6,8 @@ version = "0.1.0"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[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-time = { version = "0.1.4", path = "../../../../../embassy-time", features = ["defmt", "nightly"] }
|
||||||
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
embassy-nrf = { version = "0.1.0", path = "../../../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "nightly"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] }
|
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 = "0.3.0"
|
||||||
defmt-rtt = "0.3.0"
|
defmt-rtt = "0.3.0"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
* xref:getting_started.adoc[Getting started]
|
* xref:getting_started.adoc[Getting started]
|
||||||
** xref:basic_application.adoc[Basic application]
|
** xref:basic_application.adoc[Basic application]
|
||||||
** xref:layer_by_layer.adoc[Layer by Layer]
|
* xref:layer_by_layer.adoc[Bare metal to async]
|
||||||
* xref:runtime.adoc[Executor]
|
* xref:runtime.adoc[Executor]
|
||||||
* xref:hal.adoc[HAL]
|
* xref:hal.adoc[HAL]
|
||||||
** xref:nrf.adoc[nRF]
|
** xref:nrf.adoc[nRF]
|
||||||
@ -8,3 +8,5 @@
|
|||||||
* xref:bootloader.adoc[Bootloader]
|
* xref:bootloader.adoc[Bootloader]
|
||||||
|
|
||||||
* xref:examples.adoc[Examples]
|
* xref:examples.adoc[Examples]
|
||||||
|
* xref:developer.adoc[Developer]
|
||||||
|
** xref:developer_stm32.adoc[Developer: STM32]
|
||||||
|
@ -48,7 +48,7 @@ The `Spawner` is the way the main application spawns other tasks. The `Periphera
|
|||||||
include::example$basic/src/main.rs[lines="22..-1"]
|
include::example$basic/src/main.rs[lines="22..-1"]
|
||||||
----
|
----
|
||||||
|
|
||||||
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy::main]` macro. The macro does the following:
|
What happens when the `blinker` task has been spawned and main returns? Well, the main entry point is actually just like any other task, except that you can only have one and it takes some specific type arguments. The magic lies within the `#[embassy_executor::main]` macro. The macro does the following:
|
||||||
|
|
||||||
. Creates an Embassy Executor
|
. Creates an Embassy Executor
|
||||||
. Initializes the microcontroller HAL to get the `Peripherals`
|
. Initializes the microcontroller HAL to get the `Peripherals`
|
||||||
|
1
docs/modules/ROOT/pages/developer.adoc
Normal file
1
docs/modules/ROOT/pages/developer.adoc
Normal file
@ -0,0 +1 @@
|
|||||||
|
= Developer Documentation
|
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
79
docs/modules/ROOT/pages/developer_stm32.adoc
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
= Developer Documentation: STM32
|
||||||
|
|
||||||
|
== Understanding metapac
|
||||||
|
|
||||||
|
When a project that imports `embassy-stm32` is compiled, that project selects the feature corresponding to the chip that project is using. Based on that feature, `embassy-stm32` selects supported link:https://anysilicon.com/ip-intellectual-property-core-semiconductors/[IP] for the chip, and enables the corresponding HAL implementations. But how does `embassy-stm32` know what IP the chip contains, out of the hundreds of chips that we support? It's a long story that starts with `stm32-data-sources`.
|
||||||
|
|
||||||
|
== `stm32-data-sources`
|
||||||
|
|
||||||
|
link:https://github.com/embassy-rs/stm32-data-sources[`stm32-data-sources`] is as mostly barren repository. It has no README, no documentation, and few watchers. But it's the core of what makes `embassy-stm32` possible. The data for every chip that we support is taken in part from a corresponding XML file like link:https://github.com/embassy-rs/stm32-data-sources/blob/b8b85202e22a954d6c59d4a43d9795d34cff05cf/cubedb/mcu/STM32F051K4Ux.xml[`STM32F051K4Ux.xml`]. In that file, you'll see lines like the following:
|
||||||
|
|
||||||
|
[source,xml]
|
||||||
|
----
|
||||||
|
<IP InstanceName="I2C1" Name="I2C" Version="i2c2_v1_1_Cube"/>
|
||||||
|
<!-- snip -->
|
||||||
|
<IP ConfigFile="TIM-STM32F0xx" InstanceName="TIM1" Name="TIM1_8F0" Version="gptimer2_v2_x_Cube"/>
|
||||||
|
----
|
||||||
|
|
||||||
|
These lines indicate that this chip has an i2c, and that it's version is "v1_1". It also indicates that it has a general purpose timer that with a version of "v2_x". From this data, it's possible to determine which implementations should be included in `embassy-stm32`. But actually doing that is another matter.
|
||||||
|
|
||||||
|
|
||||||
|
== `stm32-data`
|
||||||
|
|
||||||
|
While all users of this project are familiar with `embassy-stm32`, fewer are familiar with the project that powers it: `stm32-data`. This project doesn't just aim to generate data for `embassy-stm32`, but for machine consumption in general. To acheive this, information from multiple files from the `stm32-data-sources` project are combined and parsed to assign register block implementations for each supported IP. The core of this matching resides in `chips.rs`:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
(".*:I2C:i2c2_v1_1", ("i2c", "v2", "I2C")),
|
||||||
|
// snip
|
||||||
|
(r".*TIM\d.*:gptimer.*", ("timer", "v1", "TIM_GP16")),
|
||||||
|
----
|
||||||
|
|
||||||
|
In this case, the i2c version corresponds to our "v2" and the general purpose timer version corresponds to our "v1". Therefore, the `i2c_v2.yaml` and `timer_v1.yaml` register block implementations are assigned to those IP, respectively. The result is that these lines arr generated in `STM32F051K4.json`:
|
||||||
|
|
||||||
|
[source,json]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"name": "I2C1",
|
||||||
|
"address": 1073763328,
|
||||||
|
"registers": {
|
||||||
|
"kind": "i2c",
|
||||||
|
"version": "v2",
|
||||||
|
"block": "I2C"
|
||||||
|
},
|
||||||
|
// snip
|
||||||
|
}
|
||||||
|
// snip
|
||||||
|
{
|
||||||
|
"name": "TIM1",
|
||||||
|
"address": 1073818624,
|
||||||
|
"registers": {
|
||||||
|
"kind": "timer",
|
||||||
|
"version": "v1",
|
||||||
|
"block": "TIM_ADV"
|
||||||
|
},
|
||||||
|
// snip
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
In addition to register blocks, data for pin and RCC mapping is also generated and consumed by `embassy-stm32`. `stm32-metapac-gen` is used to package and publish the data as a crate.
|
||||||
|
|
||||||
|
|
||||||
|
== `embassy-stm32`
|
||||||
|
|
||||||
|
In the `lib.rs` file located in the root of `embassy-stm32`, you'll see this line:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
#[cfg(i2c)]
|
||||||
|
pub mod i2c;
|
||||||
|
----
|
||||||
|
|
||||||
|
And in the `mod.rs` of the i2c mod, you'll see this:
|
||||||
|
|
||||||
|
[source,rust]
|
||||||
|
----
|
||||||
|
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||||
|
----
|
||||||
|
|
||||||
|
Because i2c is supported for STM32F051K4 and its version corresponds to our "v2", the `i2c` and `i2c_v2`, configuration directives will be present, and `embassy-stm32` will include these files, respectively. This and other configuration directives and tables are generated from the data for chip, allowing `embassy-stm32` to expressively and clearly adapt logic and implementations to what is required for each chip. Compared to other projects across the embedded ecosystem, `embassy-stm32` is the only project that can re-use code across the entire stm32 lineup and remove difficult-to-implement unsafe logic to the HAL.
|
@ -3,7 +3,7 @@
|
|||||||
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
So you want to try Embassy, great! To get started, there are a few tools you need to install:
|
||||||
|
|
||||||
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
* link:https://rustup.rs/[rustup] - the Rust toolchain is needed to compile Rust code.
|
||||||
* link:https://crates.io/crates/probe-run[probe-run] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
* link:https://crates.io/crates/probe-rs[probe-rs] - to flash the firmware on your device. If you already have other tools like `OpenOCD` setup, you can use that as well.
|
||||||
|
|
||||||
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
If you don't have any supported board, don't worry: you can also run embassy on your PC using the `std` examples.
|
||||||
|
|
||||||
@ -30,6 +30,10 @@ Embassy supports many microcontroller families, but the easiest ways to get star
|
|||||||
|
|
||||||
* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]
|
* link:https://www.raspberrypi.com/products/raspberry-pi-pico/[Raspberry Pi Pico]
|
||||||
|
|
||||||
|
=== ESP32
|
||||||
|
|
||||||
|
* link:https://github.com/esp-rs/esp-rust-board[ESP32C3]
|
||||||
|
|
||||||
== Running an example
|
== Running an example
|
||||||
|
|
||||||
First you need to clone the [github repository];
|
First you need to clone the [github repository];
|
||||||
@ -38,7 +42,6 @@ First you need to clone the [github repository];
|
|||||||
----
|
----
|
||||||
git clone https://github.com/embassy-rs/embassy.git
|
git clone https://github.com/embassy-rs/embassy.git
|
||||||
cd embassy
|
cd embassy
|
||||||
git submodule update --init
|
|
||||||
----
|
----
|
||||||
|
|
||||||
You can run an example by opening a terminal and entering the following commands:
|
You can run an example by opening a terminal and entering the following commands:
|
||||||
|
@ -7,4 +7,6 @@ Embassy provides HALs for several microcontroller families:
|
|||||||
* `embassy-rp` for the Raspberry Pi RP2040 microcontrollers
|
* `embassy-rp` for the Raspberry Pi RP2040 microcontrollers
|
||||||
|
|
||||||
These HALs implement async/await functionality for most peripherals while also implementing the
|
These HALs implement async/await functionality for most peripherals while also implementing the
|
||||||
async traits in `embedded-hal-async`. You can also use these HALs with another executor.
|
async traits in `embedded-hal` and `embedded-hal-async`. You can also use these HALs with another executor.
|
||||||
|
|
||||||
|
For the ESP32 series, there is an link:https://github.com/esp-rs/esp-hal[esp-hal] which you can use.
|
||||||
|
@ -4,14 +4,9 @@ Embassy is a project to make async/await a first-class option for embedded devel
|
|||||||
|
|
||||||
== What is async?
|
== What is async?
|
||||||
|
|
||||||
Software written without async may block on I/O operations. In an std environment, such as a PC, software can handle this either by using threads or non-blocking operations.
|
When handling I/O, software must call functions that block program execution until the I/O operation completes. When running inside of an OS such as Linux, such functions generally transfer control to the kernel so that another task, known as a thread, can be executed if available, or the CPU can be put to sleep until another such task is ready to perform more work. Because an OS cannot presume that threads will behave cooperatively, threads are relatively resource-intensive, and may be forcibly interrupted they do not transfer control back to the kernel within an allotted time. But if tasks could be presumed to behave cooperatively, or at least not maliciously, it would be possible to create tasks that appear to be almost free when compared to a traditional OS thread. In Rust, these lightweight tasks, known as 'coroutines' or 'goroutines' in other languages, are implemented with async.
|
||||||
|
|
||||||
With threads, one thread blocks on an I/O operation, another is able to take its place. However, even on a PC, threads are relatively heavy, and therefore some programming languages, such as Go, have implemented a concept called coroutines or 'goroutines' that are much lighter and less-intensive than threads.
|
Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
||||||
|
|
||||||
The other way to handle blocking I/O operations is to support polling the state of the underlying peripherals to check whether it is available to perform the requested operation. In programming languages without builtin async support,
|
|
||||||
this requires building a complex loop checking for events.
|
|
||||||
|
|
||||||
In Rust, non-blocking operations can be implemented using async-await. Async-await works by transforming each async function into an object called a future. When a future blocks on I/O the future yields, and the scheduler, called an executor, can select a different future to execute. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn't have to guess when a future is ready to execute. However, program size may be higher than other alternatives, which may be a problem for certain space-constrained devices with very low memory. On the devices Embassy supports, such as stm32 and nrf, memory is generally large enough to accommodate the modestly-increased program size.
|
|
||||||
|
|
||||||
== What is Embassy?
|
== What is Embassy?
|
||||||
|
|
||||||
@ -22,13 +17,26 @@ The Embassy project consists of several crates that you can use together or inde
|
|||||||
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
* **Hardware Abstraction Layers** - HALs implement safe, idiomatic Rust APIs to use the hardware capabilities, so raw register manipulation is not needed. The Embassy project maintains HALs for select hardware, but you can still use HALs from other projects with Embassy.
|
||||||
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
** link:https://docs.embassy.dev/embassy-stm32/[embassy-stm32], for all STM32 microcontroller families.
|
||||||
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
** link:https://docs.embassy.dev/embassy-nrf/[embassy-nrf], for the Nordic Semiconductor nRF52, nRF53, nRF91 series.
|
||||||
|
** link:https://docs.embassy.dev/embassy-rp/[embassy-rp], for the Raspberry Pi RP2040 microcontroller.
|
||||||
|
** link:https://github.com/esp-rs[esp-rs], for the Espressif Systems ESP32 series of chips.
|
||||||
|
+
|
||||||
|
NOTE: A common question is if one can use the Embassy HALs standalone. Yes, it is possible! There are no dependency on the executor within the HALs. You can even use them without async,
|
||||||
|
as they implement both the link:https://github.com/rust-embedded/embedded-hal[Embedded HAL] blocking and async traits.
|
||||||
|
|
||||||
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently.
|
* **Networking** - The link:https://docs.embassy.dev/embassy-net/[embassy-net] network stack implements extensive networking functionality, including Ethernet, IP, TCP, UDP, ICMP and DHCP. Async drastically simplifies managing timeouts and serving multiple connections concurrently. Several drivers for WiFi and Ethernet chips can be found.
|
||||||
|
|
||||||
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
* **Bluetooth** - The link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate provides Bluetooth Low Energy 4.x and 5.x support for nRF52 microcontrollers.
|
||||||
|
|
||||||
* **LoRa** - link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on STM32WL wireless microcontrollers and Semtech SX127x transceivers.
|
* **LoRa** - link:https://github.com/embassy-rs/lora-phy[lora-phy] and link:https://docs.embassy.dev/embassy-lora/[embassy-lora] supports LoRa networking on a wide range of LoRa radios, fully integrated with a Rust link:https://github.com/ivajloip/rust-lorawan[LoRaWAN] implementation.
|
||||||
|
|
||||||
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
* **USB** - link:https://docs.embassy.dev/embassy-usb/[embassy-usb] implements a device-side USB stack. Implementations for common classes such as USB serial (CDC ACM) and USB HID are available, and a rich builder API allows building your own.
|
||||||
|
|
||||||
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
* **Bootloader and DFU** - link:https://github.com/embassy-rs/embassy/tree/master/embassy-boot[embassy-boot] is a lightweight bootloader supporting firmware application upgrades in a power-fail-safe way, with trial boots and rollbacks.
|
||||||
|
|
||||||
|
== Resources
|
||||||
|
|
||||||
|
For more reading material on async Rust and Embassy:
|
||||||
|
|
||||||
|
* link:https://tweedegolf.nl/en/blog/65/async-rust-vs-rtos-showdown[Comparsion of FreeRTOS and Embassy]
|
||||||
|
* link:https://dev.to/apollolabsbin/series/20707[Tutorials]
|
||||||
|
* link:https://blog.drogue.io/firmware-updates-part-1/[Firmware Updates with Embassy]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
= Embassy layer by layer
|
= From bare metal to async Rust
|
||||||
|
|
||||||
If you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.
|
If you're new to Embassy, it can be overwhelming to grasp all the terminology and concepts. This guide aims to clarify the different layers in Embassy, which problem each layer solves for the application writer.
|
||||||
|
|
||||||
@ -8,8 +8,7 @@ The application we'll write is a simple 'push button, blink led' application, wh
|
|||||||
|
|
||||||
== PAC version
|
== PAC version
|
||||||
|
|
||||||
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types
|
The PAC is the lowest API for accessing peripherals and registers, if you don't count reading/writing directly to memory addresses. It provides distinct types to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
||||||
to make accessing peripheral registers easier, but it does not prevent you from writing unsafe code.
|
|
||||||
|
|
||||||
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
Writing an application using the PAC directly is therefore not recommended, but if the functionality you want to use is not exposed in the upper layers, that's what you need to use.
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ The nRF timer driver operates at 32768 Hz by default.
|
|||||||
|
|
||||||
== Peripherals
|
== Peripherals
|
||||||
|
|
||||||
The following peripherals have a HAL implementation at present:
|
The following peripherals have a HAL implementation at present
|
||||||
|
|
||||||
* PWM
|
* PWM
|
||||||
* SPIM
|
* SPIM
|
||||||
@ -23,3 +23,7 @@ The following peripherals have a HAL implementation at present:
|
|||||||
* UARTE
|
* UARTE
|
||||||
* TWIM
|
* TWIM
|
||||||
* SAADC
|
* SAADC
|
||||||
|
|
||||||
|
== Bluetooth
|
||||||
|
|
||||||
|
For bluetooth, you can use the link:https://github.com/embassy-rs/nrf-softdevice[nrf-softdevice] crate.
|
||||||
|
@ -6,11 +6,11 @@ The Embassy executor is an async/await executor designed for embedded usage alon
|
|||||||
|
|
||||||
* No `alloc`, no heap needed. Task are statically allocated.
|
* No `alloc`, no heap needed. Task are statically allocated.
|
||||||
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
* No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
* Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
* Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
* No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
* Efficient polling: a wake will only poll the woken task, not all of them.
|
* Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
* Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
* Creating multiple executor instances is supported, to run tasks with multiple priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
* Creating multiple executor instances is supported, to run tasks at different priority levels. This allows higher-priority tasks to preempt lower-priority tasks.
|
||||||
|
|
||||||
== Executor
|
== Executor
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ digest = "0.10"
|
|||||||
log = { version = "0.4", optional = true }
|
log = { version = "0.4", optional = true }
|
||||||
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
ed25519-dalek = { version = "1.0.1", default_features = false, features = ["u32_backend"], optional = true }
|
||||||
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal" }
|
||||||
embassy-sync = { version = "0.2.0", path = "../../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../../embassy-sync" }
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
salty = { git = "https://github.com/ycrypto/salty.git", rev = "a9f17911a5024698406b75c0fac56ab5ccf6a8c7", optional = true }
|
||||||
|
@ -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
|
/// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
pub struct FirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
|
pub struct FirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||||
dfu: DFU,
|
dfu: DFU,
|
||||||
state: STATE,
|
state: FirmwareState<'d, STATE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[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.
|
/// 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 {
|
Self {
|
||||||
dfu: config.dfu,
|
dfu: config.dfu,
|
||||||
state: config.state,
|
state: FirmwareState::new(config.state, aligned),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
/// 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
|
/// to do verifications and self-tests of the new image before calling
|
||||||
/// `mark_booted`.
|
/// `mark_booted`.
|
||||||
pub async fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
|
pub async fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
self.state.read(0, aligned).await?;
|
self.state.get_state().await
|
||||||
|
|
||||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
|
||||||
Ok(State::Swap)
|
|
||||||
} else {
|
|
||||||
Ok(State::Boot)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the DFU given a public key. If there is an error then DO NOT
|
/// 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
|
/// If no signature feature is set then this method will always return a
|
||||||
/// signature error.
|
/// 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")]
|
#[cfg(feature = "_verify")]
|
||||||
pub async fn verify_and_mark_updated(
|
pub async fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8],
|
||||||
_signature: &[u8],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
_aligned: &mut [u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
|
|
||||||
self.verify_booted(_aligned).await?;
|
self.state.verify_booted().await?;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[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 public_key = PublicKey::from_bytes(_public_key).map_err(into_signature_error)?;
|
||||||
let signature = Signature::from_bytes(_signature).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];
|
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)?
|
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 signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
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);
|
let r = public_key.verify(&message, &signature);
|
||||||
trace!(
|
trace!(
|
||||||
@ -156,7 +135,7 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
r.map_err(into_signature_error)?
|
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.
|
/// 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.
|
/// 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"))]
|
#[cfg(not(feature = "_verify"))]
|
||||||
pub async fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
pub async fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.mark_updated().await
|
||||||
self.set_magic(aligned, SWAP_MAGIC).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
///
|
pub async fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.mark_booted().await
|
||||||
///
|
|
||||||
/// 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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write data to a flash page.
|
/// Write data to a flash page.
|
||||||
@ -229,16 +173,10 @@ impl<DFU: NorFlash, STATE: NorFlash> FirmwareUpdater<DFU, STATE> {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Failing to meet alignment and size requirements may result in a panic.
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
pub async fn write_firmware(
|
pub async fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||||
&mut self,
|
|
||||||
aligned: &mut [u8],
|
|
||||||
offset: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
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?;
|
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
|
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||||
/// exchange for added complexity.
|
/// exchange for added complexity.
|
||||||
///
|
pub async fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.verify_booted().await?;
|
||||||
///
|
|
||||||
/// 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?;
|
|
||||||
|
|
||||||
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
|
self.dfu.erase(0, self.dfu.capacity() as u32).await?;
|
||||||
|
|
||||||
Ok(&mut self.dfu)
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use embassy_embedded_hal::flash::partition::Partition;
|
use embassy_embedded_hal::flash::partition::Partition;
|
||||||
@ -288,8 +300,8 @@ mod tests {
|
|||||||
let mut to_write = [0; 4096];
|
let mut to_write = [0; 4096];
|
||||||
to_write[..7].copy_from_slice(update.as_slice());
|
to_write[..7].copy_from_slice(update.as_slice());
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
|
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, to_write.as_slice())).unwrap();
|
block_on(updater.write_firmware(0, to_write.as_slice())).unwrap();
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut hash = [0; 20];
|
let mut hash = [0; 20];
|
||||||
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
block_on(updater.hash::<Sha1>(update.len() as u32, &mut chunk_buf, &mut hash)).unwrap();
|
||||||
|
@ -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
|
/// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to
|
||||||
/// 'mess up' the internal bootloader state
|
/// 'mess up' the internal bootloader state
|
||||||
pub struct BlockingFirmwareUpdater<DFU: NorFlash, STATE: NorFlash> {
|
pub struct BlockingFirmwareUpdater<'d, DFU: NorFlash, STATE: NorFlash> {
|
||||||
dfu: DFU,
|
dfu: DFU,
|
||||||
state: STATE,
|
state: BlockingFirmwareState<'d, STATE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "none")]
|
#[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.
|
/// 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 {
|
Self {
|
||||||
dfu: config.dfu,
|
dfu: config.dfu,
|
||||||
state: config.state,
|
state: BlockingFirmwareState::new(config.state, aligned),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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
|
/// 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
|
/// to do verifications and self-tests of the new image before calling
|
||||||
/// `mark_booted`.
|
/// `mark_booted`.
|
||||||
pub fn get_state(&mut self, aligned: &mut [u8]) -> Result<State, FirmwareUpdaterError> {
|
pub fn get_state(&mut self) -> Result<State, FirmwareUpdaterError> {
|
||||||
self.state.read(0, aligned)?;
|
self.state.get_state()
|
||||||
|
|
||||||
if !aligned.iter().any(|&b| b != SWAP_MAGIC) {
|
|
||||||
Ok(State::Swap)
|
|
||||||
} else {
|
|
||||||
Ok(State::Boot)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the DFU given a public key. If there is an error then DO NOT
|
/// 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
|
/// If no signature feature is set then this method will always return a
|
||||||
/// signature error.
|
/// 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")]
|
#[cfg(feature = "_verify")]
|
||||||
pub fn verify_and_mark_updated(
|
pub fn verify_and_mark_updated(
|
||||||
&mut self,
|
&mut self,
|
||||||
_public_key: &[u8],
|
_public_key: &[u8],
|
||||||
_signature: &[u8],
|
_signature: &[u8],
|
||||||
_update_len: u32,
|
_update_len: u32,
|
||||||
_aligned: &mut [u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(_aligned.len(), STATE::WRITE_SIZE);
|
|
||||||
assert!(_update_len <= self.dfu.capacity() as u32);
|
assert!(_update_len <= self.dfu.capacity() as u32);
|
||||||
|
|
||||||
self.verify_booted(_aligned)?;
|
self.state.verify_booted()?;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519-dalek")]
|
#[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 signature = Signature::from_bytes(_signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
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)?
|
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 signature = Signature::try_from(&signature).map_err(into_signature_error)?;
|
||||||
|
|
||||||
let mut message = [0; 64];
|
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);
|
let r = public_key.verify(&message, &signature);
|
||||||
trace!(
|
trace!(
|
||||||
@ -158,7 +142,7 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
r.map_err(into_signature_error)?
|
r.map_err(into_signature_error)?
|
||||||
}
|
}
|
||||||
|
|
||||||
self.set_magic(_aligned, SWAP_MAGIC)
|
self.state.mark_updated()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the update in DFU with any digest.
|
/// 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.
|
/// 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"))]
|
#[cfg(not(feature = "_verify"))]
|
||||||
pub fn mark_updated(&mut self, aligned: &mut [u8]) -> Result<(), FirmwareUpdaterError> {
|
pub fn mark_updated(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.mark_updated()
|
||||||
self.set_magic(aligned, SWAP_MAGIC)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark firmware boot successful and stop rollback on reset.
|
/// Mark firmware boot successful and stop rollback on reset.
|
||||||
///
|
pub fn mark_booted(&mut self) -> Result<(), FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.mark_booted()
|
||||||
///
|
|
||||||
/// 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(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write data to a flash page.
|
/// Write data to a flash page.
|
||||||
@ -231,15 +180,9 @@ impl<DFU: NorFlash, STATE: NorFlash> BlockingFirmwareUpdater<DFU, STATE> {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// Failing to meet alignment and size requirements may result in a panic.
|
/// Failing to meet alignment and size requirements may result in a panic.
|
||||||
pub fn write_firmware(
|
pub fn write_firmware(&mut self, offset: usize, data: &[u8]) -> Result<(), FirmwareUpdaterError> {
|
||||||
&mut self,
|
|
||||||
aligned: &mut [u8],
|
|
||||||
offset: usize,
|
|
||||||
data: &[u8],
|
|
||||||
) -> Result<(), FirmwareUpdaterError> {
|
|
||||||
assert!(data.len() >= DFU::ERASE_SIZE);
|
assert!(data.len() >= DFU::ERASE_SIZE);
|
||||||
assert_eq!(aligned.len(), STATE::WRITE_SIZE);
|
self.state.verify_booted()?;
|
||||||
self.verify_booted(aligned)?;
|
|
||||||
|
|
||||||
self.dfu.erase(offset as u32, (offset + data.len()) as u32)?;
|
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
|
/// Using this instead of `write_firmware` allows for an optimized API in
|
||||||
/// exchange for added complexity.
|
/// exchange for added complexity.
|
||||||
///
|
pub fn prepare_update(&mut self) -> Result<&mut DFU, FirmwareUpdaterError> {
|
||||||
/// # Safety
|
self.state.verify_booted()?;
|
||||||
///
|
|
||||||
/// 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)?;
|
|
||||||
self.dfu.erase(0, self.dfu.capacity() as u32)?;
|
self.dfu.erase(0, self.dfu.capacity() as u32)?;
|
||||||
|
|
||||||
Ok(&mut self.dfu)
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
@ -283,14 +301,14 @@ mod tests {
|
|||||||
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
let flash = Mutex::<NoopRawMutex, _>::new(RefCell::new(MemFlash::<131072, 4096, 8>::default()));
|
||||||
let state = BlockingPartition::new(&flash, 0, 4096);
|
let state = BlockingPartition::new(&flash, 0, 4096);
|
||||||
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
let dfu = BlockingPartition::new(&flash, 65536, 65536);
|
||||||
|
let mut aligned = [0; 8];
|
||||||
|
|
||||||
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
let update = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66];
|
||||||
let mut to_write = [0; 4096];
|
let mut to_write = [0; 4096];
|
||||||
to_write[..7].copy_from_slice(update.as_slice());
|
to_write[..7].copy_from_slice(update.as_slice());
|
||||||
|
|
||||||
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state });
|
let mut updater = BlockingFirmwareUpdater::new(FirmwareUpdaterConfig { dfu, state }, &mut aligned);
|
||||||
let mut aligned = [0; 8];
|
updater.write_firmware(0, to_write.as_slice()).unwrap();
|
||||||
updater.write_firmware(&mut aligned, 0, to_write.as_slice()).unwrap();
|
|
||||||
let mut chunk_buf = [0; 2];
|
let mut chunk_buf = [0; 2];
|
||||||
let mut hash = [0; 20];
|
let mut hash = [0; 20];
|
||||||
updater
|
updater
|
||||||
|
@ -3,8 +3,8 @@ mod asynch;
|
|||||||
mod blocking;
|
mod blocking;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use asynch::FirmwareUpdater;
|
pub use asynch::{FirmwareState, FirmwareUpdater};
|
||||||
pub use blocking::BlockingFirmwareUpdater;
|
pub use blocking::{BlockingFirmwareState, BlockingFirmwareUpdater};
|
||||||
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
|
||||||
|
|
||||||
/// Firmware updater flash configuration holding the two flashes used by the updater
|
/// Firmware updater flash configuration holding the two flashes used by the updater
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,9 +16,11 @@ mod test_flash;
|
|||||||
// TODO: Use the value provided by NorFlash when available
|
// TODO: Use the value provided by NorFlash when available
|
||||||
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
pub(crate) const STATE_ERASE_VALUE: u8 = 0xFF;
|
||||||
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
pub use boot_loader::{BootError, BootLoader, BootLoaderConfig};
|
||||||
|
pub use firmware_updater::{
|
||||||
|
BlockingFirmwareState, BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use firmware_updater::FirmwareUpdater;
|
pub use firmware_updater::{FirmwareState, FirmwareUpdater};
|
||||||
pub use firmware_updater::{BlockingFirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError};
|
|
||||||
|
|
||||||
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
pub(crate) const BOOT_MAGIC: u8 = 0xD0;
|
||||||
pub(crate) const SWAP_MAGIC: u8 = 0xF0;
|
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().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
dfu: flash.dfu(),
|
FirmwareUpdaterConfig {
|
||||||
state: flash.state(),
|
dfu: flash.dfu(),
|
||||||
});
|
state: flash.state(),
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
},
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
&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.
|
// 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)));
|
assert!(matches!(res, Err::<(), _>(FirmwareUpdaterError::BadState)));
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
@ -158,11 +163,14 @@ mod tests {
|
|||||||
|
|
||||||
// Mark as booted
|
// Mark as booted
|
||||||
let flash = flash.into_async();
|
let flash = flash.into_async();
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
dfu: flash.dfu(),
|
FirmwareUpdaterConfig {
|
||||||
state: flash.state(),
|
dfu: flash.dfu(),
|
||||||
});
|
state: flash.state(),
|
||||||
block_on(updater.mark_booted(&mut aligned)).unwrap();
|
},
|
||||||
|
&mut aligned,
|
||||||
|
);
|
||||||
|
block_on(updater.mark_booted()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
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().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
dfu: flash.dfu(),
|
FirmwareUpdaterConfig {
|
||||||
state: flash.state(),
|
dfu: flash.dfu(),
|
||||||
});
|
state: flash.state(),
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
},
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
&mut aligned,
|
||||||
|
);
|
||||||
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
||||||
|
block_on(updater.mark_updated()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
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().erase(0, ORIGINAL.len() as u32)).unwrap();
|
||||||
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
block_on(flash.active().write(0, &ORIGINAL)).unwrap();
|
||||||
|
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
let mut updater = FirmwareUpdater::new(
|
||||||
dfu: flash.dfu(),
|
FirmwareUpdaterConfig {
|
||||||
state: flash.state(),
|
dfu: flash.dfu(),
|
||||||
});
|
state: flash.state(),
|
||||||
block_on(updater.write_firmware(&mut aligned, 0, &UPDATE)).unwrap();
|
},
|
||||||
block_on(updater.mark_updated(&mut aligned)).unwrap();
|
&mut aligned,
|
||||||
|
);
|
||||||
|
block_on(updater.write_firmware(0, &UPDATE)).unwrap();
|
||||||
|
block_on(updater.mark_updated()).unwrap();
|
||||||
|
|
||||||
let flash = flash.into_blocking();
|
let flash = flash.into_blocking();
|
||||||
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
let mut bootloader = BootLoader::new(BootLoaderConfig {
|
||||||
@ -293,18 +307,19 @@ mod tests {
|
|||||||
|
|
||||||
// On with the test
|
// On with the test
|
||||||
let flash = flash.into_async();
|
let flash = flash.into_async();
|
||||||
let mut updater = FirmwareUpdater::new(FirmwareUpdaterConfig {
|
|
||||||
dfu: flash.dfu(),
|
|
||||||
state: flash.state(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut aligned = [0; 4];
|
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(
|
assert!(block_on(updater.verify_and_mark_updated(
|
||||||
&public_key.to_bytes(),
|
&public_key.to_bytes(),
|
||||||
&signature.to_bytes(),
|
&signature.to_bytes(),
|
||||||
firmware_len as u32,
|
firmware_len as u32,
|
||||||
&mut aligned,
|
|
||||||
))
|
))
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,37 +3,28 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig};
|
use embassy_nrf::nvmc::PAGE_SIZE;
|
||||||
use embassy_nrf::nvmc::{Nvmc, PAGE_SIZE};
|
|
||||||
use embassy_nrf::peripherals::WDT;
|
use embassy_nrf::peripherals::WDT;
|
||||||
use embassy_nrf::wdt;
|
use embassy_nrf::wdt;
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
|
|
||||||
/// A bootloader for nRF devices.
|
/// A bootloader for nRF devices.
|
||||||
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = PAGE_SIZE> {
|
pub struct BootLoader<const BUFFER_SIZE: usize = PAGE_SIZE>;
|
||||||
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
|
|
||||||
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
|
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
|
||||||
BootLoader<ACTIVE, DFU, STATE, 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>(
|
||||||
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
|
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||||
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
|
) -> Self {
|
||||||
Self {
|
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||||
boot: embassy_boot::BootLoader::new(config),
|
let mut boot = embassy_boot::BootLoader::new(config);
|
||||||
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
|
boot.prepare_boot(&mut aligned_buf.0).expect("Boot prepare error");
|
||||||
}
|
Self
|
||||||
}
|
|
||||||
|
|
||||||
/// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boots the application without softdevice mechanisms.
|
/// 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.
|
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||||
#[cfg(not(feature = "softdevice"))]
|
#[cfg(not(feature = "softdevice"))]
|
||||||
pub unsafe fn load(self, start: u32) -> ! {
|
pub unsafe fn load(self, start: u32) -> ! {
|
||||||
core::mem::drop(self.boot);
|
|
||||||
|
|
||||||
let mut p = cortex_m::Peripherals::steal();
|
let mut p = cortex_m::Peripherals::steal();
|
||||||
p.SCB.invalidate_icache();
|
p.SCB.invalidate_icache();
|
||||||
p.SCB.vtor.write(start);
|
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.
|
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||||
#[cfg(feature = "softdevice")]
|
#[cfg(feature = "softdevice")]
|
||||||
pub unsafe fn load(&mut self, _app: u32) -> ! {
|
pub unsafe fn load(self, _app: u32) -> ! {
|
||||||
use nrf_softdevice_mbr as mbr;
|
use nrf_softdevice_mbr as mbr;
|
||||||
const NRF_SUCCESS: u32 = 0;
|
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.
|
/// A flash implementation that wraps any flash and will pet a watchdog when touching flash.
|
||||||
pub struct WatchdogFlash<'d> {
|
pub struct WatchdogFlash<FLASH> {
|
||||||
flash: Nvmc<'d>,
|
flash: FLASH,
|
||||||
wdt: wdt::WatchdogHandle,
|
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
|
/// 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) {
|
let (_wdt, [wdt]) = match wdt::Watchdog::try_new(wdt, config) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@ -127,13 +116,13 @@ impl<'d> WatchdogFlash<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ErrorType for WatchdogFlash<'d> {
|
impl<FLASH: ErrorType> ErrorType for WatchdogFlash<FLASH> {
|
||||||
type Error = <Nvmc<'d> as ErrorType>::Error;
|
type Error = FLASH::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> NorFlash for WatchdogFlash<'d> {
|
impl<FLASH: NorFlash> NorFlash for WatchdogFlash<FLASH> {
|
||||||
const WRITE_SIZE: usize = <Nvmc<'d> as NorFlash>::WRITE_SIZE;
|
const WRITE_SIZE: usize = FLASH::WRITE_SIZE;
|
||||||
const ERASE_SIZE: usize = <Nvmc<'d> as NorFlash>::ERASE_SIZE;
|
const ERASE_SIZE: usize = FLASH::ERASE_SIZE;
|
||||||
|
|
||||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||||
self.wdt.pet();
|
self.wdt.pet();
|
||||||
@ -145,8 +134,8 @@ impl<'d> NorFlash for WatchdogFlash<'d> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> ReadNorFlash for WatchdogFlash<'d> {
|
impl<FLASH: ReadNorFlash> ReadNorFlash for WatchdogFlash<FLASH> {
|
||||||
const READ_SIZE: usize = <Nvmc<'d> as ReadNorFlash>::READ_SIZE;
|
const READ_SIZE: usize = FLASH::READ_SIZE;
|
||||||
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.wdt.pet();
|
self.wdt.pet();
|
||||||
self.flash.read(offset, data)
|
self.flash.read(offset, data)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,38 +3,29 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
|
use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE};
|
||||||
use embassy_rp::flash::{Flash, ERASE_SIZE};
|
|
||||||
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
use embassy_rp::peripherals::{FLASH, WATCHDOG};
|
||||||
use embassy_rp::watchdog::Watchdog;
|
use embassy_rp::watchdog::Watchdog;
|
||||||
use embassy_time::Duration;
|
use embassy_time::Duration;
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
|
|
||||||
/// A bootloader for RP2040 devices.
|
/// A bootloader for RP2040 devices.
|
||||||
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize = ERASE_SIZE> {
|
pub struct BootLoader<const BUFFER_SIZE: usize = ERASE_SIZE>;
|
||||||
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
|
|
||||||
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
|
impl<const BUFFER_SIZE: usize> BootLoader<BUFFER_SIZE> {
|
||||||
BootLoader<ACTIVE, DFU, STATE, 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>(
|
||||||
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
|
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||||
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
|
) -> Self {
|
||||||
Self {
|
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||||
boot: embassy_boot::BootLoader::new(config),
|
let mut boot = embassy_boot::BootLoader::new(config);
|
||||||
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
|
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||||
}
|
Self
|
||||||
}
|
|
||||||
|
|
||||||
/// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boots the application.
|
/// 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.
|
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||||
pub unsafe fn load(self, start: u32) -> ! {
|
pub unsafe fn load(self, start: u32) -> ! {
|
||||||
core::mem::drop(self.boot);
|
|
||||||
|
|
||||||
trace!("Loading app at 0x{:x}", start);
|
trace!("Loading app at 0x{:x}", start);
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut p = cortex_m::Peripherals::steal();
|
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.
|
/// A flash implementation that will feed a watchdog when touching flash.
|
||||||
pub struct WatchdogFlash<'d, const SIZE: usize> {
|
pub struct WatchdogFlash<'d, const SIZE: usize> {
|
||||||
flash: Flash<'d, FLASH, SIZE>,
|
flash: Flash<'d, FLASH, Blocking, SIZE>,
|
||||||
watchdog: Watchdog,
|
watchdog: Watchdog,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
|
impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> {
|
||||||
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
|
/// Start a new watchdog with a given flash and watchdog peripheral and a timeout
|
||||||
pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self {
|
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);
|
let mut watchdog = Watchdog::new(watchdog);
|
||||||
watchdog.start(timeout);
|
watchdog.start(timeout);
|
||||||
Self { flash, watchdog }
|
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> {
|
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> {
|
impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> {
|
||||||
const WRITE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::WRITE_SIZE;
|
const WRITE_SIZE: usize = <Flash<'d, FLASH, Blocking, SIZE> as NorFlash>::WRITE_SIZE;
|
||||||
const ERASE_SIZE: usize = <Flash<'d, FLASH, SIZE> as NorFlash>::ERASE_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> {
|
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||||
self.watchdog.feed();
|
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> {
|
fn write(&mut self, offset: u32, data: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.watchdog.feed();
|
self.watchdog.feed();
|
||||||
self.flash.write(offset, data)
|
self.flash.blocking_write(offset, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> {
|
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> {
|
fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.watchdog.feed();
|
self.watchdog.feed();
|
||||||
self.flash.read(offset, data)
|
self.flash.blocking_read(offset, data)
|
||||||
}
|
}
|
||||||
fn capacity(&self) -> usize {
|
fn capacity(&self) -> usize {
|
||||||
self.flash.capacity()
|
self.flash.capacity()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,34 +3,25 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
pub use embassy_boot::{
|
||||||
|
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State,
|
||||||
|
};
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_boot::FirmwareUpdater;
|
pub use embassy_boot::{FirmwareState, FirmwareUpdater};
|
||||||
pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State};
|
|
||||||
use embedded_storage::nor_flash::NorFlash;
|
use embedded_storage::nor_flash::NorFlash;
|
||||||
|
|
||||||
/// A bootloader for STM32 devices.
|
/// A bootloader for STM32 devices.
|
||||||
pub struct BootLoader<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize> {
|
pub struct BootLoader;
|
||||||
boot: embassy_boot::BootLoader<ACTIVE, DFU, STATE>,
|
|
||||||
aligned_buf: AlignedBuffer<BUFFER_SIZE>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>
|
impl BootLoader {
|
||||||
BootLoader<ACTIVE, DFU, STATE, 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, const BUFFER_SIZE: usize>(
|
||||||
/// Create a new bootloader instance using the supplied partitions for active, dfu and state.
|
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
|
||||||
pub fn new(config: BootLoaderConfig<ACTIVE, DFU, STATE>) -> Self {
|
) -> Self {
|
||||||
Self {
|
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
|
||||||
boot: embassy_boot::BootLoader::new(config),
|
let mut boot = embassy_boot::BootLoader::new(config);
|
||||||
aligned_buf: AlignedBuffer([0; BUFFER_SIZE]),
|
boot.prepare_boot(aligned_buf.as_mut()).expect("Boot prepare error");
|
||||||
}
|
Self
|
||||||
}
|
|
||||||
|
|
||||||
/// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Boots the application.
|
/// 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.
|
/// This modifies the stack pointer and reset vector and will run code placed in the active partition.
|
||||||
pub unsafe fn load(self, start: u32) -> ! {
|
pub unsafe fn load(self, start: u32) -> ! {
|
||||||
core::mem::drop(self.boot);
|
|
||||||
|
|
||||||
trace!("Loading app at 0x{:x}", start);
|
trace!("Loading app at 0x{:x}", start);
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut p = cortex_m::Peripherals::steal();
|
let mut p = cortex_m::Peripherals::steal();
|
||||||
|
@ -15,15 +15,18 @@ target = "x86_64-unknown-linux-gnu"
|
|||||||
std = []
|
std = []
|
||||||
# Enable nightly-only features
|
# Enable nightly-only features
|
||||||
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
nightly = ["embassy-futures", "embedded-hal-async", "embedded-storage-async"]
|
||||||
|
time = ["dep:embassy-time"]
|
||||||
|
default = ["time"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [
|
||||||
"unproven",
|
"unproven",
|
||||||
] }
|
] }
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.10" }
|
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1", optional = true }
|
embedded-hal-async = { version = "=1.0.0-rc.1", optional = true }
|
||||||
embedded-storage = "0.3.0"
|
embedded-storage = "0.3.0"
|
||||||
embedded-storage-async = { version = "0.4.0", optional = true }
|
embedded-storage-async = { version = "0.4.0", optional = true }
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use embedded_hal_02::{blocking, serial};
|
use embedded_hal_02::blocking;
|
||||||
|
|
||||||
/// Wrapper that implements async traits using blocking implementations.
|
/// Wrapper that implements async traits using blocking implementations.
|
||||||
///
|
///
|
||||||
@ -74,7 +74,21 @@ where
|
|||||||
E: embedded_hal_1::spi::Error + 'static,
|
E: embedded_hal_1::spi::Error + 'static,
|
||||||
T: blocking::spi::Transfer<u8, Error = E> + blocking::spi::Write<u8, Error = E>,
|
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
|
// Ensure we write the expected bytes
|
||||||
for i in 0..core::cmp::min(read.len(), write.len()) {
|
for i in 0..core::cmp::min(read.len(), write.len()) {
|
||||||
read[i] = write[i].clone();
|
read[i] = write[i].clone();
|
||||||
@ -83,52 +97,12 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transfer_in_place<'a>(&'a mut self, _: &'a mut [u8]) -> Result<(), Self::Error> {
|
async fn transfer_in_place(&mut self, data: &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> {
|
|
||||||
self.wrapped.transfer(data)?;
|
self.wrapped.transfer(data)?;
|
||||||
Ok(())
|
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
|
/// NOR flash wrapper
|
||||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||||
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
use embedded_storage_async::nor_flash::{NorFlash as AsyncNorFlash, ReadNorFlash as AsyncReadNorFlash};
|
||||||
|
@ -69,54 +69,39 @@ where
|
|||||||
type Error = T::Error;
|
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
|
where
|
||||||
T: embedded_hal_async::spi::SpiBus,
|
T: embedded_hal_async::spi::SpiBus<Word>,
|
||||||
{
|
|
||||||
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,
|
|
||||||
{
|
{
|
||||||
async fn flush(&mut self) -> Result<(), Self::Error> {
|
async fn flush(&mut self) -> Result<(), Self::Error> {
|
||||||
self.wrapped.flush().await?;
|
self.wrapped.flush().await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusWrite<u8> for YieldingAsync<T>
|
async fn write(&mut self, data: &[Word]) -> Result<(), Self::Error> {
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusWrite<u8>,
|
|
||||||
{
|
|
||||||
async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.write(data).await?;
|
self.wrapped.write(data).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> embedded_hal_async::spi::SpiBusRead<u8> for YieldingAsync<T>
|
async fn read(&mut self, data: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
where
|
|
||||||
T: embedded_hal_async::spi::SpiBusRead<u8>,
|
|
||||||
{
|
|
||||||
async fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
|
|
||||||
self.wrapped.read(data).await?;
|
self.wrapped.read(data).await?;
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -11,7 +11,7 @@ pub use asynch::Partition;
|
|||||||
pub use blocking::BlockingPartition;
|
pub use blocking::BlockingPartition;
|
||||||
|
|
||||||
/// Partition error
|
/// Partition error
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum Error<T> {
|
pub enum Error<T> {
|
||||||
/// The requested flash area is outside the partition
|
/// The requested flash area is outside the partition
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections, try_blocks))]
|
#![cfg_attr(feature = "nightly", feature(async_fn_in_trait, try_blocks))]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
//! Utilities to use `embedded-hal` traits with Embassy.
|
//! Utilities to use `embedded-hal` traits with Embassy.
|
||||||
@ -26,6 +26,18 @@ pub trait SetConfig {
|
|||||||
/// The configuration type used by this driver.
|
/// The configuration type used by this driver.
|
||||||
type Config;
|
type Config;
|
||||||
|
|
||||||
|
/// The error type that can occur if `set_config` fails.
|
||||||
|
type ConfigError;
|
||||||
|
|
||||||
/// Set the configuration of the driver.
|
/// Set the configuration of the driver.
|
||||||
fn set_config(&mut self, config: &Self::Config);
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the configuration of a peripheral driver.
|
||||||
|
pub trait GetConfig {
|
||||||
|
/// The configuration type used by this driver.
|
||||||
|
type Config;
|
||||||
|
|
||||||
|
/// Get the configuration of the driver.
|
||||||
|
fn get_config(&self) -> Self::Config;
|
||||||
}
|
}
|
||||||
|
@ -125,14 +125,14 @@ where
|
|||||||
{
|
{
|
||||||
async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
|
bus.read(address, buffer).await.map_err(I2cDeviceError::I2c)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
async fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
|
bus.write(address, bytes).await.map_err(I2cDeviceError::I2c)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ where
|
|||||||
rd_buffer: &mut [u8],
|
rd_buffer: &mut [u8],
|
||||||
) -> Result<(), I2cDeviceError<BUS::Error>> {
|
) -> Result<(), I2cDeviceError<BUS::Error>> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write_read(address, wr_buffer, rd_buffer)
|
bus.write_read(address, wr_buffer, rd_buffer)
|
||||||
.await
|
.await
|
||||||
.map_err(I2cDeviceError::I2c)?;
|
.map_err(I2cDeviceError::I2c)?;
|
||||||
@ -153,7 +153,7 @@ where
|
|||||||
|
|
||||||
async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, address: u8, operations: &mut [i2c::Operation<'_>]) -> Result<(), Self::Error> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.transaction(address, operations)
|
bus.transaction(address, operations)
|
||||||
.await
|
.await
|
||||||
.map_err(I2cDeviceError::I2c)?;
|
.map_err(I2cDeviceError::I2c)?;
|
||||||
|
@ -56,62 +56,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -129,6 +73,10 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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_micros(*us as _).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -172,64 +120,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<M, BUS, CS> spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -238,7 +128,7 @@ where
|
|||||||
{
|
{
|
||||||
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
async fn transaction(&mut self, operations: &mut [spi::Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
let mut bus = self.bus.lock().await;
|
let mut bus = self.bus.lock().await;
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res: Result<(), BUS::Error> = try {
|
let op_res: Result<(), BUS::Error> = try {
|
||||||
@ -248,6 +138,10 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf).await?,
|
Operation::Write(buf) => bus.write(buf).await?,
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
Operation::Transfer(read, write) => bus.transfer(read, write).await?,
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf).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_micros(*us as _).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -148,7 +148,7 @@ where
|
|||||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.read(address, buffer).map_err(I2cDeviceError::I2c)
|
bus.read(address, buffer).map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ where
|
|||||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write(address, bytes).map_err(I2cDeviceError::I2c)
|
bus.write(address, bytes).map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ where
|
|||||||
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| I2cDeviceError::Config)?;
|
||||||
bus.write_read(address, wr_buffer, rd_buffer)
|
bus.write_read(address, wr_buffer, rd_buffer)
|
||||||
.map_err(I2cDeviceError::I2c)
|
.map_err(I2cDeviceError::I2c)
|
||||||
})
|
})
|
||||||
|
@ -22,7 +22,7 @@ use core::cell::RefCell;
|
|||||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
use embassy_sync::blocking_mutex::raw::RawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embedded_hal_1::digital::OutputPin;
|
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::shared_bus::SpiDeviceError;
|
||||||
use crate::SetConfig;
|
use crate::SetConfig;
|
||||||
@ -48,58 +48,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDevice<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -116,6 +64,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
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.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
@ -199,58 +154,6 @@ where
|
|||||||
type Error = SpiDeviceError<BUS::Error, CS::Error>;
|
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>
|
impl<BUS, M, CS> embedded_hal_1::spi::SpiDevice for SpiDeviceWithConfig<'_, M, BUS, CS>
|
||||||
where
|
where
|
||||||
M: RawMutex,
|
M: RawMutex,
|
||||||
@ -260,7 +163,7 @@ where
|
|||||||
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
|
||||||
self.bus.lock(|bus| {
|
self.bus.lock(|bus| {
|
||||||
let mut bus = bus.borrow_mut();
|
let mut bus = bus.borrow_mut();
|
||||||
bus.set_config(&self.config);
|
bus.set_config(&self.config).map_err(|_| SpiDeviceError::Config)?;
|
||||||
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
self.cs.set_low().map_err(SpiDeviceError::Cs)?;
|
||||||
|
|
||||||
let op_res = operations.iter_mut().try_for_each(|op| match op {
|
let op_res = operations.iter_mut().try_for_each(|op| match op {
|
||||||
@ -268,6 +171,13 @@ where
|
|||||||
Operation::Write(buf) => bus.write(buf),
|
Operation::Write(buf) => bus.write(buf),
|
||||||
Operation::Transfer(read, write) => bus.transfer(read, write),
|
Operation::Transfer(read, write) => bus.transfer(read, write),
|
||||||
Operation::TransferInPlace(buf) => bus.transfer_in_place(buf),
|
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.
|
// On failure, it's important to still flush and deassert CS.
|
||||||
|
@ -14,6 +14,8 @@ pub mod blocking;
|
|||||||
pub enum I2cDeviceError<BUS> {
|
pub enum I2cDeviceError<BUS> {
|
||||||
/// An operation on the inner I2C bus failed.
|
/// An operation on the inner I2C bus failed.
|
||||||
I2c(BUS),
|
I2c(BUS),
|
||||||
|
/// Configuration of the inner I2C bus failed.
|
||||||
|
Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS> i2c::Error for I2cDeviceError<BUS>
|
impl<BUS> i2c::Error for I2cDeviceError<BUS>
|
||||||
@ -23,6 +25,7 @@ where
|
|||||||
fn kind(&self) -> i2c::ErrorKind {
|
fn kind(&self) -> i2c::ErrorKind {
|
||||||
match self {
|
match self {
|
||||||
Self::I2c(e) => e.kind(),
|
Self::I2c(e) => e.kind(),
|
||||||
|
Self::Config => i2c::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -30,11 +33,16 @@ where
|
|||||||
/// Error returned by SPI device implementations in this crate.
|
/// Error returned by SPI device implementations in this crate.
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum SpiDeviceError<BUS, CS> {
|
pub enum SpiDeviceError<BUS, CS> {
|
||||||
/// An operation on the inner SPI bus failed.
|
/// An operation on the inner SPI bus failed.
|
||||||
Spi(BUS),
|
Spi(BUS),
|
||||||
/// Setting the value of the Chip Select (CS) pin failed.
|
/// Setting the value of the Chip Select (CS) pin failed.
|
||||||
Cs(CS),
|
Cs(CS),
|
||||||
|
/// DelayUs operations are not supported when the `time` Cargo feature is not enabled.
|
||||||
|
DelayUsNotSupported,
|
||||||
|
/// The SPI bus could not be configured.
|
||||||
|
Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||||
@ -46,6 +54,8 @@ where
|
|||||||
match self {
|
match self {
|
||||||
Self::Spi(e) => e.kind(),
|
Self::Spi(e) => e.kind(),
|
||||||
Self::Cs(_) => spi::ErrorKind::Other,
|
Self::Cs(_) => spi::ErrorKind::Other,
|
||||||
|
Self::DelayUsNotSupported => spi::ErrorKind::Other,
|
||||||
|
Self::Config => spi::ErrorKind::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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/),
|
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).
|
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
|
## 0.2.0 - 2023-04-27
|
||||||
|
|
||||||
- Replace unnecessary atomics in runqueue
|
- Replace unnecessary atomics in runqueue
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "async/await executor designed for embedded usage"
|
description = "async/await executor designed for embedded usage"
|
||||||
@ -14,7 +14,7 @@ categories = [
|
|||||||
[package.metadata.embassy_docs]
|
[package.metadata.embassy_docs]
|
||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
|
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/"
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
|
||||||
features = ["nightly", "defmt", "pender-callback"]
|
features = ["nightly", "defmt"]
|
||||||
flavors = [
|
flavors = [
|
||||||
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
|
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
|
||||||
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
|
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
|
||||||
@ -25,7 +25,7 @@ flavors = [
|
|||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
default-target = "thumbv7em-none-eabi"
|
default-target = "thumbv7em-none-eabi"
|
||||||
targets = ["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]
|
[features]
|
||||||
|
|
||||||
@ -37,9 +37,6 @@ arch-xtensa = ["_arch"]
|
|||||||
arch-riscv32 = ["_arch"]
|
arch-riscv32 = ["_arch"]
|
||||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
|
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)
|
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||||
executor-thread = []
|
executor-thread = []
|
||||||
# Enable the interrupt-mode executor (available in Cortex-M only)
|
# 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 }
|
rtos-trace = { version = "0.1.2", optional = true }
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
embassy-macros = { version = "0.2.0", path = "../embassy-macros" }
|
embassy-macros = { version = "0.2.1", path = "../embassy-macros" }
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time", optional = true}
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true}
|
||||||
atomic-polyfill = "1.0.1"
|
atomic-polyfill = "1.0.1"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
static_cell = "1.1"
|
static_cell = "1.1"
|
||||||
|
@ -4,7 +4,7 @@ An async/await executor designed for embedded usage.
|
|||||||
|
|
||||||
- No `alloc`, no heap needed. Task futures are statically allocated.
|
- No `alloc`, no heap needed. Task futures are statically allocated.
|
||||||
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
- No "fixed capacity" data structures, executor works with 1 or 1000 tasks without needing config/tuning.
|
||||||
- Integrated timer queue: sleeping is easy, just do `Timer::after(Duration::from_secs(1)).await;`.
|
- Integrated timer queue: sleeping is easy, just do `Timer::after_secs(1).await;`.
|
||||||
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
- No busy-loop polling: CPU sleeps when there's no work to do, using interrupts or `WFE/SEV`.
|
||||||
- Efficient polling: a wake will only poll the woken task, not all of them.
|
- Efficient polling: a wake will only poll the woken task, not all of them.
|
||||||
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
- Fair: a task can't monopolize CPU time even if it's constantly being woken. All other tasks get a chance to run before a given task gets polled for the second time.
|
||||||
|
@ -1,25 +1,61 @@
|
|||||||
|
#[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")]
|
#[cfg(feature = "executor-thread")]
|
||||||
pub use thread::*;
|
pub use thread::*;
|
||||||
#[cfg(feature = "executor-thread")]
|
#[cfg(feature = "executor-thread")]
|
||||||
mod thread {
|
mod thread {
|
||||||
|
pub(super) const THREAD_PENDER: usize = usize::MAX;
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::main_cortex_m as main;
|
pub use embassy_macros::main_cortex_m as main;
|
||||||
|
|
||||||
use crate::raw::{Pender, PenderInner};
|
|
||||||
use crate::{raw, Spawner};
|
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.
|
/// Thread mode executor, using WFE/SEV.
|
||||||
///
|
///
|
||||||
/// This is the simplest and most common kind of executor. It runs on
|
/// This is the simplest and most common kind of executor. It runs on
|
||||||
@ -39,7 +75,7 @@ mod thread {
|
|||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
|
inner: raw::Executor::new(THREAD_PENDER as *mut ()),
|
||||||
not_send: PhantomData,
|
not_send: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,30 +122,7 @@ mod interrupt {
|
|||||||
use cortex_m::interrupt::InterruptNumber;
|
use cortex_m::interrupt::InterruptNumber;
|
||||||
use cortex_m::peripheral::NVIC;
|
use cortex_m::peripheral::NVIC;
|
||||||
|
|
||||||
use crate::raw::{self, Pender, PenderInner};
|
use crate::raw;
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Interrupt mode executor.
|
/// Interrupt mode executor.
|
||||||
///
|
///
|
||||||
@ -194,9 +207,7 @@ mod interrupt {
|
|||||||
unsafe {
|
unsafe {
|
||||||
(&mut *self.executor.get())
|
(&mut *self.executor.get())
|
||||||
.as_mut_ptr()
|
.as_mut_ptr()
|
||||||
.write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
|
.write(raw::Executor::new(irq.number() as *mut ()))
|
||||||
irq.number(),
|
|
||||||
)))))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||||
|
@ -11,22 +11,16 @@ mod thread {
|
|||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::main_riscv as main;
|
pub use embassy_macros::main_riscv as main;
|
||||||
|
|
||||||
use crate::raw::{Pender, PenderInner};
|
|
||||||
use crate::{raw, Spawner};
|
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
|
/// 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);
|
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
|
/// RISCV32 Executor
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
inner: raw::Executor,
|
inner: raw::Executor,
|
||||||
@ -37,7 +31,7 @@ mod thread {
|
|||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
|
inner: raw::Executor::new(core::ptr::null_mut()),
|
||||||
not_send: PhantomData,
|
not_send: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,17 +11,12 @@ mod thread {
|
|||||||
#[cfg(feature = "nightly")]
|
#[cfg(feature = "nightly")]
|
||||||
pub use embassy_macros::main_std as main;
|
pub use embassy_macros::main_std as main;
|
||||||
|
|
||||||
use crate::raw::{Pender, PenderInner};
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[export_name = "__pender"]
|
||||||
pub(crate) struct ThreadPender(&'static Signaler);
|
fn __pender(context: *mut ()) {
|
||||||
|
let signaler: &'static Signaler = unsafe { std::mem::transmute(context) };
|
||||||
impl ThreadPender {
|
signaler.signal()
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn pend(self) {
|
|
||||||
self.0.signal()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Single-threaded std-based executor.
|
/// Single-threaded std-based executor.
|
||||||
@ -34,9 +29,9 @@ mod thread {
|
|||||||
impl Executor {
|
impl Executor {
|
||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let signaler = &*Box::leak(Box::new(Signaler::new()));
|
let signaler = Box::leak(Box::new(Signaler::new()));
|
||||||
Self {
|
Self {
|
||||||
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
|
inner: raw::Executor::new(signaler as *mut Signaler as *mut ()),
|
||||||
not_send: PhantomData,
|
not_send: PhantomData,
|
||||||
signaler,
|
signaler,
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,12 @@ mod thread {
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::raw::util::UninitCell;
|
use crate::raw::util::UninitCell;
|
||||||
use crate::raw::{Pender, PenderInner};
|
|
||||||
use crate::{raw, Spawner};
|
use crate::{raw, Spawner};
|
||||||
|
|
||||||
/// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
|
#[export_name = "__pender"]
|
||||||
pub struct Executor {
|
fn __pender(context: *mut ()) {
|
||||||
inner: raw::Executor,
|
let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
|
||||||
ctx: &'static WasmContext,
|
let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
|
||||||
not_send: PhantomData<*mut ()>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WasmContext {
|
pub(crate) struct WasmContext {
|
||||||
@ -29,16 +27,6 @@ mod thread {
|
|||||||
closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
|
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 {
|
impl WasmContext {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
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 {
|
impl Executor {
|
||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let ctx = &*Box::leak(Box::new(WasmContext::new()));
|
let ctx = Box::leak(Box::new(WasmContext::new()));
|
||||||
Self {
|
Self {
|
||||||
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
|
inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),
|
||||||
not_send: PhantomData,
|
|
||||||
ctx,
|
ctx,
|
||||||
|
not_send: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,9 +73,10 @@ mod thread {
|
|||||||
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let executor = &self.inner;
|
let executor = &self.inner;
|
||||||
self.ctx.closure.write(Closure::new(move |_| {
|
let future = Closure::new(move |_| {
|
||||||
executor.poll();
|
executor.poll();
|
||||||
}));
|
});
|
||||||
|
self.ctx.closure.write_in_place(|| future);
|
||||||
init(self.inner.spawner());
|
init(self.inner.spawner());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,22 +8,16 @@ mod thread {
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
use crate::raw::{Pender, PenderInner};
|
|
||||||
use crate::{raw, Spawner};
|
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
|
/// 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);
|
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
|
/// Xtensa Executor
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
inner: raw::Executor,
|
inner: raw::Executor,
|
||||||
@ -34,7 +28,7 @@ mod thread {
|
|||||||
/// Create a new Executor.
|
/// Create a new Executor.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
|
inner: raw::Executor::new(core::ptr::null_mut()),
|
||||||
not_send: PhantomData,
|
not_send: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,8 +71,8 @@ mod thread {
|
|||||||
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
|
SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
core::arch::asm!(
|
core::arch::asm!(
|
||||||
"wsr.ps {0}",
|
"wsr.ps {0}",
|
||||||
"rsync", in(reg) token)
|
"rsync", in(reg) token)
|
||||||
} else {
|
} else {
|
||||||
// waiti sets the PS.INTLEVEL when slipping into sleep
|
// waiti sets the PS.INTLEVEL when slipping into sleep
|
||||||
// because critical sections in Xtensa are implemented via increasing
|
// because critical sections in Xtensa are implemented via increasing
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -147,10 +147,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
|
||||||
let task = AvailableTask::claim(self);
|
let task = AvailableTask::claim(self);
|
||||||
match task {
|
match task {
|
||||||
Some(task) => {
|
Some(task) => task.initialize(future),
|
||||||
let task = task.initialize(future);
|
|
||||||
unsafe { SpawnToken::<F>::new(task) }
|
|
||||||
}
|
|
||||||
None => SpawnToken::new_failed(),
|
None => SpawnToken::new_failed(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,6 +162,9 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
Poll::Ready(_) => {
|
Poll::Ready(_) => {
|
||||||
this.future.drop_in_place();
|
this.future.drop_in_place();
|
||||||
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
|
||||||
|
#[cfg(feature = "integrated-timers")]
|
||||||
|
this.raw.expires_at.set(Instant::MAX);
|
||||||
}
|
}
|
||||||
Poll::Pending => {}
|
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>,
|
task: &'static TaskStorage<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future + 'static> AvailableTask<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
|
task.raw
|
||||||
.state
|
.state
|
||||||
.compare_exchange(0, STATE_SPAWNED | STATE_RUN_QUEUED, Ordering::AcqRel, Ordering::Acquire)
|
.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 })
|
.map(|_| Self { task })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize(self, future: impl FnOnce() -> F) -> TaskRef {
|
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
||||||
self.task.future.write(future());
|
self.task.future.write_in_place(future);
|
||||||
}
|
|
||||||
TaskRef::new(self.task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Raw storage that can hold up to N tasks of the same type.
|
let task = TaskRef::new(self.task);
|
||||||
///
|
|
||||||
/// 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> {
|
SpawnToken::new(task)
|
||||||
/// Create a new TaskPool, with all tasks in non-spawned state.
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
pool: [TaskStorage::NEW; N],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to spawn a task in the pool.
|
/// Initialize the [`TaskStorage`] to run the given future.
|
||||||
///
|
pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
|
||||||
/// See [`TaskStorage::spawn()`] for details.
|
self.initialize_impl::<F>(future)
|
||||||
///
|
|
||||||
/// 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(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like spawn(), but allows the task to be send-spawned if the args are Send even if
|
/// Initialize the [`TaskStorage`] to run the given future.
|
||||||
/// the future is !Send.
|
|
||||||
///
|
///
|
||||||
/// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
|
/// # Safety
|
||||||
/// by the Embassy macros ONLY.
|
|
||||||
///
|
///
|
||||||
/// 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`.
|
/// is an `async fn`, NOT a hand-written `Future`.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
|
pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
|
||||||
where
|
|
||||||
FutFn: FnOnce() -> F,
|
|
||||||
{
|
|
||||||
// When send-spawning a task, we construct the future in this thread, and effectively
|
// 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" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
|
||||||
// send-spawning should require the future `F` to be `Send`.
|
// 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
|
// 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>`.
|
// 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);
|
/// Raw storage that can hold up to N tasks of the same type.
|
||||||
match task {
|
///
|
||||||
Some(task) => {
|
/// This is essentially a `[TaskStorage<F>; N]`.
|
||||||
let task = task.initialize(future);
|
pub struct TaskPool<F: Future + 'static, const N: usize> {
|
||||||
unsafe { SpawnToken::<FutFn>::new(task) }
|
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(),
|
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)]
|
#[derive(Clone, Copy)]
|
||||||
pub(crate) enum PenderInner {
|
pub(crate) struct Pender(*mut ());
|
||||||
#[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 () },
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for PenderInner {}
|
unsafe impl Send for Pender {}
|
||||||
unsafe impl Sync for PenderInner {}
|
unsafe impl Sync for Pender {}
|
||||||
|
|
||||||
/// 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);
|
|
||||||
|
|
||||||
impl Pender {
|
impl Pender {
|
||||||
/// Create a `Pender` that will call an arbitrary function pointer.
|
pub(crate) fn pend(self) {
|
||||||
///
|
extern "Rust" {
|
||||||
/// # Arguments
|
fn __pender(context: *mut ());
|
||||||
///
|
|
||||||
/// - `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),
|
|
||||||
}
|
}
|
||||||
|
unsafe { __pender(self.0) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +386,7 @@ impl SyncExecutor {
|
|||||||
#[allow(clippy::never_loop)]
|
#[allow(clippy::never_loop)]
|
||||||
loop {
|
loop {
|
||||||
#[cfg(feature = "integrated-timers")]
|
#[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| {
|
self.run_queue.dequeue_all(|p| {
|
||||||
let task = p.header();
|
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
|
/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
|
||||||
/// that "want to run").
|
/// that "want to run").
|
||||||
/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
|
/// - You must supply a pender function, as shown below. The executor will call it to notify you
|
||||||
/// to do. You must arrange for `poll()` to be called as soon as possible.
|
/// 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.
|
/// level, etc. It may be called synchronously from any `Executor` method call as well.
|
||||||
/// You must deal with this correctly.
|
/// You must deal with this correctly.
|
||||||
///
|
///
|
||||||
/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
|
/// 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 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)]
|
#[repr(transparent)]
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
pub(crate) inner: SyncExecutor,
|
pub(crate) inner: SyncExecutor,
|
||||||
@ -492,12 +488,12 @@ impl Executor {
|
|||||||
|
|
||||||
/// Create a new 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`.
|
/// See [`Executor`] docs for details on the pender.
|
||||||
pub fn new(pender: Pender) -> Self {
|
pub fn new(context: *mut ()) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: SyncExecutor::new(pender),
|
inner: SyncExecutor::new(Pender(context)),
|
||||||
_not_sync: PhantomData,
|
_not_sync: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -520,16 +516,16 @@ impl Executor {
|
|||||||
/// This loops over all tasks that are queued to be polled (i.e. they're
|
/// 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.
|
/// 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
|
/// 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
|
/// to call `poll` even when not requested by the pender, but it wastes
|
||||||
/// energy.
|
/// energy.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// You must NOT call `poll` reentrantly on the same executor.
|
/// You must NOT call `poll` reentrantly on the same executor.
|
||||||
///
|
///
|
||||||
/// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
|
/// 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
|
/// 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
|
/// somehow schedule for `poll()` to be called later, at a time you know for sure there's
|
||||||
/// no `poll()` already running.
|
/// no `poll()` already running.
|
||||||
pub unsafe fn poll(&'static self) {
|
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")]
|
#[cfg(feature = "integrated-timers")]
|
||||||
struct TimerQueue;
|
struct TimerQueue;
|
||||||
|
|
||||||
|
@ -17,8 +17,9 @@ impl<T> UninitCell<T> {
|
|||||||
&mut *self.as_mut_ptr()
|
&mut *self.as_mut_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn write(&self, val: T) {
|
#[inline(never)]
|
||||||
ptr::write(self.as_mut_ptr(), val)
|
pub unsafe fn write_in_place(&self, func: impl FnOnce() -> T) {
|
||||||
|
ptr::write(self.as_mut_ptr(), func())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn drop_in_place(&self) {
|
pub unsafe fn drop_in_place(&self) {
|
||||||
|
@ -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 {
|
Self {
|
||||||
raw_task: None,
|
raw_task: None,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
pub struct RingBuffer<'a> {
|
|
||||||
buf: &'a mut [u8],
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
empty: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RingBuffer<'a> {
|
|
||||||
pub fn new(buf: &'a mut [u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
buf,
|
|
||||||
start: 0,
|
|
||||||
end: 0,
|
|
||||||
empty: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.start == self.end && !self.empty {
|
|
||||||
trace!(" ringbuf: push_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.start <= self.end {
|
|
||||||
self.buf.len() - self.end
|
|
||||||
} else {
|
|
||||||
self.start - self.end
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: push_buf {:?}..{:?}", self.end, self.end + n);
|
|
||||||
&mut self.buf[self.end..self.end + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: push {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.end = self.wrap(self.end + n);
|
|
||||||
self.empty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_buf(&mut self) -> &mut [u8] {
|
|
||||||
if self.empty {
|
|
||||||
trace!(" ringbuf: pop_buf empty");
|
|
||||||
return &mut self.buf[..0];
|
|
||||||
}
|
|
||||||
|
|
||||||
let n = if self.end <= self.start {
|
|
||||||
self.buf.len() - self.start
|
|
||||||
} else {
|
|
||||||
self.end - self.start
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(" ringbuf: pop_buf {:?}..{:?}", self.start, self.start + n);
|
|
||||||
&mut self.buf[self.start..self.start + n]
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(&mut self, n: usize) {
|
|
||||||
trace!(" ringbuf: pop {:?}", n);
|
|
||||||
if n == 0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.start = self.wrap(self.start + n);
|
|
||||||
self.empty = self.start == self.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_full(&self) -> bool {
|
|
||||||
self.start == self.end && !self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.start = 0;
|
|
||||||
self.end = 0;
|
|
||||||
self.empty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap(&self, n: usize) -> usize {
|
|
||||||
assert!(n <= self.buf.len());
|
|
||||||
if n == self.buf.len() {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_pop() {
|
|
||||||
let mut b = [0; 4];
|
|
||||||
let mut rb = RingBuffer::new(&mut b);
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
buf[0] = 1;
|
|
||||||
buf[1] = 2;
|
|
||||||
buf[2] = 3;
|
|
||||||
buf[3] = 4;
|
|
||||||
rb.push(4);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
assert_eq!(1, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(3, buf.len());
|
|
||||||
assert_eq!(2, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(2, buf.len());
|
|
||||||
assert_eq!(3, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(1, buf.len());
|
|
||||||
assert_eq!(4, buf[0]);
|
|
||||||
rb.pop(1);
|
|
||||||
|
|
||||||
let buf = rb.pop_buf();
|
|
||||||
assert_eq!(0, buf.len());
|
|
||||||
|
|
||||||
let buf = rb.push_buf();
|
|
||||||
assert_eq!(4, buf.len());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-hal-common"
|
name = "embassy-hal-internal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
16
embassy-hal-internal/README.md
Normal file
16
embassy-hal-internal/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# embassy-hal-internal
|
||||||
|
|
||||||
|
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.
|
258
embassy-hal-internal/src/fmt.rs
Normal file
258
embassy-hal-internal/src/fmt.rs
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
#![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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering};
|
|||||||
|
|
||||||
use cortex_m::interrupt::InterruptNumber;
|
use cortex_m::interrupt::InterruptNumber;
|
||||||
use cortex_m::peripheral::NVIC;
|
use cortex_m::peripheral::NVIC;
|
||||||
|
use critical_section::CriticalSection;
|
||||||
|
|
||||||
/// Generate a standard `mod interrupt` for a HAL.
|
/// Generate a standard `mod interrupt` for a HAL.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
@ -91,6 +92,12 @@ macro_rules! interrupt_mod {
|
|||||||
fn set_priority(prio: crate::interrupt::Priority) {
|
fn set_priority(prio: crate::interrupt::Priority) {
|
||||||
Self::IRQ.set_priority(prio)
|
Self::IRQ.set_priority(prio)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the interrupt priority with an already-acquired critical section
|
||||||
|
#[inline]
|
||||||
|
fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: crate::interrupt::Priority) {
|
||||||
|
Self::IRQ.set_priority_with_cs(cs, prio)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$(
|
$(
|
||||||
@ -195,10 +202,29 @@ pub unsafe trait InterruptExt: InterruptNumber + Copy {
|
|||||||
/// Set the interrupt priority.
|
/// Set the interrupt priority.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn set_priority(self, prio: Priority) {
|
fn set_priority(self, prio: Priority) {
|
||||||
critical_section::with(|_| unsafe {
|
unsafe {
|
||||||
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
||||||
nvic.set_priority(self, prio.into())
|
|
||||||
})
|
// On thumbv6, set_priority must do a RMW to change 8bit in a 32bit reg.
|
||||||
|
#[cfg(armv6m)]
|
||||||
|
critical_section::with(|_| nvic.set_priority(self, prio.into()));
|
||||||
|
// On thumbv7+, set_priority does an atomic 8bit write, so no CS needed.
|
||||||
|
#[cfg(not(armv6m))]
|
||||||
|
nvic.set_priority(self, prio.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the interrupt priority with an already-acquired critical section
|
||||||
|
///
|
||||||
|
/// Equivalent to `set_priority`, except you pass a `CriticalSection` to prove
|
||||||
|
/// you've already acquired a critical section. This prevents acquiring another
|
||||||
|
/// one, which saves code size.
|
||||||
|
#[inline]
|
||||||
|
fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) {
|
||||||
|
unsafe {
|
||||||
|
let mut nvic: cortex_m::peripheral::NVIC = mem::transmute(());
|
||||||
|
nvic.set_priority(self, prio.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![allow(clippy::new_without_default)]
|
#![allow(clippy::new_without_default)]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
// This mod MUST go first, so that the others see its macros.
|
// This mod MUST go first, so that the others see its macros.
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -9,7 +10,6 @@ pub mod drop;
|
|||||||
mod macros;
|
mod macros;
|
||||||
mod peripheral;
|
mod peripheral;
|
||||||
pub mod ratio;
|
pub mod ratio;
|
||||||
pub mod ring_buffer;
|
|
||||||
pub use peripheral::{Peripheral, PeripheralRef};
|
pub use peripheral::{Peripheral, PeripheralRef};
|
||||||
|
|
||||||
#[cfg(feature = "cortex-m")]
|
#[cfg(feature = "cortex-m")]
|
@ -48,17 +48,23 @@ macro_rules! peripherals_struct {
|
|||||||
///Returns all the peripherals *once*
|
///Returns all the peripherals *once*
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn take() -> Self {
|
pub(crate) fn take() -> Self {
|
||||||
|
critical_section::with(Self::take_with_cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns all the peripherals *once*
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self {
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false;
|
||||||
|
|
||||||
critical_section::with(|_| unsafe {
|
// safety: OK because we're inside a CS.
|
||||||
|
unsafe {
|
||||||
if _EMBASSY_DEVICE_PERIPHERALS {
|
if _EMBASSY_DEVICE_PERIPHERALS {
|
||||||
panic!("init called more than once!")
|
panic!("init called more than once!")
|
||||||
}
|
}
|
||||||
_EMBASSY_DEVICE_PERIPHERALS = true;
|
_EMBASSY_DEVICE_PERIPHERALS = true;
|
||||||
Self::steal()
|
Self::steal()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +122,7 @@ macro_rules! impl_peripheral {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn clone_unchecked(&self) -> Self::P {
|
unsafe fn clone_unchecked(&self) -> Self::P {
|
||||||
|
#[allow(clippy::needless_update)]
|
||||||
$type { ..*self }
|
$type { ..*self }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -161,7 +161,7 @@ pub trait Peripheral: Sized {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b, T: Deref> Peripheral for T
|
impl<'b, T: DerefMut> Peripheral for T
|
||||||
where
|
where
|
||||||
T::Target: Peripheral,
|
T::Target: Peripheral,
|
||||||
{
|
{
|
@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
stm32wl = ["dep:embassy-stm32"]
|
stm32wl = ["dep:embassy-stm32"]
|
||||||
time = []
|
time = ["embassy-time", "lorawan-device"]
|
||||||
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
defmt = ["dep:defmt", "lorawan-device/defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
@ -20,15 +20,12 @@ defmt = ["dep:defmt", "lorawan-device/defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time" }
|
embassy-time = { version = "0.1.5", path = "../embassy-time", optional = true }
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true }
|
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 = "=1.0.0-rc.1" }
|
||||||
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 = { version = "0.2", features = ["unproven"] }
|
embedded-hal = { version = "0.2", features = ["unproven"] }
|
||||||
bit_field = { version = "0.10" }
|
|
||||||
|
|
||||||
lora-phy = { version = "1" }
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
|
lora-phy = { version = "2" }
|
||||||
|
lorawan-device = { version = "0.11.0", default-features = false, features = ["async"], optional = true }
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -67,24 +67,20 @@ where
|
|||||||
self.board_type = board_type;
|
self.board_type = board_type;
|
||||||
}
|
}
|
||||||
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
async fn set_nss_low(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(false));
|
||||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::LOW));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
async fn set_nss_high(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
pac::PWR.subghzspicr().modify(|w| w.set_nss(true));
|
||||||
pwr.subghzspicr().modify(|w| w.set_nss(pac::pwr::vals::Nss::HIGH));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
async fn reset(&mut self, _delay: &mut impl DelayUs) -> Result<(), RadioError> {
|
||||||
let rcc = pac::RCC;
|
pac::RCC.csr().modify(|w| w.set_rfrst(true));
|
||||||
rcc.csr().modify(|w| w.set_rfrst(true));
|
pac::RCC.csr().modify(|w| w.set_rfrst(false));
|
||||||
rcc.csr().modify(|w| w.set_rfrst(false));
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
async fn wait_on_busy(&mut self) -> Result<(), RadioError> {
|
||||||
let pwr = pac::PWR;
|
while pac::PWR.sr2().read().rfbusys() {}
|
||||||
while pwr.sr2().read().rfbusys() == pac::pwr::vals::Rfbusys::BUSY {}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,11 +280,7 @@ where
|
|||||||
self.busy.wait_for_low().await.map_err(|_| Busy)
|
self.busy.wait_for_low().await.map_err(|_| Busy)
|
||||||
}
|
}
|
||||||
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
async fn await_irq(&mut self) -> Result<(), RadioError> {
|
||||||
if self.board_type != BoardType::RpPicoWaveshareSx1262 {
|
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
||||||
self.dio1.wait_for_high().await.map_err(|_| DIO1)?;
|
|
||||||
} else {
|
|
||||||
self.dio1.wait_for_rising_edge().await.map_err(|_| DIO1)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![feature(async_fn_in_trait, impl_trait_projections)]
|
#![feature(async_fn_in_trait)]
|
||||||
//! embassy-lora holds LoRa-specific functionality.
|
//! embassy-lora holds LoRa-specific functionality.
|
||||||
|
|
||||||
pub(crate) mod fmt;
|
pub(crate) mod fmt;
|
||||||
@ -34,6 +34,6 @@ impl lorawan_device::async_device::radio::Timer for LoraTimer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn delay_ms(&mut self, millis: u64) {
|
async fn delay_ms(&mut self, millis: u64) {
|
||||||
Timer::after(Duration::from_millis(millis)).await
|
Timer::after_millis(millis).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-macros"
|
name = "embassy-macros"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "macros for creating the entry point and tasks for embassy-executor"
|
description = "macros for creating the entry point and tasks for embassy-executor"
|
||||||
|
42
embassy-net-adin1110/Cargo.toml
Normal file
42
embassy-net-adin1110/Cargo.toml
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-net-adin1110"
|
||||||
|
version = "0.2.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", 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.2.0", path = "../embassy-net-driver-channel" }
|
||||||
|
embassy-time = { version = "0.1.5", path = "../embassy-time" }
|
||||||
|
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.2", features = ["std"] }
|
||||||
|
futures-test = "0.3.28"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = [ ]
|
||||||
|
defmt = [ "dep:defmt", "embedded-hal-1/defmt-03" ]
|
||||||
|
log = ["dep:log"]
|
||||||
|
|
||||||
|
[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"
|
||||||
|
features = ["defmt"]
|
88
embassy-net-adin1110/README.md
Normal file
88
embassy-net-adin1110/README.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 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) for an example.
|
||||||
|
- [`SparkFun MicroMod Single Pair Ethernet Function Board`](https://www.sparkfun.com/products/19038) or [`SparkFun MicroMod Single Pair Ethernet Kit (End Of Life)`](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`
|
||||||
|
|
||||||
|
## Benchmark
|
||||||
|
|
||||||
|
- Benchmarked on [`Analog Devices EVAL-ADIN1110EBZ`](https://www.analog.com/en/design-center/evaluation-hardware-and-software/evaluation-boards-kits/eval-adin1110.html), with [`spe_adin1110_http_server`](../examples/stm32l4/src/bin/spe_adin1110_http_server.rs) example.
|
||||||
|
|
||||||
|
Basic `ping` benchmark
|
||||||
|
```rust,ignore
|
||||||
|
# ping <IP> -c 60
|
||||||
|
|
||||||
|
60 packets transmitted, 60 received, 0% packet loss, time 59066ms
|
||||||
|
rtt min/avg/max/mdev = 1.089/1.161/1.237/0.018 ms
|
||||||
|
|
||||||
|
# ping <IP> -s 1472 -M do -c 60
|
||||||
|
|
||||||
|
60 packets transmitted, 60 received, 0% packet loss, time 59066ms
|
||||||
|
rtt min/avg/max/mdev = 5.122/5.162/6.177/0.133 ms
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP load generator benchmark with [`oha`](https://github.com/hatoo/oha)
|
||||||
|
```rust,ignore
|
||||||
|
# oha -c 1 http://<IP> -z 60s
|
||||||
|
Summary:
|
||||||
|
Success rate: 50.00%
|
||||||
|
Total: 60.0005 secs
|
||||||
|
Slowest: 0.0055 secs
|
||||||
|
Fastest: 0.0033 secs
|
||||||
|
Average: 0.0034 secs
|
||||||
|
Requests/sec: 362.1971
|
||||||
|
|
||||||
|
Total data: 2.99 MiB
|
||||||
|
Size/request: 289 B
|
||||||
|
Size/sec: 51.11 KiB
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
359
embassy-net-adin1110/src/crc32.rs
Normal file
359
embassy-net-adin1110/src/crc32.rs
Normal file
@ -0,0 +1,359 @@
|
|||||||
|
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,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Generate Ethernet Frame Check Sequence
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ETH_FCS(pub u32);
|
||||||
|
|
||||||
|
impl ETH_FCS {
|
||||||
|
pub const CRC32_OK: u32 = 0x2144_df1c;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(data: &[u8]) -> Self {
|
||||||
|
let fcs = 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(fcs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn update(self, data: &[u8]) -> Self {
|
||||||
|
let fcs = 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(fcs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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_FCS::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_FCS::new(packet_a);
|
||||||
|
println!("{:08x}", own_crc.0);
|
||||||
|
assert_eq!(own_crc.0, ETH_FCS::CRC32_OK);
|
||||||
|
|
||||||
|
// Packet B
|
||||||
|
let own_crc = ETH_FCS::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_FCS::new(packet_b);
|
||||||
|
println!("{:08x}", own_crc.0);
|
||||||
|
assert_eq!(own_crc.0, ETH_FCS::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_FCS::new(part_a).update(part_b);
|
||||||
|
|
||||||
|
let crc_full = ETH_FCS::new(full_data);
|
||||||
|
|
||||||
|
assert_eq!(crc_full.0, crc_partially.0);
|
||||||
|
}
|
||||||
|
}
|
53
embassy-net-adin1110/src/crc8.rs
Normal file
53
embassy-net-adin1110/src/crc8.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -111,7 +113,7 @@ macro_rules! trace {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
::defmt::trace!($s $(, $x)*);
|
::defmt::trace!($s $(, $x)*);
|
||||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
let _ = ($( & $x ),*);
|
let _ignored = ($( & $x ),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -124,7 +126,7 @@ macro_rules! debug {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
::defmt::debug!($s $(, $x)*);
|
::defmt::debug!($s $(, $x)*);
|
||||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
let _ = ($( & $x ),*);
|
let _ignored = ($( & $x ),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -137,7 +139,7 @@ macro_rules! info {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
::defmt::info!($s $(, $x)*);
|
::defmt::info!($s $(, $x)*);
|
||||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
let _ = ($( & $x ),*);
|
let _ignored = ($( & $x ),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -150,7 +152,7 @@ macro_rules! warn {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
::defmt::warn!($s $(, $x)*);
|
::defmt::warn!($s $(, $x)*);
|
||||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
let _ = ($( & $x ),*);
|
let _ignored = ($( & $x ),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -163,7 +165,7 @@ macro_rules! error {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
::defmt::error!($s $(, $x)*);
|
::defmt::error!($s $(, $x)*);
|
||||||
#[cfg(not(any(feature = "log", feature="defmt")))]
|
#[cfg(not(any(feature = "log", feature="defmt")))]
|
||||||
let _ = ($( & $x ),*);
|
let _ignored = ($( & $x ),*);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -223,3 +225,30 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
1323
embassy-net-adin1110/src/lib.rs
Normal file
1323
embassy-net-adin1110/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
176
embassy-net-adin1110/src/mdio.rs
Normal file
176
embassy-net-adin1110/src/mdio.rs
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Clause 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;
|
||||||
|
|
||||||
|
async 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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[futures_test::test]
|
||||||
|
async fn read_test() {
|
||||||
|
let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
|
||||||
|
|
||||||
|
mdiobus.clear();
|
||||||
|
mdiobus.read_cl22(0x01, 0x00).await.unwrap();
|
||||||
|
assert_eq!(mdiobus.0, vec![A::Read(0x01, 0x00)]);
|
||||||
|
|
||||||
|
mdiobus.clear();
|
||||||
|
mdiobus.read_cl45(0x01, (0xBB, 0x1234)).await.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)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[futures_test::test]
|
||||||
|
async fn write_test() {
|
||||||
|
let mut mdiobus = MockMdioBus(Vec::with_capacity(20));
|
||||||
|
|
||||||
|
mdiobus.clear();
|
||||||
|
mdiobus.write_cl22(0x01, 0x00, 0xABCD).await.unwrap();
|
||||||
|
assert_eq!(mdiobus.0, vec![A::Write(0x01, 0x00, 0xABCD)]);
|
||||||
|
|
||||||
|
mdiobus.clear();
|
||||||
|
mdiobus.write_cl45(0x01, (0xBB, 0x1234), 0xABCD).await.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)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
143
embassy-net-adin1110/src/phy.rs
Normal file
143
embassy-net-adin1110/src/phy.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 10-BASE-T1x PHY functions.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
416
embassy-net-adin1110/src/regs.rs
Normal file
416
embassy-net-adin1110/src/regs.rs
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
use core::fmt::{Debug, Display};
|
||||||
|
|
||||||
|
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 Display for SpiRegisters {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
write!(f, "{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 SPI CRC
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
16
embassy-net-driver-channel/CHANGELOG.md
Normal file
16
embassy-net-driver-channel/CHANGELOG.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
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.2.0 - 2023-10-18
|
||||||
|
|
||||||
|
- Update `embassy-net-driver` to v0.2
|
||||||
|
- `Runner::new` now takes an `embassy_net_driver::HardwareAddress` parameter.
|
||||||
|
- `Runner::set_ethernet_address` is now `set_hardware_address`.
|
||||||
|
|
||||||
|
## 0.1.0 - 2023-06-29
|
||||||
|
|
||||||
|
- First release
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack."
|
||||||
@ -24,6 +24,6 @@ features = ["defmt"]
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
embassy-sync = { version = "0.2.0", path = "../embassy-sync" }
|
embassy-sync = { version = "0.3.0", path = "../embassy-sync" }
|
||||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" }
|
||||||
|
@ -7,7 +7,9 @@ The `embassy-net-driver` trait is polling-based. To implement it, you must write
|
|||||||
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
hand, and hook up the `Waker`s provided by `embassy-net` to the right interrupt handlers so that `embassy-net`
|
||||||
knows when to poll your driver again to make more progress.
|
knows when to poll your driver again to make more progress.
|
||||||
|
|
||||||
With `embassy-net-driver-channel`
|
With `embassy-net-driver-channel` you get a "channel-like" interface instead, where you can send/receive packets
|
||||||
|
to/from embassy-net. The intended usage is to spawn a "driver task" in the background that does this, passing
|
||||||
|
packets between the hardware and the channel.
|
||||||
|
|
||||||
## A note about deadlocks
|
## A note about deadlocks
|
||||||
|
|
||||||
@ -18,19 +20,19 @@ loop {
|
|||||||
// Wait for either..
|
// Wait for either..
|
||||||
match select(
|
match select(
|
||||||
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
// ... the chip signaling an interrupt, indicating a packet is available to receive, or
|
||||||
irq_pin.wait_for_low(),
|
irq_pin.wait_for_low(),
|
||||||
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
// ... a TX buffer becoming available, i.e. embassy-net wants to send a packet
|
||||||
tx_chan.tx_buf(),
|
tx_chan.tx_buf(),
|
||||||
).await {
|
).await {
|
||||||
Either::First(_) => {
|
Either::First(_) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
let buf = rx_chan.rx_buf().await; // allocate a rx buf from the packet queue
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,7 +43,7 @@ However, this code has a latent deadlock bug. The symptom is it can hang at `rx_
|
|||||||
|
|
||||||
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
The reason is that, under load, both the TX and RX queues can get full at the same time. When this happens, the `embassy-net` task stalls trying to send because the TX queue is full, therefore it stops processing packets in the RX queue. Your driver task also stalls because the RX queue is full, therefore it stops processing packets in the TX queue.
|
||||||
|
|
||||||
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the TX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
The fix is to make sure to always service the TX queue while you're waiting for space to become available in the RX queue. For example, select on either "tx_chan.tx_buf() available" or "INT is low AND rx_chan.rx_buf() available":
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
loop {
|
loop {
|
||||||
@ -58,12 +60,12 @@ loop {
|
|||||||
).await {
|
).await {
|
||||||
Either::First(buf) => {
|
Either::First(buf) => {
|
||||||
// a packet is ready to be received!
|
// a packet is ready to be received!
|
||||||
let n = receive_packet_over_spi(buf).await;
|
let n = receive_packet_over_spi(buf).await;
|
||||||
rx_chan.rx_done(n);
|
rx_chan.rx_done(n);
|
||||||
}
|
}
|
||||||
Either::Second(buf) => {
|
Either::Second(buf) => {
|
||||||
// a packet is ready to be sent!
|
// a packet is ready to be sent!
|
||||||
send_packet_over_spi(buf).await;
|
send_packet_over_spi(buf).await;
|
||||||
tx_chan.tx_done();
|
tx_chan.tx_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,15 +78,13 @@ 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
|
- [`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-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.
|
- [`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.
|
||||||
|
|
||||||
|
|
||||||
## Interoperability
|
## Interoperability
|
||||||
|
|
||||||
This crate can run on any executor.
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This work is licensed under either of
|
This work is licensed under either of
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
#![allow(unused_macros)]
|
#![allow(unused_macros)]
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, LowerHex};
|
||||||
|
|
||||||
#[cfg(all(feature = "defmt", feature = "log"))]
|
#[cfg(all(feature = "defmt", feature = "log"))]
|
||||||
compile_error!("You may not enable both `defmt` and `log` features.");
|
compile_error!("You may not enable both `defmt` and `log` features.");
|
||||||
|
|
||||||
@ -81,14 +83,17 @@ macro_rules! todo {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
{
|
::core::unreachable!($($x)*)
|
||||||
#[cfg(not(feature = "defmt"))]
|
};
|
||||||
::core::unreachable!($($x)*);
|
}
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
::defmt::unreachable!($($x)*);
|
#[cfg(feature = "defmt")]
|
||||||
}
|
macro_rules! unreachable {
|
||||||
|
($($x:tt)*) => {
|
||||||
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,3 +228,31 @@ impl<T, E> Try for Result<T, E> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub(crate) 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user