Compare commits
777 Commits
embassy-ne
...
embassy-sy
Author | SHA1 | Date | |
---|---|---|---|
f1f4943ca5 | |||
1b20ba27b1 | |||
f5e96a9d60 | |||
e8b961232b | |||
85a9f5816f | |||
7bcc7e8962 | |||
b118c4cc1b | |||
f090a38dde | |||
dcd1a91966 | |||
49847ff432 | |||
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 | |||
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 | |||
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 | |||
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
|
|
1
.github/ci/build.sh
vendored
1
.github/ci/build.sh
vendored
@ -11,6 +11,7 @@ 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
|
||||||
|
|
||||||
hashtime restore /ci/cache/filetime.json || true
|
hashtime restore /ci/cache/filetime.json || true
|
||||||
|
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
|
62
.github/ci/doc.sh
vendored
62
.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,42 @@ 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.
|
||||||
|
|
||||||
|
# temporarily disabled because it's bringing CI down.
|
||||||
|
#rm -rf webroot
|
||||||
|
#docserver-builder -i ./embassy-stm32 -o webroot/crates/embassy-stm32/git.zup
|
||||||
|
#POD=$(kubectl -n embassy get po -l app=docserver -o jsonpath={.items[0].metadata.name})
|
||||||
|
#kubectl cp webroot/crates $POD:/data
|
||||||
|
4
.github/ci/test.sh
vendored
4
.github/ci/test.sh
vendored
@ -13,7 +13,7 @@ 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 +28,5 @@ cargo test --manifest-path ./embassy-rp/Cargo.toml --no-default-features --featu
|
|||||||
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,stm32f429vg,exti,time-driver-any,exti
|
cargo test --manifest-path ./embassy-stm32/Cargo.toml --no-default-features --features nightly,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",
|
||||||
|
11
README.md
11
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.
|
||||||
@ -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
|
||||||
|
42
ci.sh
42
ci.sh
@ -3,11 +3,7 @@
|
|||||||
set -euo pipefail
|
set -euo 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
|
||||||
|
|
||||||
# needed by wifi examples
|
|
||||||
export WIFI_NETWORK=x
|
|
||||||
export WIFI_PASSWORD=x
|
|
||||||
|
|
||||||
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
TARGET=$(rustc -vV | sed -n 's|host: ||p')
|
||||||
|
|
||||||
@ -23,21 +19,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 \
|
--- 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,6 +65,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,unstable-traits \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,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-rp/Cargo.toml --target thumbv6m-none-eabi --features nightly,qspi-as-gpio \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32l552ze,defmt,exti,time-driver-any \
|
||||||
--- 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,time-driver-any \
|
||||||
@ -85,11 +94,13 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32l041f6,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32l151cb-a,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f398ve,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f378cc,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32g0c1ve,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f217zg,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features nightly,stm32l552ze,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features nightly,stm32wl54jc-cm0p,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32wle5jb,defmt,exti,time-driver-any,unstable-traits \
|
||||||
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features nightly,stm32g474pe,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f107vc,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f103re,defmt,exti,time-driver-any,unstable-traits \
|
||||||
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
--- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7m-none-eabi --features nightly,stm32f100c4,defmt,exti,time-driver-any,unstable-traits \
|
||||||
@ -119,6 +130,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 \
|
||||||
@ -133,8 +145,8 @@ cargo batch \
|
|||||||
--- 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/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 \
|
||||||
@ -168,4 +180,6 @@ if [[ -z "${TELEPROBE_TOKEN-}" ]]; then
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
rm out/tests/stm32wb55rg/wpan_ble
|
||||||
|
|
||||||
teleprobe client run -r out/tests
|
teleprobe client run -r out/tests
|
||||||
|
@ -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,6 +34,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,defmt \
|
--- build --release --manifest-path embassy-rp/Cargo.toml --target thumbv6m-none-eabi --features unstable-traits,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 \
|
||||||
|
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,8 +11,8 @@ log = ["dep:log"]
|
|||||||
firmware-logs = []
|
firmware-logs = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy-time = { version = "0.1.0", path = "../embassy-time"}
|
embassy-time = { version = "0.1.3", 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.1.0", path = "../embassy-net-driver-channel"}
|
||||||
atomic-polyfill = "0.1.5"
|
atomic-polyfill = "0.1.5"
|
||||||
@ -24,11 +24,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
|
||||||
```
|
```
|
||||||
|
@ -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;
|
||||||
|
@ -124,7 +124,7 @@ impl<'a> Control<'a> {
|
|||||||
Timer::after(Duration::from_millis(100)).await;
|
Timer::after(Duration::from_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(Duration::from_millis(100)).await;
|
||||||
|
|
||||||
@ -138,6 +138,16 @@ impl<'a> Control<'a> {
|
|||||||
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();
|
||||||
@ -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;
|
||||||
@ -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() {
|
||||||
|
@ -6,7 +6,7 @@ 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.0", 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"] }
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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.3", 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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -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,12 @@ 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(embassy_time::Duration::from_micros(*us as _)).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -172,64 +122,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,
|
||||||
@ -248,6 +140,12 @@ 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(embassy_time::Duration::from_micros(*us as _)).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
@ -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.
|
||||||
|
@ -30,11 +30,14 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
impl<BUS, CS> spi::Error for SpiDeviceError<BUS, CS>
|
||||||
@ -46,6 +49,7 @@ 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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.3", 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"
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(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;
|
||||||
|
|
||||||
|
@ -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,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-macros
|
||||||
|
|
||||||
|
An [Embassy](https://embassy.dev) project.
|
||||||
|
|
||||||
|
Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport
|
||||||
|
everything you need to use them effectively.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||||
|
|
||||||
|
at your option.
|
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)
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -116,6 +116,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,15 @@ 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.3", 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" }
|
|
||||||
|
|
||||||
|
futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] }
|
||||||
lora-phy = { version = "1" }
|
lora-phy = { version = "1" }
|
||||||
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] }
|
lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true }
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "1323eccc1c470d4259f95f4f315d1be830d572a3"}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -284,11 +284,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,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.1.0", path = "../embassy-net-driver-channel" }
|
||||||
|
embassy-time = { version = "0.1.3" }
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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.1.0", path = "../embassy-net-driver" }
|
||||||
|
@ -76,7 +76,7 @@ These `embassy-net` drivers are implemented using this crate. You can look at th
|
|||||||
|
|
||||||
- [`cyw43`](https://github.com/embassy-rs/embassy/tree/main/cyw43) for WiFi on CYW43xx chips, used in the Raspberry Pi Pico W
|
- [`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.
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -8,11 +8,13 @@ use core::cell::RefCell;
|
|||||||
use core::mem::MaybeUninit;
|
use core::mem::MaybeUninit;
|
||||||
use core::task::{Context, Poll};
|
use core::task::{Context, Poll};
|
||||||
|
|
||||||
|
use driver::HardwareAddress;
|
||||||
pub use embassy_net_driver as driver;
|
pub use embassy_net_driver as driver;
|
||||||
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
use embassy_net_driver::{Capabilities, LinkState, Medium};
|
||||||
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
use embassy_sync::waitqueue::WakerRegistration;
|
||||||
|
use embassy_sync::zerocopy_channel;
|
||||||
|
|
||||||
pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
|
pub struct State<const MTU: usize, const N_RX: usize, const N_TX: usize> {
|
||||||
rx: [PacketBuf<MTU>; N_RX],
|
rx: [PacketBuf<MTU>; N_RX],
|
||||||
@ -42,7 +44,7 @@ struct StateInner<'d, const MTU: usize> {
|
|||||||
struct Shared {
|
struct Shared {
|
||||||
link_state: LinkState,
|
link_state: LinkState,
|
||||||
waker: WakerRegistration,
|
waker: WakerRegistration,
|
||||||
ethernet_address: [u8; 6],
|
hardware_address: driver::HardwareAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Runner<'d, const MTU: usize> {
|
pub struct Runner<'d, const MTU: usize> {
|
||||||
@ -73,6 +75,18 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow_split(&mut self) -> (StateRunner<'_>, RxRunner<'_, MTU>, TxRunner<'_, MTU>) {
|
||||||
|
(
|
||||||
|
StateRunner { shared: self.shared },
|
||||||
|
RxRunner {
|
||||||
|
rx_chan: self.rx_chan.borrow(),
|
||||||
|
},
|
||||||
|
TxRunner {
|
||||||
|
tx_chan: self.tx_chan.borrow(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn state_runner(&self) -> StateRunner<'d> {
|
pub fn state_runner(&self) -> StateRunner<'d> {
|
||||||
StateRunner { shared: self.shared }
|
StateRunner { shared: self.shared }
|
||||||
}
|
}
|
||||||
@ -85,10 +99,10 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ethernet_address(&mut self, address: [u8; 6]) {
|
pub fn set_hardware_address(&mut self, address: driver::HardwareAddress) {
|
||||||
self.shared.lock(|s| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.ethernet_address = address;
|
s.hardware_address = address;
|
||||||
s.waker.wake();
|
s.waker.wake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -117,24 +131,24 @@ impl<'d, const MTU: usize> Runner<'d, MTU> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
||||||
let p = self.tx_chan.recv().await;
|
let p = self.tx_chan.receive().await;
|
||||||
&mut p.buf[..p.len]
|
&mut p.buf[..p.len]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
||||||
let p = self.tx_chan.try_recv()?;
|
let p = self.tx_chan.try_receive()?;
|
||||||
Some(&mut p.buf[..p.len])
|
Some(&mut p.buf[..p.len])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
||||||
match self.tx_chan.poll_recv(cx) {
|
match self.tx_chan.poll_receive(cx) {
|
||||||
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tx_done(&mut self) {
|
pub fn tx_done(&mut self) {
|
||||||
self.tx_chan.recv_done();
|
self.tx_chan.receive_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +164,15 @@ impl<'d> StateRunner<'d> {
|
|||||||
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
pub fn set_ethernet_address(&self, address: [u8; 6]) {
|
||||||
self.shared.lock(|s| {
|
self.shared.lock(|s| {
|
||||||
let s = &mut *s.borrow_mut();
|
let s = &mut *s.borrow_mut();
|
||||||
s.ethernet_address = address;
|
s.hardware_address = driver::HardwareAddress::Ethernet(address);
|
||||||
|
s.waker.wake();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ieee802154_address(&self, address: [u8; 8]) {
|
||||||
|
self.shared.lock(|s| {
|
||||||
|
let s = &mut *s.borrow_mut();
|
||||||
|
s.hardware_address = driver::HardwareAddress::Ieee802154(address);
|
||||||
s.waker.wake();
|
s.waker.wake();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -183,34 +205,38 @@ impl<'d, const MTU: usize> RxRunner<'d, MTU> {
|
|||||||
|
|
||||||
impl<'d, const MTU: usize> TxRunner<'d, MTU> {
|
impl<'d, const MTU: usize> TxRunner<'d, MTU> {
|
||||||
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
pub async fn tx_buf(&mut self) -> &mut [u8] {
|
||||||
let p = self.tx_chan.recv().await;
|
let p = self.tx_chan.receive().await;
|
||||||
&mut p.buf[..p.len]
|
&mut p.buf[..p.len]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
pub fn try_tx_buf(&mut self) -> Option<&mut [u8]> {
|
||||||
let p = self.tx_chan.try_recv()?;
|
let p = self.tx_chan.try_receive()?;
|
||||||
Some(&mut p.buf[..p.len])
|
Some(&mut p.buf[..p.len])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
pub fn poll_tx_buf(&mut self, cx: &mut Context) -> Poll<&mut [u8]> {
|
||||||
match self.tx_chan.poll_recv(cx) {
|
match self.tx_chan.poll_receive(cx) {
|
||||||
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
Poll::Ready(p) => Poll::Ready(&mut p.buf[..p.len]),
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tx_done(&mut self) {
|
pub fn tx_done(&mut self) {
|
||||||
self.tx_chan.recv_done();
|
self.tx_chan.receive_done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
||||||
state: &'d mut State<MTU, N_RX, N_TX>,
|
state: &'d mut State<MTU, N_RX, N_TX>,
|
||||||
ethernet_address: [u8; 6],
|
hardware_address: driver::HardwareAddress,
|
||||||
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
) -> (Runner<'d, MTU>, Device<'d, MTU>) {
|
||||||
let mut caps = Capabilities::default();
|
let mut caps = Capabilities::default();
|
||||||
caps.max_transmission_unit = MTU;
|
caps.max_transmission_unit = MTU;
|
||||||
caps.medium = Medium::Ethernet;
|
caps.medium = match &hardware_address {
|
||||||
|
HardwareAddress::Ethernet(_) => Medium::Ethernet,
|
||||||
|
HardwareAddress::Ieee802154(_) => Medium::Ieee802154,
|
||||||
|
HardwareAddress::Ip => Medium::Ip,
|
||||||
|
};
|
||||||
|
|
||||||
// safety: this is a self-referential struct, however:
|
// safety: this is a self-referential struct, however:
|
||||||
// - it can't move while the `'d` borrow is active.
|
// - it can't move while the `'d` borrow is active.
|
||||||
@ -222,7 +248,7 @@ pub fn new<'d, const MTU: usize, const N_RX: usize, const N_TX: usize>(
|
|||||||
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
|
tx: zerocopy_channel::Channel::new(&mut state.tx[..]),
|
||||||
shared: Mutex::new(RefCell::new(Shared {
|
shared: Mutex::new(RefCell::new(Shared {
|
||||||
link_state: LinkState::Down,
|
link_state: LinkState::Down,
|
||||||
ethernet_address,
|
hardware_address,
|
||||||
waker: WakerRegistration::new(),
|
waker: WakerRegistration::new(),
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
@ -268,7 +294,7 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
|
|||||||
type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
|
type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ;
|
||||||
|
|
||||||
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
if self.rx.poll_recv(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
|
if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() {
|
||||||
Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
|
Some((RxToken { rx: self.rx.borrow() }, TxToken { tx: self.tx.borrow() }))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -289,8 +315,8 @@ impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> {
|
|||||||
self.caps.clone()
|
self.caps.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ethernet_address(&self) -> [u8; 6] {
|
fn hardware_address(&self) -> driver::HardwareAddress {
|
||||||
self.shared.lock(|s| s.borrow().ethernet_address)
|
self.shared.lock(|s| s.borrow().hardware_address)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||||
@ -312,9 +338,9 @@ impl<'a, const MTU: usize> embassy_net_driver::RxToken for RxToken<'a, MTU> {
|
|||||||
F: FnOnce(&mut [u8]) -> R,
|
F: FnOnce(&mut [u8]) -> R,
|
||||||
{
|
{
|
||||||
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
|
// NOTE(unwrap): we checked the queue wasn't full when creating the token.
|
||||||
let pkt = unwrap!(self.rx.try_recv());
|
let pkt = unwrap!(self.rx.try_receive());
|
||||||
let r = f(&mut pkt.buf[..pkt.len]);
|
let r = f(&mut pkt.buf[..pkt.len]);
|
||||||
self.rx.recv_done();
|
self.rx.receive_done();
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -336,215 +362,3 @@ impl<'a, const MTU: usize> embassy_net_driver::TxToken for TxToken<'a, MTU> {
|
|||||||
r
|
r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod zerocopy_channel {
|
|
||||||
use core::cell::RefCell;
|
|
||||||
use core::future::poll_fn;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
|
|
||||||
use embassy_sync::blocking_mutex::raw::RawMutex;
|
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
|
||||||
use embassy_sync::waitqueue::WakerRegistration;
|
|
||||||
|
|
||||||
pub struct Channel<'a, M: RawMutex, T> {
|
|
||||||
buf: *mut T,
|
|
||||||
phantom: PhantomData<&'a mut T>,
|
|
||||||
state: Mutex<M, RefCell<State>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M: RawMutex, T> Channel<'a, M, T> {
|
|
||||||
pub fn new(buf: &'a mut [T]) -> Self {
|
|
||||||
let len = buf.len();
|
|
||||||
assert!(len != 0);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
buf: buf.as_mut_ptr(),
|
|
||||||
phantom: PhantomData,
|
|
||||||
state: Mutex::new(RefCell::new(State {
|
|
||||||
len,
|
|
||||||
front: 0,
|
|
||||||
back: 0,
|
|
||||||
full: false,
|
|
||||||
send_waker: WakerRegistration::new(),
|
|
||||||
recv_waker: WakerRegistration::new(),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) {
|
|
||||||
(Sender { channel: self }, Receiver { channel: self })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Sender<'a, M: RawMutex, T> {
|
|
||||||
channel: &'a Channel<'a, M, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M: RawMutex, T> Sender<'a, M, T> {
|
|
||||||
pub fn borrow(&mut self) -> Sender<'_, M, T> {
|
|
||||||
Sender { channel: self.channel }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_send(&mut self) -> Option<&mut T> {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.push_index() {
|
|
||||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_send(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.push_index() {
|
|
||||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
|
||||||
None => {
|
|
||||||
s.recv_waker.register(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn send(&mut self) -> &mut T {
|
|
||||||
let i = poll_fn(|cx| {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.push_index() {
|
|
||||||
Some(i) => Poll::Ready(i),
|
|
||||||
None => {
|
|
||||||
s.recv_waker.register(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
unsafe { &mut *self.channel.buf.add(i) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_done(&mut self) {
|
|
||||||
self.channel.state.lock(|s| s.borrow_mut().push_done())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct Receiver<'a, M: RawMutex, T> {
|
|
||||||
channel: &'a Channel<'a, M, T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, M: RawMutex, T> Receiver<'a, M, T> {
|
|
||||||
pub fn borrow(&mut self) -> Receiver<'_, M, T> {
|
|
||||||
Receiver { channel: self.channel }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_recv(&mut self) -> Option<&mut T> {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.pop_index() {
|
|
||||||
Some(i) => Some(unsafe { &mut *self.channel.buf.add(i) }),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_recv(&mut self, cx: &mut Context) -> Poll<&mut T> {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.pop_index() {
|
|
||||||
Some(i) => Poll::Ready(unsafe { &mut *self.channel.buf.add(i) }),
|
|
||||||
None => {
|
|
||||||
s.send_waker.register(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn recv(&mut self) -> &mut T {
|
|
||||||
let i = poll_fn(|cx| {
|
|
||||||
self.channel.state.lock(|s| {
|
|
||||||
let s = &mut *s.borrow_mut();
|
|
||||||
match s.pop_index() {
|
|
||||||
Some(i) => Poll::Ready(i),
|
|
||||||
None => {
|
|
||||||
s.send_waker.register(cx.waker());
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
unsafe { &mut *self.channel.buf.add(i) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recv_done(&mut self) {
|
|
||||||
self.channel.state.lock(|s| s.borrow_mut().pop_done())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
len: usize,
|
|
||||||
|
|
||||||
/// Front index. Always 0..=(N-1)
|
|
||||||
front: usize,
|
|
||||||
/// Back index. Always 0..=(N-1).
|
|
||||||
back: usize,
|
|
||||||
|
|
||||||
/// Used to distinguish "empty" and "full" cases when `front == back`.
|
|
||||||
/// May only be `true` if `front == back`, always `false` otherwise.
|
|
||||||
full: bool,
|
|
||||||
|
|
||||||
send_waker: WakerRegistration,
|
|
||||||
recv_waker: WakerRegistration,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn increment(&self, i: usize) -> usize {
|
|
||||||
if i + 1 == self.len {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
i + 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_full(&self) -> bool {
|
|
||||||
self.full
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.front == self.back && !self.full
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_index(&mut self) -> Option<usize> {
|
|
||||||
match self.is_full() {
|
|
||||||
true => None,
|
|
||||||
false => Some(self.back),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_done(&mut self) {
|
|
||||||
assert!(!self.is_full());
|
|
||||||
self.back = self.increment(self.back);
|
|
||||||
if self.back == self.front {
|
|
||||||
self.full = true;
|
|
||||||
}
|
|
||||||
self.send_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_index(&mut self) -> Option<usize> {
|
|
||||||
match self.is_empty() {
|
|
||||||
true => None,
|
|
||||||
false => Some(self.front),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pop_done(&mut self) {
|
|
||||||
assert!(!self.is_empty());
|
|
||||||
self.front = self.increment(self.front);
|
|
||||||
self.full = false;
|
|
||||||
self.recv_waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -21,4 +21,4 @@ target = "thumbv7em-none-eabi"
|
|||||||
features = ["defmt"]
|
features = ["defmt"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
|
@ -4,6 +4,18 @@
|
|||||||
|
|
||||||
use core::task::Context;
|
use core::task::Context;
|
||||||
|
|
||||||
|
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum HardwareAddress {
|
||||||
|
/// A six-octet Ethernet address
|
||||||
|
Ethernet([u8; 6]),
|
||||||
|
/// An eight-octet IEEE802.15.4 address
|
||||||
|
Ieee802154([u8; 8]),
|
||||||
|
/// Indicates that a Driver is IP-native, and has no hardware address
|
||||||
|
Ip,
|
||||||
|
}
|
||||||
|
|
||||||
/// Main `embassy-net` driver API.
|
/// Main `embassy-net` driver API.
|
||||||
///
|
///
|
||||||
/// This is essentially an interface for sending and receiving raw network frames.
|
/// This is essentially an interface for sending and receiving raw network frames.
|
||||||
@ -51,8 +63,8 @@ pub trait Driver {
|
|||||||
/// Get a description of device capabilities.
|
/// Get a description of device capabilities.
|
||||||
fn capabilities(&self) -> Capabilities;
|
fn capabilities(&self) -> Capabilities;
|
||||||
|
|
||||||
/// Get the device's Ethernet address.
|
/// Get the device's hardware address.
|
||||||
fn ethernet_address(&self) -> [u8; 6];
|
fn hardware_address(&self) -> HardwareAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ?Sized + Driver> Driver for &mut T {
|
impl<T: ?Sized + Driver> Driver for &mut T {
|
||||||
@ -75,8 +87,8 @@ impl<T: ?Sized + Driver> Driver for &mut T {
|
|||||||
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
fn link_state(&mut self, cx: &mut Context) -> LinkState {
|
||||||
T::link_state(self, cx)
|
T::link_state(self, cx)
|
||||||
}
|
}
|
||||||
fn ethernet_address(&self) -> [u8; 6] {
|
fn hardware_address(&self) -> HardwareAddress {
|
||||||
T::ethernet_address(self)
|
T::hardware_address(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,6 +176,9 @@ pub enum Medium {
|
|||||||
///
|
///
|
||||||
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
|
||||||
Ip,
|
Ip,
|
||||||
|
|
||||||
|
/// IEEE 802_15_4 medium
|
||||||
|
Ieee802154,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Medium {
|
impl Default for Medium {
|
||||||
|
23
embassy-net-enc28j60/Cargo.toml
Normal file
23
embassy-net-enc28j60/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-net-enc28j60"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "embassy-net driver for the ENC28J60 ethernet chip"
|
||||||
|
keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet", "async"]
|
||||||
|
categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" }
|
||||||
|
embassy-time = { version = "0.1.3", path = "../embassy-time" }
|
||||||
|
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||||
|
|
||||||
|
defmt = { version = "0.3", optional = true }
|
||||||
|
log = { version = "0.4.14", optional = true }
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-enc28j60-v$VERSION/embassy-net-enc28j60/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-enc28j60/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
19
embassy-net-enc28j60/README.md
Normal file
19
embassy-net-enc28j60/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# `embassy-net-enc28j60`
|
||||||
|
|
||||||
|
[`embassy-net`](https://crates.io/crates/embassy-net) integration for the Microchip ENC28J60 Ethernet chip.
|
||||||
|
|
||||||
|
Based on [@japaric](https://github.com/japaric)'s [`enc28j60`](https://github.com/japaric/enc28j60) crate.
|
||||||
|
|
||||||
|
## Interoperability
|
||||||
|
|
||||||
|
This crate can run on any executor.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This work is licensed under either of
|
||||||
|
|
||||||
|
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
at your option.
|
69
embassy-net-enc28j60/src/bank0.rs
Normal file
69
embassy-net-enc28j60/src/bank0.rs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
ERDPTL = 0x00,
|
||||||
|
ERDPTH = 0x01,
|
||||||
|
EWRPTL = 0x02,
|
||||||
|
EWRPTH = 0x03,
|
||||||
|
ETXSTL = 0x04,
|
||||||
|
ETXSTH = 0x05,
|
||||||
|
ETXNDL = 0x06,
|
||||||
|
ETXNDH = 0x07,
|
||||||
|
ERXSTL = 0x08,
|
||||||
|
ERXSTH = 0x09,
|
||||||
|
ERXNDL = 0x0a,
|
||||||
|
ERXNDH = 0x0b,
|
||||||
|
ERXRDPTL = 0x0c,
|
||||||
|
ERXRDPTH = 0x0d,
|
||||||
|
ERXWRPTL = 0x0e,
|
||||||
|
ERXWRPTH = 0x0f,
|
||||||
|
EDMASTL = 0x10,
|
||||||
|
EDMASTH = 0x11,
|
||||||
|
EDMANDL = 0x12,
|
||||||
|
EDMANDH = 0x13,
|
||||||
|
EDMADSTL = 0x14,
|
||||||
|
EDMADSTH = 0x15,
|
||||||
|
EDMACSL = 0x16,
|
||||||
|
EDMACSH = 0x17,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::ERDPTL => true,
|
||||||
|
Register::ERDPTH => true,
|
||||||
|
Register::EWRPTL => true,
|
||||||
|
Register::EWRPTH => true,
|
||||||
|
Register::ETXSTL => true,
|
||||||
|
Register::ETXSTH => true,
|
||||||
|
Register::ETXNDL => true,
|
||||||
|
Register::ETXNDH => true,
|
||||||
|
Register::ERXSTL => true,
|
||||||
|
Register::ERXSTH => true,
|
||||||
|
Register::ERXNDL => true,
|
||||||
|
Register::ERXNDH => true,
|
||||||
|
Register::ERXRDPTL => true,
|
||||||
|
Register::ERXRDPTH => true,
|
||||||
|
Register::ERXWRPTL => true,
|
||||||
|
Register::ERXWRPTH => true,
|
||||||
|
Register::EDMASTL => true,
|
||||||
|
Register::EDMASTH => true,
|
||||||
|
Register::EDMANDL => true,
|
||||||
|
Register::EDMANDH => true,
|
||||||
|
Register::EDMADSTL => true,
|
||||||
|
Register::EDMADSTH => true,
|
||||||
|
Register::EDMACSL => true,
|
||||||
|
Register::EDMACSH => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<super::Register> for Register {
|
||||||
|
fn into(self) -> super::Register {
|
||||||
|
super::Register::Bank0(self)
|
||||||
|
}
|
||||||
|
}
|
84
embassy-net-enc28j60/src/bank1.rs
Normal file
84
embassy-net-enc28j60/src/bank1.rs
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
EHT0 = 0x00,
|
||||||
|
EHT1 = 0x01,
|
||||||
|
EHT2 = 0x02,
|
||||||
|
EHT3 = 0x03,
|
||||||
|
EHT4 = 0x04,
|
||||||
|
EHT5 = 0x05,
|
||||||
|
EHT6 = 0x06,
|
||||||
|
EHT7 = 0x07,
|
||||||
|
EPMM0 = 0x08,
|
||||||
|
EPMM1 = 0x09,
|
||||||
|
EPMM2 = 0x0a,
|
||||||
|
EPMM3 = 0x0b,
|
||||||
|
EPMM4 = 0x0c,
|
||||||
|
EPMM5 = 0x0d,
|
||||||
|
EPMM6 = 0x0e,
|
||||||
|
EPMM7 = 0x0f,
|
||||||
|
EPMCSL = 0x10,
|
||||||
|
EPMCSH = 0x11,
|
||||||
|
EPMOL = 0x14,
|
||||||
|
EPMOH = 0x15,
|
||||||
|
ERXFCON = 0x18,
|
||||||
|
EPKTCNT = 0x19,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::EHT0 => true,
|
||||||
|
Register::EHT1 => true,
|
||||||
|
Register::EHT2 => true,
|
||||||
|
Register::EHT3 => true,
|
||||||
|
Register::EHT4 => true,
|
||||||
|
Register::EHT5 => true,
|
||||||
|
Register::EHT6 => true,
|
||||||
|
Register::EHT7 => true,
|
||||||
|
Register::EPMM0 => true,
|
||||||
|
Register::EPMM1 => true,
|
||||||
|
Register::EPMM2 => true,
|
||||||
|
Register::EPMM3 => true,
|
||||||
|
Register::EPMM4 => true,
|
||||||
|
Register::EPMM5 => true,
|
||||||
|
Register::EPMM6 => true,
|
||||||
|
Register::EPMM7 => true,
|
||||||
|
Register::EPMCSL => true,
|
||||||
|
Register::EPMCSH => true,
|
||||||
|
Register::EPMOL => true,
|
||||||
|
Register::EPMOH => true,
|
||||||
|
Register::ERXFCON => true,
|
||||||
|
Register::EPKTCNT => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<super::Register> for Register {
|
||||||
|
fn into(self) -> super::Register {
|
||||||
|
super::Register::Bank1(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(ERXFCON, 0b1010_0001, u8, {
|
||||||
|
#[doc = "Broadcast Filter Enable bit"]
|
||||||
|
bcen @ 0,
|
||||||
|
#[doc = "Multicast Filter Enable bit"]
|
||||||
|
mcen @ 1,
|
||||||
|
#[doc = "Hash Table Filter Enable bit"]
|
||||||
|
hten @ 2,
|
||||||
|
#[doc = "Magic Packet™ Filter Enable bit"]
|
||||||
|
mpen @ 3,
|
||||||
|
#[doc = "Pattern Match Filter Enable bit"]
|
||||||
|
pmen @ 4,
|
||||||
|
#[doc = "Post-Filter CRC Check Enable bit"]
|
||||||
|
crcen @ 5,
|
||||||
|
#[doc = "AND/OR Filter Select bit"]
|
||||||
|
andor @ 6,
|
||||||
|
#[doc = "Unicast Filter Enable bit"]
|
||||||
|
ucen @ 7,
|
||||||
|
});
|
86
embassy-net-enc28j60/src/bank2.rs
Normal file
86
embassy-net-enc28j60/src/bank2.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
MACON1 = 0x00,
|
||||||
|
MACON3 = 0x02,
|
||||||
|
MACON4 = 0x03,
|
||||||
|
MABBIPG = 0x04,
|
||||||
|
MAIPGL = 0x06,
|
||||||
|
MAIPGH = 0x07,
|
||||||
|
MACLCON1 = 0x08,
|
||||||
|
MACLCON2 = 0x09,
|
||||||
|
MAMXFLL = 0x0a,
|
||||||
|
MAMXFLH = 0x0b,
|
||||||
|
MICMD = 0x12,
|
||||||
|
MIREGADR = 0x14,
|
||||||
|
MIWRL = 0x16,
|
||||||
|
MIWRH = 0x17,
|
||||||
|
MIRDL = 0x18,
|
||||||
|
MIRDH = 0x19,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::MACON1 => false,
|
||||||
|
Register::MACON3 => false,
|
||||||
|
Register::MACON4 => false,
|
||||||
|
Register::MABBIPG => false,
|
||||||
|
Register::MAIPGL => false,
|
||||||
|
Register::MAIPGH => false,
|
||||||
|
Register::MACLCON1 => false,
|
||||||
|
Register::MACLCON2 => false,
|
||||||
|
Register::MAMXFLL => false,
|
||||||
|
Register::MAMXFLH => false,
|
||||||
|
Register::MICMD => false,
|
||||||
|
Register::MIREGADR => false,
|
||||||
|
Register::MIWRL => false,
|
||||||
|
Register::MIWRH => false,
|
||||||
|
Register::MIRDL => false,
|
||||||
|
Register::MIRDH => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<super::Register> for Register {
|
||||||
|
fn into(self) -> super::Register {
|
||||||
|
super::Register::Bank2(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(MACON1, 0, u8, {
|
||||||
|
#[doc = "Enable packets to be received by the MAC"]
|
||||||
|
marxen @ 0,
|
||||||
|
#[doc = "Control frames will be discarded after being processed by the MAC"]
|
||||||
|
passall @ 1,
|
||||||
|
#[doc = "Inhibit transmissions when pause control frames are received"]
|
||||||
|
rxpaus @ 2,
|
||||||
|
#[doc = "Allow the MAC to transmit pause control frames"]
|
||||||
|
txpaus @ 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(MACON3, 0, u8, {
|
||||||
|
#[doc = "MAC will operate in Full-Duplex mode"]
|
||||||
|
fuldpx @ 0,
|
||||||
|
#[doc = "The type/length field of transmitted and received frames will be checked"]
|
||||||
|
frmlnen @ 1,
|
||||||
|
#[doc = "Frames bigger than MAMXFL will be aborted when transmitted or received"]
|
||||||
|
hfrmen @ 2,
|
||||||
|
#[doc = "No proprietary header is present"]
|
||||||
|
phdren @ 3,
|
||||||
|
#[doc = "MAC will append a valid CRC to all frames transmitted regardless of PADCFG bit"]
|
||||||
|
txcrcen @ 4,
|
||||||
|
#[doc = "All short frames will be zero-padded to 64 bytes and a valid CRC will then be appended"]
|
||||||
|
padcfg @ 5..7,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(MICMD, 0, u8, {
|
||||||
|
#[doc = "MII Read Enable bit"]
|
||||||
|
miird @ 0,
|
||||||
|
#[doc = "MII Scan Enable bit"]
|
||||||
|
miiscan @ 1,
|
||||||
|
});
|
53
embassy-net-enc28j60/src/bank3.rs
Normal file
53
embassy-net-enc28j60/src/bank3.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
MAADR5 = 0x00,
|
||||||
|
MAADR6 = 0x01,
|
||||||
|
MAADR3 = 0x02,
|
||||||
|
MAADR4 = 0x03,
|
||||||
|
MAADR1 = 0x04,
|
||||||
|
MAADR2 = 0x05,
|
||||||
|
EBSTSD = 0x06,
|
||||||
|
EBSTCON = 0x07,
|
||||||
|
EBSTCSL = 0x08,
|
||||||
|
EBSTCSH = 0x09,
|
||||||
|
MISTAT = 0x0a,
|
||||||
|
EREVID = 0x12,
|
||||||
|
ECOCON = 0x15,
|
||||||
|
EFLOCON = 0x17,
|
||||||
|
EPAUSL = 0x18,
|
||||||
|
EPAUSH = 0x19,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::MAADR5 => false,
|
||||||
|
Register::MAADR6 => false,
|
||||||
|
Register::MAADR3 => false,
|
||||||
|
Register::MAADR4 => false,
|
||||||
|
Register::MAADR1 => false,
|
||||||
|
Register::MAADR2 => false,
|
||||||
|
Register::EBSTSD => true,
|
||||||
|
Register::EBSTCON => true,
|
||||||
|
Register::EBSTCSL => true,
|
||||||
|
Register::EBSTCSH => true,
|
||||||
|
Register::MISTAT => false,
|
||||||
|
Register::EREVID => true,
|
||||||
|
Register::ECOCON => true,
|
||||||
|
Register::EFLOCON => true,
|
||||||
|
Register::EPAUSL => true,
|
||||||
|
Register::EPAUSH => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<super::Register> for Register {
|
||||||
|
fn into(self) -> super::Register {
|
||||||
|
super::Register::Bank3(self)
|
||||||
|
}
|
||||||
|
}
|
106
embassy-net-enc28j60/src/common.rs
Normal file
106
embassy-net-enc28j60/src/common.rs
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
ECON1 = 0x1f,
|
||||||
|
ECON2 = 0x1e,
|
||||||
|
EIE = 0x1b,
|
||||||
|
EIR = 0x1c,
|
||||||
|
ESTAT = 0x1d,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::ECON1 => true,
|
||||||
|
Register::ECON2 => true,
|
||||||
|
Register::EIE => true,
|
||||||
|
Register::EIR => true,
|
||||||
|
Register::ESTAT => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<super::Register> for Register {
|
||||||
|
fn into(self) -> super::Register {
|
||||||
|
super::Register::Common(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(EIE, 0, u8, {
|
||||||
|
#[doc = "Receive Error Interrupt Enable bit"]
|
||||||
|
rxerie @ 0,
|
||||||
|
#[doc = "Transmit Error Interrupt Enable bit"]
|
||||||
|
txerie @ 1,
|
||||||
|
#[doc = "Transmit Enable bit"]
|
||||||
|
txie @ 3,
|
||||||
|
#[doc = "Link Status Change Interrupt Enable bit"]
|
||||||
|
linkie @ 4,
|
||||||
|
#[doc = "DMA Interrupt Enable bit"]
|
||||||
|
dmaie @ 5,
|
||||||
|
#[doc = "Receive Packet Pending Interrupt Enable bit"]
|
||||||
|
pktie @ 6,
|
||||||
|
#[doc = "Global INT Interrupt Enable bit"]
|
||||||
|
intie @ 7,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(EIR, 0, u8, {
|
||||||
|
#[doc = "Receive Error Interrupt Flag bit"]
|
||||||
|
rxerif @ 0,
|
||||||
|
#[doc = "Transmit Error Interrupt Flag bit"]
|
||||||
|
txerif @ 1,
|
||||||
|
#[doc = "Transmit Interrupt Flag bit"]
|
||||||
|
txif @ 3,
|
||||||
|
#[doc = "Link Change Interrupt Flag bit"]
|
||||||
|
linkif @ 4,
|
||||||
|
#[doc = "DMA Interrupt Flag bit"]
|
||||||
|
dmaif @ 5,
|
||||||
|
#[doc = "Receive Packet Pending Interrupt Flag bit"]
|
||||||
|
pktif @ 6,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(ESTAT, 0, u8, {
|
||||||
|
#[doc = "Clock Ready bit"]
|
||||||
|
clkrdy @ 0,
|
||||||
|
#[doc = "Transmit Abort Error bit"]
|
||||||
|
txabrt @ 1,
|
||||||
|
#[doc = "Receive Busy bit"]
|
||||||
|
rxbusy @ 2,
|
||||||
|
#[doc = "Late Collision Error bit"]
|
||||||
|
latecol @ 4,
|
||||||
|
#[doc = "Ethernet Buffer Error Status bit"]
|
||||||
|
bufer @ 6,
|
||||||
|
#[doc = "INT Interrupt Flag bit"]
|
||||||
|
int @ 7,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(ECON2, 0b1000_0000, u8, {
|
||||||
|
#[doc = "Voltage Regulator Power Save Enable bit"]
|
||||||
|
vrps @ 3,
|
||||||
|
#[doc = "Power Save Enable bit"]
|
||||||
|
pwrsv @ 5,
|
||||||
|
#[doc = "Packet Decrement bit"]
|
||||||
|
pktdec @ 6,
|
||||||
|
#[doc = "Automatic Buffer Pointer Increment Enable bit"]
|
||||||
|
autoinc @ 7,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(ECON1, 0, u8, {
|
||||||
|
#[doc = "Bank Select bits"]
|
||||||
|
bsel @ 0..1,
|
||||||
|
#[doc = "Receive Enable bi"]
|
||||||
|
rxen @ 2,
|
||||||
|
#[doc = "Transmit Request to Send bit"]
|
||||||
|
txrts @ 3,
|
||||||
|
#[doc = "DMA Checksum Enable bit"]
|
||||||
|
csumen @ 4,
|
||||||
|
#[doc = "DMA Start and Busy Status bit"]
|
||||||
|
dmast @ 5,
|
||||||
|
#[doc = "Receive Logic Reset bit"]
|
||||||
|
rxrst @ 6,
|
||||||
|
#[doc = "Transmit Logic Reset bit"]
|
||||||
|
txrst @ 7,
|
||||||
|
});
|
258
embassy-net-enc28j60/src/fmt.rs
Normal file
258
embassy-net-enc28j60/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)
|
||||||
|
}
|
||||||
|
}
|
30
embassy-net-enc28j60/src/header.rs
Normal file
30
embassy-net-enc28j60/src/header.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
register!(RxStatus, 0, u32, {
|
||||||
|
#[doc = "Indicates length of the received frame"]
|
||||||
|
byte_count @ 0..15,
|
||||||
|
#[doc = "Indicates a packet over 50,000 bit times occurred or that a packet was dropped since the last receive"]
|
||||||
|
long_event @ 16,
|
||||||
|
#[doc = "Indicates that at some time since the last receive, a carrier event was detected"]
|
||||||
|
carrier_event @ 18,
|
||||||
|
#[doc = "Indicates that frame CRC field value does not match the CRC calculated by the MAC"]
|
||||||
|
crc_error @ 20,
|
||||||
|
#[doc = "Indicates that frame length field value in the packet does not match the actual data byte length and specifies a valid length"]
|
||||||
|
length_check_error @ 21,
|
||||||
|
#[doc = "Indicates that frame type/length field was larger than 1500 bytes (type field)"]
|
||||||
|
length_out_of_range @ 22,
|
||||||
|
#[doc = "Indicates that at the packet had a valid CRC and no symbol errors"]
|
||||||
|
received_ok @ 23,
|
||||||
|
#[doc = "Indicates packet received had a valid Multicast address"]
|
||||||
|
multicast @ 24,
|
||||||
|
#[doc = "Indicates packet received had a valid Broadcast address."]
|
||||||
|
broadcast @ 25,
|
||||||
|
#[doc = "Indicates that after the end of this packet, an additional 1 to 7 bits were received"]
|
||||||
|
dribble_nibble @ 26,
|
||||||
|
#[doc = "Current frame was recognized as a control frame for having a valid type/length designating it as a control frame"]
|
||||||
|
receive_control_frame @ 27,
|
||||||
|
#[doc = "Current frame was recognized as a control frame containing a valid pause frame opcode and a valid destination address"]
|
||||||
|
receive_pause_control_frame @ 28,
|
||||||
|
#[doc = "Current frame was recognized as a control frame but it contained an unknown opcode"]
|
||||||
|
receive_unknown_opcode @ 29,
|
||||||
|
#[doc = "Current frame was recognized as a VLAN tagged frame"]
|
||||||
|
receive_vlan_type_detected @ 30,
|
||||||
|
});
|
717
embassy-net-enc28j60/src/lib.rs
Normal file
717
embassy-net-enc28j60/src/lib.rs
Normal file
@ -0,0 +1,717 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
// must go first.
|
||||||
|
mod fmt;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
mod bank0;
|
||||||
|
mod bank1;
|
||||||
|
mod bank2;
|
||||||
|
mod bank3;
|
||||||
|
mod common;
|
||||||
|
mod header;
|
||||||
|
mod phy;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
use core::cmp;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
|
||||||
|
use embassy_net_driver::{Capabilities, HardwareAddress, LinkState, Medium};
|
||||||
|
use embassy_time::Duration;
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
|
use embedded_hal::spi::{Operation, SpiDevice};
|
||||||
|
use traits::U16Ext;
|
||||||
|
|
||||||
|
// Total buffer size (see section 3.2)
|
||||||
|
const BUF_SZ: u16 = 8 * 1024;
|
||||||
|
|
||||||
|
// Maximum frame length
|
||||||
|
const MAX_FRAME_LENGTH: u16 = 1518; // value recommended in the data sheet
|
||||||
|
|
||||||
|
// Size of the Frame check sequence (32-bit CRC)
|
||||||
|
const CRC_SZ: u16 = 4;
|
||||||
|
|
||||||
|
// define the boundaries of the TX and RX buffers
|
||||||
|
// to workaround errata #5 we do the opposite of what section 6.1 of the data sheet
|
||||||
|
// says: we place the RX buffer at address 0 and the TX buffer after it
|
||||||
|
const RXST: u16 = 0x0000;
|
||||||
|
const RXND: u16 = 0x19ff;
|
||||||
|
const TXST: u16 = 0x1a00;
|
||||||
|
const _TXND: u16 = 0x1fff;
|
||||||
|
|
||||||
|
const MTU: usize = 1514; // 1500 IP + 14 ethernet header
|
||||||
|
|
||||||
|
/// ENC28J60 embassy-net driver
|
||||||
|
pub struct Enc28j60<S, O> {
|
||||||
|
mac_addr: [u8; 6],
|
||||||
|
|
||||||
|
spi: S,
|
||||||
|
rst: Option<O>,
|
||||||
|
|
||||||
|
bank: Bank,
|
||||||
|
|
||||||
|
// address of the next packet in buffer memory
|
||||||
|
next_packet: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, O> Enc28j60<S, O>
|
||||||
|
where
|
||||||
|
S: SpiDevice,
|
||||||
|
O: OutputPin,
|
||||||
|
{
|
||||||
|
/// Create a new ENC28J60 driver instance.
|
||||||
|
///
|
||||||
|
/// The RST pin is optional. If None, reset will be done with a SPI
|
||||||
|
/// soft reset command, instead of via the RST pin.
|
||||||
|
pub fn new(spi: S, rst: Option<O>, mac_addr: [u8; 6]) -> Self {
|
||||||
|
let mut res = Self {
|
||||||
|
mac_addr,
|
||||||
|
spi,
|
||||||
|
rst,
|
||||||
|
|
||||||
|
bank: Bank::Bank0,
|
||||||
|
next_packet: RXST,
|
||||||
|
};
|
||||||
|
res.init();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(&mut self) {
|
||||||
|
if let Some(rst) = &mut self.rst {
|
||||||
|
rst.set_low().unwrap();
|
||||||
|
embassy_time::block_for(Duration::from_millis(5));
|
||||||
|
rst.set_high().unwrap();
|
||||||
|
embassy_time::block_for(Duration::from_millis(5));
|
||||||
|
} else {
|
||||||
|
embassy_time::block_for(Duration::from_millis(5));
|
||||||
|
self.soft_reset();
|
||||||
|
embassy_time::block_for(Duration::from_millis(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"enc28j60: erevid {=u8:x}",
|
||||||
|
self.read_control_register(bank3::Register::EREVID)
|
||||||
|
);
|
||||||
|
debug!("enc28j60: waiting for clk");
|
||||||
|
while common::ESTAT(self.read_control_register(common::Register::ESTAT)).clkrdy() == 0 {}
|
||||||
|
debug!("enc28j60: clk ok");
|
||||||
|
|
||||||
|
if self.read_control_register(bank3::Register::EREVID) == 0 {
|
||||||
|
panic!("ErevidIsZero");
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable CLKOUT output
|
||||||
|
self.write_control_register(bank3::Register::ECOCON, 0);
|
||||||
|
|
||||||
|
self.init_rx();
|
||||||
|
|
||||||
|
// TX start
|
||||||
|
// "It is recommended that an even address be used for ETXST"
|
||||||
|
debug_assert_eq!(TXST % 2, 0);
|
||||||
|
self.write_control_register(bank0::Register::ETXSTL, TXST.low());
|
||||||
|
self.write_control_register(bank0::Register::ETXSTH, TXST.high());
|
||||||
|
|
||||||
|
// TX end is set in `transmit`
|
||||||
|
|
||||||
|
// MAC initialization (see section 6.5)
|
||||||
|
// 1. Set the MARXEN bit in MACON1 to enable the MAC to receive frames.
|
||||||
|
self.write_control_register(
|
||||||
|
bank2::Register::MACON1,
|
||||||
|
bank2::MACON1::default().marxen(1).passall(0).rxpaus(1).txpaus(1).bits(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Configure the PADCFG, TXCRCEN and FULDPX bits of MACON3.
|
||||||
|
self.write_control_register(
|
||||||
|
bank2::Register::MACON3,
|
||||||
|
bank2::MACON3::default().frmlnen(1).txcrcen(1).padcfg(0b001).bits(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Program the MAMXFL registers with the maximum frame length to be permitted to be
|
||||||
|
// received or transmitted
|
||||||
|
self.write_control_register(bank2::Register::MAMXFLL, MAX_FRAME_LENGTH.low());
|
||||||
|
self.write_control_register(bank2::Register::MAMXFLH, MAX_FRAME_LENGTH.high());
|
||||||
|
|
||||||
|
// 5. Configure the Back-to-Back Inter-Packet Gap register, MABBIPG.
|
||||||
|
// Use recommended value of 0x12
|
||||||
|
self.write_control_register(bank2::Register::MABBIPG, 0x12);
|
||||||
|
|
||||||
|
// 6. Configure the Non-Back-to-Back Inter-Packet Gap register low byte, MAIPGL.
|
||||||
|
// Use recommended value of 0x12
|
||||||
|
self.write_control_register(bank2::Register::MAIPGL, 0x12);
|
||||||
|
self.write_control_register(bank2::Register::MAIPGH, 0x0c);
|
||||||
|
|
||||||
|
// 9. Program the local MAC address into the MAADR1:MAADR6 registers
|
||||||
|
self.write_control_register(bank3::Register::MAADR1, self.mac_addr[0]);
|
||||||
|
self.write_control_register(bank3::Register::MAADR2, self.mac_addr[1]);
|
||||||
|
self.write_control_register(bank3::Register::MAADR3, self.mac_addr[2]);
|
||||||
|
self.write_control_register(bank3::Register::MAADR4, self.mac_addr[3]);
|
||||||
|
self.write_control_register(bank3::Register::MAADR5, self.mac_addr[4]);
|
||||||
|
self.write_control_register(bank3::Register::MAADR6, self.mac_addr[5]);
|
||||||
|
|
||||||
|
// Set the PHCON2.HDLDIS bit to prevent automatic loopback of the data which is transmitted
|
||||||
|
self.write_phy_register(phy::Register::PHCON2, phy::PHCON2::default().hdldis(1).bits());
|
||||||
|
|
||||||
|
// Globally enable interrupts
|
||||||
|
//self.bit_field_set(common::Register::EIE, common::EIE::mask().intie());
|
||||||
|
|
||||||
|
// Set the per packet control byte; we'll always use the value 0
|
||||||
|
self.write_buffer_memory(Some(TXST), &mut [0]);
|
||||||
|
|
||||||
|
// Enable reception
|
||||||
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_rx(&mut self) {
|
||||||
|
// RX start
|
||||||
|
// "It is recommended that the ERXST Pointer be programmed with an even address"
|
||||||
|
self.write_control_register(bank0::Register::ERXSTL, RXST.low());
|
||||||
|
self.write_control_register(bank0::Register::ERXSTH, RXST.high());
|
||||||
|
|
||||||
|
// RX read pointer
|
||||||
|
// NOTE Errata #14 so we are using an *odd* address here instead of ERXST
|
||||||
|
self.write_control_register(bank0::Register::ERXRDPTL, RXND.low());
|
||||||
|
self.write_control_register(bank0::Register::ERXRDPTH, RXND.high());
|
||||||
|
|
||||||
|
// RX end
|
||||||
|
self.write_control_register(bank0::Register::ERXNDL, RXND.low());
|
||||||
|
self.write_control_register(bank0::Register::ERXNDH, RXND.high());
|
||||||
|
|
||||||
|
// decrease the packet count to 0
|
||||||
|
while self.read_control_register(bank1::Register::EPKTCNT) != 0 {
|
||||||
|
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next_packet = RXST;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_rx(&mut self) {
|
||||||
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxrst());
|
||||||
|
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().rxrst());
|
||||||
|
self.init_rx();
|
||||||
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().rxen());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes the transmit buffer, ensuring all pending transmissions have completed
|
||||||
|
/// NOTE: The returned packet *must* be `read` or `ignore`-d, otherwise this method will always
|
||||||
|
/// return `None` on subsequent invocations
|
||||||
|
pub fn receive<'a>(&mut self, buf: &'a mut [u8]) -> Option<&'a mut [u8]> {
|
||||||
|
if self.pending_packets() == 0 {
|
||||||
|
// Errata #6: we can't rely on PKTIF so we check PKTCNT
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let curr_packet = self.next_packet;
|
||||||
|
|
||||||
|
// read out the first 6 bytes
|
||||||
|
let mut temp_buf = [0; 6];
|
||||||
|
self.read_buffer_memory(Some(curr_packet), &mut temp_buf);
|
||||||
|
|
||||||
|
// next packet pointer
|
||||||
|
let next_packet = u16::from_parts(temp_buf[0], temp_buf[1]);
|
||||||
|
// status vector
|
||||||
|
let status = header::RxStatus(u32::from_le_bytes(temp_buf[2..].try_into().unwrap()));
|
||||||
|
let len_with_crc = status.byte_count() as u16;
|
||||||
|
|
||||||
|
if len_with_crc < CRC_SZ || len_with_crc > 1600 || next_packet > RXND {
|
||||||
|
warn!("RX buffer corrupted, resetting RX logic to recover...");
|
||||||
|
self.reset_rx();
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = len_with_crc - CRC_SZ;
|
||||||
|
self.read_buffer_memory(None, &mut buf[..len as usize]);
|
||||||
|
|
||||||
|
// update ERXRDPT
|
||||||
|
// due to Errata #14 we must write an odd address to ERXRDPT
|
||||||
|
// we know that ERXST = 0, that ERXND is odd and that next_packet is even
|
||||||
|
let rxrdpt = if self.next_packet < 1 || self.next_packet > RXND + 1 {
|
||||||
|
RXND
|
||||||
|
} else {
|
||||||
|
self.next_packet - 1
|
||||||
|
};
|
||||||
|
// "To move ERXRDPT, the host controller must write to ERXRDPTL first."
|
||||||
|
self.write_control_register(bank0::Register::ERXRDPTL, rxrdpt.low());
|
||||||
|
self.write_control_register(bank0::Register::ERXRDPTH, rxrdpt.high());
|
||||||
|
|
||||||
|
// decrease the packet count
|
||||||
|
self.bit_field_set(common::Register::ECON2, common::ECON2::mask().pktdec());
|
||||||
|
|
||||||
|
self.next_packet = next_packet;
|
||||||
|
|
||||||
|
Some(&mut buf[..len as usize])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_tx_ready(&mut self) {
|
||||||
|
for _ in 0u32..10000 {
|
||||||
|
if common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// work around errata #12 by resetting the transmit logic before every new
|
||||||
|
// transmission
|
||||||
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrst());
|
||||||
|
self.bit_field_clear(common::Register::ECON1, common::ECON1::mask().txrst());
|
||||||
|
//self.bit_field_clear(common::Register::EIR, {
|
||||||
|
// let mask = common::EIR::mask();
|
||||||
|
// mask.txerif() | mask.txif()
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the transmission of `bytes`
|
||||||
|
///
|
||||||
|
/// It's up to the caller to ensure that `bytes` is a valid Ethernet frame. The interface will
|
||||||
|
/// take care of appending a (4 byte) CRC to the frame and of padding the frame to the minimum
|
||||||
|
/// size allowed by the Ethernet specification (64 bytes, or 46 bytes of payload).
|
||||||
|
///
|
||||||
|
/// NOTE This method will flush any previous transmission that's in progress
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `bytes` length is greater than 1514, the maximum frame length allowed by the interface,
|
||||||
|
/// or greater than the transmit buffer
|
||||||
|
pub fn transmit(&mut self, bytes: &[u8]) {
|
||||||
|
assert!(bytes.len() <= self.mtu() as usize);
|
||||||
|
|
||||||
|
self.wait_tx_ready();
|
||||||
|
|
||||||
|
// NOTE the plus one is to not overwrite the per packet control byte
|
||||||
|
let wrpt = TXST + 1;
|
||||||
|
|
||||||
|
// 1. ETXST was set during initialization
|
||||||
|
|
||||||
|
// 2. write the frame to the IC memory
|
||||||
|
self.write_buffer_memory(Some(wrpt), bytes);
|
||||||
|
|
||||||
|
let txnd = wrpt + bytes.len() as u16 - 1;
|
||||||
|
|
||||||
|
// 3. Set the end address of the transmit buffer
|
||||||
|
self.write_control_register(bank0::Register::ETXNDL, txnd.low());
|
||||||
|
self.write_control_register(bank0::Register::ETXNDH, txnd.high());
|
||||||
|
|
||||||
|
// 4. reset interrupt flag
|
||||||
|
//self.bit_field_clear(common::Register::EIR, common::EIR::mask().txif());
|
||||||
|
|
||||||
|
// 5. start transmission
|
||||||
|
self.bit_field_set(common::Register::ECON1, common::ECON1::mask().txrts());
|
||||||
|
|
||||||
|
// Wait until transmission finishes
|
||||||
|
//while common::ECON1(self.read_control_register(common::Register::ECON1)).txrts() == 1 {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// read the transmit status vector
|
||||||
|
let mut tx_stat = [0; 7];
|
||||||
|
self.read_buffer_memory(None, &mut tx_stat);
|
||||||
|
|
||||||
|
let stat = common::ESTAT(self.read_control_register(common::Register::ESTAT));
|
||||||
|
|
||||||
|
if stat.txabrt() == 1 {
|
||||||
|
// work around errata #12 by reading the transmit status vector
|
||||||
|
if stat.latecol() == 1 || (tx_stat[2] & (1 << 5)) != 0 {
|
||||||
|
panic!("LateCollision")
|
||||||
|
} else {
|
||||||
|
panic!("TransmitAbort")
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get whether the link is up
|
||||||
|
pub fn is_link_up(&mut self) -> bool {
|
||||||
|
let bits = self.read_phy_register(phy::Register::PHSTAT2);
|
||||||
|
phy::PHSTAT2(bits).lstat() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the interface Maximum Transmission Unit (MTU)
|
||||||
|
///
|
||||||
|
/// The value returned by this function will never exceed 1514 bytes. The actual value depends
|
||||||
|
/// on the memory assigned to the transmission buffer when initializing the device
|
||||||
|
pub fn mtu(&self) -> u16 {
|
||||||
|
cmp::min(BUF_SZ - RXND - 1, MAX_FRAME_LENGTH - CRC_SZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Miscellaneous */
|
||||||
|
/// Returns the number of packets that have been received but have not been processed yet
|
||||||
|
pub fn pending_packets(&mut self) -> u8 {
|
||||||
|
self.read_control_register(bank1::Register::EPKTCNT)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adjusts the receive filter to *accept* these packet types
|
||||||
|
pub fn accept(&mut self, packets: &[Packet]) {
|
||||||
|
let mask = bank1::ERXFCON::mask();
|
||||||
|
let mut val = 0;
|
||||||
|
for packet in packets {
|
||||||
|
match packet {
|
||||||
|
Packet::Broadcast => val |= mask.bcen(),
|
||||||
|
Packet::Multicast => val |= mask.mcen(),
|
||||||
|
Packet::Unicast => val |= mask.ucen(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bit_field_set(bank1::Register::ERXFCON, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adjusts the receive filter to *ignore* these packet types
|
||||||
|
pub fn ignore(&mut self, packets: &[Packet]) {
|
||||||
|
let mask = bank1::ERXFCON::mask();
|
||||||
|
let mut val = 0;
|
||||||
|
for packet in packets {
|
||||||
|
match packet {
|
||||||
|
Packet::Broadcast => val |= mask.bcen(),
|
||||||
|
Packet::Multicast => val |= mask.mcen(),
|
||||||
|
Packet::Unicast => val |= mask.ucen(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bit_field_clear(bank1::Register::ERXFCON, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Private */
|
||||||
|
/* Read */
|
||||||
|
fn read_control_register<R>(&mut self, register: R) -> u8
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
self._read_control_register(register.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _read_control_register(&mut self, register: Register) -> u8 {
|
||||||
|
self.change_bank(register);
|
||||||
|
|
||||||
|
if register.is_eth_register() {
|
||||||
|
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0];
|
||||||
|
self.spi.transfer_in_place(&mut buffer).unwrap();
|
||||||
|
buffer[1]
|
||||||
|
} else {
|
||||||
|
// MAC, MII regs need a dummy byte.
|
||||||
|
let mut buffer = [Instruction::RCR.opcode() | register.addr(), 0, 0];
|
||||||
|
self.spi.transfer_in_place(&mut buffer).unwrap();
|
||||||
|
buffer[2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_phy_register(&mut self, register: phy::Register) -> u16 {
|
||||||
|
// set PHY register address
|
||||||
|
self.write_control_register(bank2::Register::MIREGADR, register.addr());
|
||||||
|
|
||||||
|
// start read operation
|
||||||
|
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(1).bits());
|
||||||
|
|
||||||
|
// wait until the read operation finishes
|
||||||
|
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
|
||||||
|
|
||||||
|
self.write_control_register(bank2::Register::MICMD, bank2::MICMD::default().miird(0).bits());
|
||||||
|
|
||||||
|
let l = self.read_control_register(bank2::Register::MIRDL);
|
||||||
|
let h = self.read_control_register(bank2::Register::MIRDH);
|
||||||
|
(l as u16) | (h as u16) << 8
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write */
|
||||||
|
fn _write_control_register(&mut self, register: Register, value: u8) {
|
||||||
|
self.change_bank(register);
|
||||||
|
|
||||||
|
let buffer = [Instruction::WCR.opcode() | register.addr(), value];
|
||||||
|
self.spi.write(&buffer).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_control_register<R>(&mut self, register: R, value: u8)
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
self._write_control_register(register.into(), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_phy_register(&mut self, register: phy::Register, value: u16) {
|
||||||
|
// set PHY register address
|
||||||
|
self.write_control_register(bank2::Register::MIREGADR, register.addr());
|
||||||
|
|
||||||
|
self.write_control_register(bank2::Register::MIWRL, (value & 0xff) as u8);
|
||||||
|
// this starts the write operation
|
||||||
|
self.write_control_register(bank2::Register::MIWRH, (value >> 8) as u8);
|
||||||
|
|
||||||
|
// wait until the write operation finishes
|
||||||
|
while self.read_control_register(bank3::Register::MISTAT) & 0b1 != 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RMW */
|
||||||
|
fn modify_control_register<R, F>(&mut self, register: R, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(u8) -> u8,
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
self._modify_control_register(register.into(), f)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _modify_control_register<F>(&mut self, register: Register, f: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(u8) -> u8,
|
||||||
|
{
|
||||||
|
let r = self._read_control_register(register);
|
||||||
|
self._write_control_register(register, f(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auxiliary */
|
||||||
|
fn change_bank(&mut self, register: Register) {
|
||||||
|
let bank = register.bank();
|
||||||
|
|
||||||
|
if let Some(bank) = bank {
|
||||||
|
if self.bank == bank {
|
||||||
|
// already on the register bank
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// change bank
|
||||||
|
self.bank = bank;
|
||||||
|
match bank {
|
||||||
|
Bank::Bank0 => self.bit_field_clear(common::Register::ECON1, 0b11),
|
||||||
|
Bank::Bank1 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b01),
|
||||||
|
Bank::Bank2 => self.modify_control_register(common::Register::ECON1, |r| (r & !0b11) | 0b10),
|
||||||
|
Bank::Bank3 => self.bit_field_set(common::Register::ECON1, 0b11),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// common register
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primitive operations */
|
||||||
|
fn bit_field_clear<R>(&mut self, register: R, mask: u8)
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
self._bit_field_clear(register.into(), mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _bit_field_clear(&mut self, register: Register, mask: u8) {
|
||||||
|
debug_assert!(register.is_eth_register());
|
||||||
|
|
||||||
|
self.change_bank(register);
|
||||||
|
|
||||||
|
self.spi
|
||||||
|
.write(&[Instruction::BFC.opcode() | register.addr(), mask])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bit_field_set<R>(&mut self, register: R, mask: u8)
|
||||||
|
where
|
||||||
|
R: Into<Register>,
|
||||||
|
{
|
||||||
|
self._bit_field_set(register.into(), mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _bit_field_set(&mut self, register: Register, mask: u8) {
|
||||||
|
debug_assert!(register.is_eth_register());
|
||||||
|
|
||||||
|
self.change_bank(register);
|
||||||
|
|
||||||
|
self.spi
|
||||||
|
.write(&[Instruction::BFS.opcode() | register.addr(), mask])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_buffer_memory(&mut self, addr: Option<u16>, buf: &mut [u8]) {
|
||||||
|
if let Some(addr) = addr {
|
||||||
|
self.write_control_register(bank0::Register::ERDPTL, addr.low());
|
||||||
|
self.write_control_register(bank0::Register::ERDPTH, addr.high());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.spi
|
||||||
|
.transaction(&mut [Operation::Write(&[Instruction::RBM.opcode()]), Operation::Read(buf)])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn soft_reset(&mut self) {
|
||||||
|
self.spi.write(&[Instruction::SRC.opcode()]).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_buffer_memory(&mut self, addr: Option<u16>, buffer: &[u8]) {
|
||||||
|
if let Some(addr) = addr {
|
||||||
|
self.write_control_register(bank0::Register::EWRPTL, addr.low());
|
||||||
|
self.write_control_register(bank0::Register::EWRPTH, addr.high());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.spi
|
||||||
|
.transaction(&mut [Operation::Write(&[Instruction::WBM.opcode()]), Operation::Write(buffer)])
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
enum Bank {
|
||||||
|
Bank0,
|
||||||
|
Bank1,
|
||||||
|
Bank2,
|
||||||
|
Bank3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Instruction {
|
||||||
|
/// Read Control Register
|
||||||
|
RCR = 0b000_00000,
|
||||||
|
/// Read Buffer Memory
|
||||||
|
RBM = 0b001_11010,
|
||||||
|
/// Write Control Register
|
||||||
|
WCR = 0b010_00000,
|
||||||
|
/// Write Buffer Memory
|
||||||
|
WBM = 0b011_11010,
|
||||||
|
/// Bit Field Set
|
||||||
|
BFS = 0b100_00000,
|
||||||
|
/// Bit Field Clear
|
||||||
|
BFC = 0b101_00000,
|
||||||
|
/// System Reset Command
|
||||||
|
SRC = 0b111_11111,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
fn opcode(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
enum Register {
|
||||||
|
Bank0(bank0::Register),
|
||||||
|
Bank1(bank1::Register),
|
||||||
|
Bank2(bank2::Register),
|
||||||
|
Bank3(bank3::Register),
|
||||||
|
Common(common::Register),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
fn addr(&self) -> u8 {
|
||||||
|
match *self {
|
||||||
|
Register::Bank0(r) => r.addr(),
|
||||||
|
Register::Bank1(r) => r.addr(),
|
||||||
|
Register::Bank2(r) => r.addr(),
|
||||||
|
Register::Bank3(r) => r.addr(),
|
||||||
|
Register::Common(r) => r.addr(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bank(&self) -> Option<Bank> {
|
||||||
|
Some(match *self {
|
||||||
|
Register::Bank0(_) => Bank::Bank0,
|
||||||
|
Register::Bank1(_) => Bank::Bank1,
|
||||||
|
Register::Bank2(_) => Bank::Bank2,
|
||||||
|
Register::Bank3(_) => Bank::Bank3,
|
||||||
|
Register::Common(_) => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_eth_register(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Register::Bank0(r) => r.is_eth_register(),
|
||||||
|
Register::Bank1(r) => r.is_eth_register(),
|
||||||
|
Register::Bank2(r) => r.is_eth_register(),
|
||||||
|
Register::Bank3(r) => r.is_eth_register(),
|
||||||
|
Register::Common(r) => r.is_eth_register(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Packet type, used to configure receive filters
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum Packet {
|
||||||
|
/// Broadcast packets
|
||||||
|
Broadcast,
|
||||||
|
/// Multicast packets
|
||||||
|
Multicast,
|
||||||
|
/// Unicast packets
|
||||||
|
Unicast,
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut TX_BUF: [u8; MTU] = [0; MTU];
|
||||||
|
static mut RX_BUF: [u8; MTU] = [0; MTU];
|
||||||
|
|
||||||
|
impl<S, O> embassy_net_driver::Driver for Enc28j60<S, O>
|
||||||
|
where
|
||||||
|
S: SpiDevice,
|
||||||
|
O: OutputPin,
|
||||||
|
{
|
||||||
|
type RxToken<'a> = RxToken<'a>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
type TxToken<'a> = TxToken<'a, S, O>
|
||||||
|
where
|
||||||
|
Self: 'a;
|
||||||
|
|
||||||
|
fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
|
||||||
|
let rx_buf = unsafe { &mut RX_BUF };
|
||||||
|
let tx_buf = unsafe { &mut TX_BUF };
|
||||||
|
if let Some(pkt) = self.receive(rx_buf) {
|
||||||
|
let n = pkt.len();
|
||||||
|
Some((RxToken { buf: &mut pkt[..n] }, TxToken { buf: tx_buf, eth: self }))
|
||||||
|
} else {
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transmit(&mut self, _cx: &mut core::task::Context) -> Option<Self::TxToken<'_>> {
|
||||||
|
let tx_buf = unsafe { &mut TX_BUF };
|
||||||
|
Some(TxToken { buf: tx_buf, eth: self })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn link_state(&mut self, cx: &mut core::task::Context) -> LinkState {
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
match self.is_link_up() {
|
||||||
|
true => LinkState::Up,
|
||||||
|
false => LinkState::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capabilities(&self) -> Capabilities {
|
||||||
|
let mut caps = Capabilities::default();
|
||||||
|
caps.max_transmission_unit = MTU;
|
||||||
|
caps.medium = Medium::Ethernet;
|
||||||
|
caps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hardware_address(&self) -> HardwareAddress {
|
||||||
|
HardwareAddress::Ethernet(self.mac_addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// embassy-net RX token.
|
||||||
|
pub struct RxToken<'a> {
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> embassy_net_driver::RxToken for RxToken<'a> {
|
||||||
|
fn consume<R, F>(self, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut [u8]) -> R,
|
||||||
|
{
|
||||||
|
f(self.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// embassy-net TX token.
|
||||||
|
pub struct TxToken<'a, S, O>
|
||||||
|
where
|
||||||
|
S: SpiDevice,
|
||||||
|
O: OutputPin,
|
||||||
|
{
|
||||||
|
eth: &'a mut Enc28j60<S, O>,
|
||||||
|
buf: &'a mut [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S, O> embassy_net_driver::TxToken for TxToken<'a, S, O>
|
||||||
|
where
|
||||||
|
S: SpiDevice,
|
||||||
|
O: OutputPin,
|
||||||
|
{
|
||||||
|
fn consume<R, F>(self, len: usize, f: F) -> R
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut [u8]) -> R,
|
||||||
|
{
|
||||||
|
assert!(len <= self.buf.len());
|
||||||
|
let r = f(&mut self.buf[..len]);
|
||||||
|
self.eth.transmit(&self.buf[..len]);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
89
embassy-net-enc28j60/src/macros.rs
Normal file
89
embassy-net-enc28j60/src/macros.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
macro_rules! register {
|
||||||
|
($REGISTER:ident, $reset_value:expr, $uxx:ty, {
|
||||||
|
$(#[$($attr:tt)*] $bitfield:ident @ $range:expr,)+
|
||||||
|
}) => {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub(crate) struct $REGISTER<MODE> {
|
||||||
|
bits: $uxx,
|
||||||
|
_mode: ::core::marker::PhantomData<MODE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $REGISTER<super::traits::Mask> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn mask() -> $REGISTER<super::traits::Mask> {
|
||||||
|
$REGISTER { bits: 0, _mode: ::core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn $bitfield(&self) -> $uxx {
|
||||||
|
use super::traits::OffsetSize;
|
||||||
|
|
||||||
|
let size = $range.size();
|
||||||
|
let offset = $range.offset();
|
||||||
|
((1 << size) - 1) << offset
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::core::default::Default for $REGISTER<super::traits::W> {
|
||||||
|
fn default() -> Self {
|
||||||
|
$REGISTER { bits: $reset_value, _mode: ::core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn $REGISTER(bits: $uxx) -> $REGISTER<super::traits::R> {
|
||||||
|
$REGISTER { bits, _mode: ::core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $REGISTER<super::traits::R> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn modify(self) -> $REGISTER<super::traits::W> {
|
||||||
|
$REGISTER { bits: self.bits, _mode: ::core::marker::PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[$($attr)*]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn $bitfield(&self) -> $uxx {
|
||||||
|
use super::traits::OffsetSize;
|
||||||
|
|
||||||
|
let offset = $range.offset();
|
||||||
|
let size = $range.size();
|
||||||
|
let mask = (1 << size) - 1;
|
||||||
|
|
||||||
|
(self.bits >> offset) & mask
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $REGISTER<super::traits::W> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn bits(self) -> $uxx {
|
||||||
|
self.bits
|
||||||
|
}
|
||||||
|
|
||||||
|
$(
|
||||||
|
#[$($attr)*]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn $bitfield(&mut self, mut bits: $uxx) -> &mut Self {
|
||||||
|
use super::traits::OffsetSize;
|
||||||
|
|
||||||
|
let offset = $range.offset();
|
||||||
|
let size = $range.size();
|
||||||
|
let mask = (1 << size) - 1;
|
||||||
|
|
||||||
|
debug_assert!(bits <= mask);
|
||||||
|
bits &= mask;
|
||||||
|
|
||||||
|
self.bits &= !(mask << offset);
|
||||||
|
self.bits |= bits << offset;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
embassy-net-enc28j60/src/phy.rs
Normal file
35
embassy-net-enc28j60/src/phy.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum Register {
|
||||||
|
PHCON1 = 0x00,
|
||||||
|
PHSTAT1 = 0x01,
|
||||||
|
PHID1 = 0x02,
|
||||||
|
PHID2 = 0x03,
|
||||||
|
PHCON2 = 0x10,
|
||||||
|
PHSTAT2 = 0x11,
|
||||||
|
PHIE = 0x12,
|
||||||
|
PHIR = 0x13,
|
||||||
|
PHLCON = 0x14,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register {
|
||||||
|
pub(crate) fn addr(&self) -> u8 {
|
||||||
|
*self as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
register!(PHCON2, 0, u16, {
|
||||||
|
#[doc = "PHY Half-Duplex Loopback Disable bit"]
|
||||||
|
hdldis @ 8,
|
||||||
|
#[doc = "Jabber Correction Disable bit"]
|
||||||
|
jabber @ 10,
|
||||||
|
#[doc = "Twisted-Pair Transmitter Disable bit"]
|
||||||
|
txdis @ 13,
|
||||||
|
#[doc = "PHY Force Linkup bit"]
|
||||||
|
frclnk @ 14,
|
||||||
|
});
|
||||||
|
|
||||||
|
register!(PHSTAT2, 0, u16, {
|
||||||
|
#[doc = "Link Status bit"]
|
||||||
|
lstat @ 10,
|
||||||
|
});
|
57
embassy-net-enc28j60/src/traits.rs
Normal file
57
embassy-net-enc28j60/src/traits.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
|
pub(crate) trait OffsetSize {
|
||||||
|
fn offset(self) -> u8;
|
||||||
|
fn size(self) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffsetSize for u8 {
|
||||||
|
fn offset(self) -> u8 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(self) -> u8 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OffsetSize for Range<u8> {
|
||||||
|
fn offset(self) -> u8 {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(self) -> u8 {
|
||||||
|
self.end - self.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait U16Ext {
|
||||||
|
fn from_parts(low: u8, high: u8) -> Self;
|
||||||
|
|
||||||
|
fn low(self) -> u8;
|
||||||
|
|
||||||
|
fn high(self) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl U16Ext for u16 {
|
||||||
|
fn from_parts(low: u8, high: u8) -> u16 {
|
||||||
|
((high as u16) << 8) + low as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
fn low(self) -> u8 {
|
||||||
|
(self & 0xff) as u8
|
||||||
|
}
|
||||||
|
|
||||||
|
fn high(self) -> u8 {
|
||||||
|
(self >> 8) as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct Mask;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct R;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct W;
|
@ -7,14 +7,20 @@ edition = "2021"
|
|||||||
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.3", 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.1.0", path = "../embassy-net-driver-channel"}
|
||||||
|
|
||||||
embedded-hal = { version = "1.0.0-alpha.10" }
|
embedded-hal = { version = "1.0.0-rc.1" }
|
||||||
embedded-hal-async = { version = "=0.2.0-alpha.1" }
|
embedded-hal-async = { version = "=1.0.0-rc.1" }
|
||||||
|
|
||||||
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
noproto = { git="https://github.com/embassy-rs/noproto", default-features = false, features = ["derive"] }
|
||||||
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
#noproto = { version = "0.1", path = "/home/dirbaio/noproto", default-features = false, features = ["derive"] }
|
||||||
heapless = "0.7.16"
|
heapless = "0.7.16"
|
||||||
|
|
||||||
|
[package.metadata.embassy_docs]
|
||||||
|
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-esp-hosted-v$VERSION/embassy-net-esp-hosted/src/"
|
||||||
|
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-esp-hosted/src/"
|
||||||
|
target = "thumbv7em-none-eabi"
|
||||||
|
features = ["defmt"]
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use ch::driver::LinkState;
|
use ch::driver::LinkState;
|
||||||
use defmt::Debug2Format;
|
|
||||||
use embassy_net_driver_channel as ch;
|
use embassy_net_driver_channel as ch;
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
|
|
||||||
use crate::ioctl::Shared;
|
use crate::ioctl::Shared;
|
||||||
use crate::proto::{self, CtrlMsg};
|
use crate::proto::{self, CtrlMsg};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Error {
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub status: u32,
|
pub enum Error {
|
||||||
|
Failed(u32),
|
||||||
|
Timeout,
|
||||||
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Control<'a> {
|
pub struct Control<'a> {
|
||||||
@ -17,6 +19,8 @@ pub struct Control<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
enum WifiMode {
|
enum WifiMode {
|
||||||
None = 0,
|
None = 0,
|
||||||
Sta = 1,
|
Sta = 1,
|
||||||
@ -24,92 +28,124 @@ enum WifiMode {
|
|||||||
ApSta = 3,
|
ApSta = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use proto::CtrlWifiSecProt as Security;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct Status {
|
||||||
|
pub ssid: String<32>,
|
||||||
|
pub bssid: [u8; 6],
|
||||||
|
pub rssi: i32,
|
||||||
|
pub channel: u32,
|
||||||
|
pub security: Security,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ioctl {
|
||||||
|
($self:ident, $req_variant:ident, $resp_variant:ident, $req:ident, $resp:ident) => {
|
||||||
|
let mut msg = proto::CtrlMsg {
|
||||||
|
msg_id: proto::CtrlMsgId::$req_variant as _,
|
||||||
|
msg_type: proto::CtrlMsgType::Req as _,
|
||||||
|
payload: Some(proto::CtrlMsgPayload::$req_variant($req)),
|
||||||
|
};
|
||||||
|
$self.ioctl(&mut msg).await?;
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload
|
||||||
|
else {
|
||||||
|
warn!("unexpected response variant");
|
||||||
|
return Err(Error::Internal);
|
||||||
|
};
|
||||||
|
if $resp.resp != 0 {
|
||||||
|
return Err(Error::Failed($resp.resp));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Control<'a> {
|
impl<'a> Control<'a> {
|
||||||
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
|
pub(crate) fn new(state_ch: ch::StateRunner<'a>, shared: &'a Shared) -> Self {
|
||||||
Self { state_ch, shared }
|
Self { state_ch, shared }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn init(&mut self) {
|
pub async fn init(&mut self) -> Result<(), Error> {
|
||||||
debug!("wait for init event...");
|
debug!("wait for init event...");
|
||||||
self.shared.init_wait().await;
|
self.shared.init_wait().await;
|
||||||
|
|
||||||
debug!("set wifi mode");
|
debug!("set heartbeat");
|
||||||
self.set_wifi_mode(WifiMode::Sta as _).await;
|
self.set_heartbeat(10).await?;
|
||||||
|
|
||||||
let mac_addr = self.get_mac_addr().await;
|
debug!("set wifi mode");
|
||||||
|
self.set_wifi_mode(WifiMode::Sta as _).await?;
|
||||||
|
|
||||||
|
let mac_addr = self.get_mac_addr().await?;
|
||||||
debug!("mac addr: {:02x}", mac_addr);
|
debug!("mac addr: {:02x}", mac_addr);
|
||||||
self.state_ch.set_ethernet_address(mac_addr);
|
self.state_ch.set_ethernet_address(mac_addr);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn join(&mut self, ssid: &str, password: &str) {
|
pub async fn get_status(&mut self) -> Result<Status, Error> {
|
||||||
let req = proto::CtrlMsg {
|
let req = proto::CtrlMsgReqGetApConfig {};
|
||||||
msg_id: proto::CtrlMsgId::ReqConnectAp as _,
|
ioctl!(self, ReqGetApConfig, RespGetApConfig, req, resp);
|
||||||
msg_type: proto::CtrlMsgType::Req as _,
|
trim_nulls(&mut resp.ssid);
|
||||||
payload: Some(proto::CtrlMsgPayload::ReqConnectAp(proto::CtrlMsgReqConnectAp {
|
Ok(Status {
|
||||||
ssid: String::from(ssid),
|
ssid: resp.ssid,
|
||||||
pwd: String::from(password),
|
bssid: parse_mac(&resp.bssid)?,
|
||||||
bssid: String::new(),
|
rssi: resp.rssi as _,
|
||||||
listen_interval: 3,
|
channel: resp.chnl,
|
||||||
is_wpa3_supported: false,
|
security: resp.sec_prot,
|
||||||
})),
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn connect(&mut self, ssid: &str, password: &str) -> Result<(), Error> {
|
||||||
|
let req = proto::CtrlMsgReqConnectAp {
|
||||||
|
ssid: String::from(ssid),
|
||||||
|
pwd: String::from(password),
|
||||||
|
bssid: String::new(),
|
||||||
|
listen_interval: 3,
|
||||||
|
is_wpa3_supported: false,
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
ioctl!(self, ReqConnectAp, RespConnectAp, req, resp);
|
||||||
let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
|
||||||
debug!("======= {:?}", Debug2Format(&resp));
|
|
||||||
assert_eq!(resp.resp, 0);
|
|
||||||
self.state_ch.set_link_state(LinkState::Up);
|
self.state_ch.set_link_state(LinkState::Up);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_mac_addr(&mut self) -> [u8; 6] {
|
pub async fn disconnect(&mut self) -> Result<(), Error> {
|
||||||
let req = proto::CtrlMsg {
|
let req = proto::CtrlMsgReqGetStatus {};
|
||||||
msg_id: proto::CtrlMsgId::ReqGetMacAddress as _,
|
ioctl!(self, ReqDisconnectAp, RespDisconnectAp, req, resp);
|
||||||
msg_type: proto::CtrlMsgType::Req as _,
|
self.state_ch.set_link_state(LinkState::Down);
|
||||||
payload: Some(proto::CtrlMsgPayload::ReqGetMacAddress(
|
Ok(())
|
||||||
proto::CtrlMsgReqGetMacAddress {
|
}
|
||||||
mode: WifiMode::Sta as _,
|
|
||||||
},
|
/// duration in seconds, clamped to [10, 3600]
|
||||||
)),
|
async fn set_heartbeat(&mut self, duration: u32) -> Result<(), Error> {
|
||||||
|
let req = proto::CtrlMsgReqConfigHeartbeat { enable: true, duration };
|
||||||
|
ioctl!(self, ReqConfigHeartbeat, RespConfigHeartbeat, req, resp);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mac_addr(&mut self) -> Result<[u8; 6], Error> {
|
||||||
|
let req = proto::CtrlMsgReqGetMacAddress {
|
||||||
|
mode: WifiMode::Sta as _,
|
||||||
};
|
};
|
||||||
let resp = self.ioctl(req).await;
|
ioctl!(self, ReqGetMacAddress, RespGetMacAddress, req, resp);
|
||||||
let proto::CtrlMsgPayload::RespGetMacAddress(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
parse_mac(&resp.mac)
|
||||||
assert_eq!(resp.resp, 0);
|
|
||||||
|
|
||||||
// WHY IS THIS A STRING? WHYYYY
|
|
||||||
fn nibble_from_hex(b: u8) -> u8 {
|
|
||||||
match b {
|
|
||||||
b'0'..=b'9' => b - b'0',
|
|
||||||
b'a'..=b'f' => b + 0xa - b'a',
|
|
||||||
b'A'..=b'F' => b + 0xa - b'A',
|
|
||||||
_ => panic!("invalid hex digit {}", b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mac = resp.mac.as_bytes();
|
|
||||||
let mut res = [0; 6];
|
|
||||||
assert_eq!(mac.len(), 17);
|
|
||||||
for (i, b) in res.iter_mut().enumerate() {
|
|
||||||
*b = (nibble_from_hex(mac[i * 3]) << 4) | nibble_from_hex(mac[i * 3 + 1])
|
|
||||||
}
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_wifi_mode(&mut self, mode: u32) {
|
async fn set_wifi_mode(&mut self, mode: u32) -> Result<(), Error> {
|
||||||
let req = proto::CtrlMsg {
|
let req = proto::CtrlMsgReqSetMode { mode };
|
||||||
msg_id: proto::CtrlMsgId::ReqSetWifiMode as _,
|
ioctl!(self, ReqSetWifiMode, RespSetWifiMode, req, resp);
|
||||||
msg_type: proto::CtrlMsgType::Req as _,
|
|
||||||
payload: Some(proto::CtrlMsgPayload::ReqSetWifiMode(proto::CtrlMsgReqSetMode { mode })),
|
Ok(())
|
||||||
};
|
|
||||||
let resp = self.ioctl(req).await;
|
|
||||||
let proto::CtrlMsgPayload::RespSetWifiMode(resp) = resp.payload.unwrap() else { panic!("unexpected resp") };
|
|
||||||
assert_eq!(resp.resp, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn ioctl(&mut self, req: CtrlMsg) -> CtrlMsg {
|
async fn ioctl(&mut self, msg: &mut CtrlMsg) -> Result<(), Error> {
|
||||||
debug!("ioctl req: {:?}", &req);
|
debug!("ioctl req: {:?}", &msg);
|
||||||
|
|
||||||
let mut buf = [0u8; 128];
|
let mut buf = [0u8; 128];
|
||||||
|
|
||||||
let req_len = noproto::write(&req, &mut buf).unwrap();
|
let req_len = noproto::write(msg, &mut buf).map_err(|_| {
|
||||||
|
warn!("failed to serialize control request");
|
||||||
|
Error::Internal
|
||||||
|
})?;
|
||||||
|
|
||||||
struct CancelOnDrop<'a>(&'a Shared);
|
struct CancelOnDrop<'a>(&'a Shared);
|
||||||
|
|
||||||
@ -131,9 +167,44 @@ impl<'a> Control<'a> {
|
|||||||
|
|
||||||
ioctl.defuse();
|
ioctl.defuse();
|
||||||
|
|
||||||
let res = noproto::read(&buf[..resp_len]).unwrap();
|
*msg = noproto::read(&buf[..resp_len]).map_err(|_| {
|
||||||
debug!("ioctl resp: {:?}", &res);
|
warn!("failed to serialize control request");
|
||||||
|
Error::Internal
|
||||||
|
})?;
|
||||||
|
debug!("ioctl resp: {:?}", msg);
|
||||||
|
|
||||||
res
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WHY IS THIS A STRING? WHYYYY
|
||||||
|
fn parse_mac(mac: &str) -> Result<[u8; 6], Error> {
|
||||||
|
fn nibble_from_hex(b: u8) -> Result<u8, Error> {
|
||||||
|
match b {
|
||||||
|
b'0'..=b'9' => Ok(b - b'0'),
|
||||||
|
b'a'..=b'f' => Ok(b + 0xa - b'a'),
|
||||||
|
b'A'..=b'F' => Ok(b + 0xa - b'A'),
|
||||||
|
_ => {
|
||||||
|
warn!("invalid hex digit {}", b);
|
||||||
|
Err(Error::Internal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mac = mac.as_bytes();
|
||||||
|
let mut res = [0; 6];
|
||||||
|
if mac.len() != 17 {
|
||||||
|
warn!("unexpected MAC length");
|
||||||
|
return Err(Error::Internal);
|
||||||
|
}
|
||||||
|
for (i, b) in res.iter_mut().enumerate() {
|
||||||
|
*b = (nibble_from_hex(mac[i * 3])? << 4) | nibble_from_hex(mac[i * 3 + 1])?
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trim_nulls<const N: usize>(s: &mut String<N>) {
|
||||||
|
while s.chars().rev().next() == Some(0 as char) {
|
||||||
|
s.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ macro_rules! unreachable {
|
|||||||
#[cfg(feature = "defmt")]
|
#[cfg(feature = "defmt")]
|
||||||
macro_rules! unreachable {
|
macro_rules! unreachable {
|
||||||
($($x:tt)*) => {
|
($($x:tt)*) => {
|
||||||
::defmt::unreachable!($($x)*);
|
::defmt::unreachable!($($x)*)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,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 {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user