-
Notifications
You must be signed in to change notification settings - Fork 0
/
message.rs
1500 lines (1337 loc) · 52.4 KB
/
message.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
// Copyright 2023 Oxide Computer Company
//! Message formats and definitions.
//!
use crate::mac::BadMacAddrRange;
use crate::mac::MacAddrs;
use crate::mgmt::ManagementInterface;
use crate::mgmt::MemoryRead;
use crate::mgmt::MemoryWrite;
use crate::ModuleId;
use core::fmt;
use hubpack::SerializedSize;
use serde::de::Error as SerdeError;
use serde::de::Unexpected;
use serde::de::Visitor;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
/// Definitions of message versions.
///
/// Both the inner and outer messages define minimum-supported and current
/// versions. The `MIN` version is the minimum guaranteed to be compatible with
/// the `CURRENT`. Here _compatible_ means that all messages from `MIN` can be
/// correctly deserialized by software running at least `MIN`. That means that
/// all message data in `MIN` correspond to the same values in `CURRENT` --
/// colloquially, `CURRENT` is a superset of `MIN`.
///
/// Because this crate uses `hubpack` for serialization, this means that no
/// variants of the message enums have been removed or reordered between `MIN`
/// and `CURRENT`. So `CURRENT` may contain _new_ items (or renamed ones), but
/// existing ones cannot be moved around relative to one another or removed.
///
/// This version of the protocol is _committed_. Any changes to the types here
/// **MUST** be compatible with `MIN`. Changes may add new enum variants; rename
/// variants; or rename struct fields; but they **MUST NOT** change the types of
/// those fields or reorder them. Peers **SHOULD**, on a best-effort basis,
/// decode and hande any messages that whose version is at least `MIN`. If the
/// message comes from a version prior to `CURRENT`, they must be able to decode
/// it, assuming we've not broken compatibility. If the message comes from a
/// version _after_ `CURRENT`, they _may_ be able to decode it. Specifically, if
/// they cannot, presumably because it came from a later version, they
/// **SHOULD** still send back a `ProtocolError` message, such as
/// `VersionMismatch` or `NotSupported`. Those are gauranteed to be compatible
/// with and decodable by the peer.
pub mod version {
pub mod inner {
pub const V1: u8 = 1;
pub const V2: u8 = 2;
/// Includes `ExtendedStatus` and `clear_disabled_latch`
pub const V3: u8 = 3;
/// Additions to HwError: NoFrontIo, FrontIoNotReady, NotPresent,
/// NotPowered, PowerFault, NotInitialized, I2cAddressNack, I2cByteNack
pub const V4: u8 = 4;
pub const CURRENT: u8 = V4;
pub const MIN: u8 = V1;
}
pub mod outer {
pub const V1: u8 = 1;
pub const MIN: u8 = V1;
pub const CURRENT: u8 = V1;
}
}
/// An error related to the actual messaging protocol.
///
/// This is an error reported in the outer message.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
#[cfg_attr(any(test, feature = "std"), derive(thiserror::Error))]
pub enum ProtocolError {
/// An unexpected message type was encountered.
#[cfg_attr(any(test, feature = "std"), error("Wrong message type"))]
WrongMessage,
/// The requested operation is not supported or unknown.
#[cfg_attr(any(test, feature = "std"), error("Unsupported or unknown operation"))]
NotSupported,
/// A request would result in a response that is too large to fit in a
/// single UDP message.
#[cfg_attr(
any(test, feature = "std"),
error("Request too large for single protocol message")
)]
RequestTooLarge,
/// The trailing data is an unexpected size.
#[cfg_attr(
any(test, feature = "std"),
error(
"Message trailing data has incorrect size: \
expected={expected} actual={actual}"
)
)]
WrongDataSize { expected: u32, actual: u32 },
/// The version in the header is unexpected.
#[cfg_attr(
any(test, feature = "std"),
error("Version mismatch: expected={expected}, actual={actual}")
)]
VersionMismatch { expected: u8, actual: u8 },
/// A failure to serialize or deserialize data.
#[cfg_attr(any(test, feature = "std"), error("Serialization failed"))]
Serialization,
}
/// An inner message error related to hardware accesses.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, SerializedSize)]
#[cfg_attr(any(test, feature = "std"), derive(thiserror::Error))]
pub enum HwError {
/// The FPGA reported an I2C error (deprecated)
///
/// This error has been deprecated in favor of the more explicit and
/// informative errors below (NotPresent, NotPowered, PowerFault,
/// NotInitialized, I2cAddressNack, I2cByteNack). It is being maintained
/// for backwards compatability.
#[cfg_attr(any(test, feature = "std"), error("FPGA reported an I2C error"))]
I2cError,
/// An invalid module index was provided.
///
/// Module indices are provided in a `u64` bitmask. However, current Sidecar
/// designs include only 32 switch ports. This is used to return errors in
/// the event that the host sets any of the upper 32 bits of that `u64`.
#[cfg_attr(any(test, feature = "std"), error("Invalid module index"))]
InvalidModuleIndex,
/// An error interacting with a board FPGA to operate on the transceivers.
#[cfg_attr(any(test, feature = "std"), error("Error interacting with FPGA"))]
FpgaError,
/// The module has been disabled by the SP due to a policy violation
#[cfg_attr(any(test, feature = "std"), error("Port is disabled by the SP"))]
DisabledBySp,
/// The Front IO board is not present
#[cfg_attr(any(test, feature = "std"), error("Front IO board not present"))]
NoFrontIo,
/// The Front IO board is present but not yet ready for communication
#[cfg_attr(any(test, feature = "std"), error("Front IO board not ready"))]
FrontIoNotReady,
/// The module is not present
///
/// ModPrsL is not asserted.
#[cfg_attr(any(test, feature = "std"), error("The module is not present"))]
NotPresent,
/// The module is not powered
///
/// A module is present but has not had power enabled.
#[cfg_attr(any(test, feature = "std"), error("The module is not powered"))]
NotPowered,
/// The module has experienced a power fault
///
/// This could mean the power supply did not come up in time after it was
/// enabled or the power good signal was deasserted during normal operation.
#[cfg_attr(any(test, feature = "std"), error("The module's power faulted"))]
PowerFault,
/// The module is not yet initialzed
///
/// It can take modules on the order of seconds to be ready for action after
/// they come out of reset (per SFF-8679, t_init is 2 seconds). We reject
/// interactions with modules that are not yet considered initialized.
#[cfg_attr(any(test, feature = "std"), error("The module is not initialized"))]
NotInitialized,
/// The module has nack'd the address byte of an I2C transaction
#[cfg_attr(
any(test, feature = "std"),
error("The module nack'd an I2C address byte")
)]
I2cAddressNack,
/// The module has nack'd a byte of an I2C transaction.
#[cfg_attr(any(test, feature = "std"), error("The module nack'd an I2C byte"))]
I2cByteNack,
}
/// Deserialize the [`HwError`]s from a packet buffer that are expected, given
/// the set of _failed_ modules described in `failed_modules`.
///
/// When issue certain requests to the SP, such as `HostRequest::Read`, the SP
/// responds with two `ModuleId`s: one for the successful modules and one for
/// the failed modules. The trailing data in a UDP packet, after the message
/// itself, contains all the data for the succesfully-read modules, followed by
/// one `HwError` for each failed module. This method can be used to decode
/// those errors.
///
/// Note that `buf` is expected to start at the first `HwError`. One can use
/// `SpResponse::expected_data_len()` and
/// `SpResponse::expected_error_data_len()` to determine the length of these
/// buffers.
#[cfg(any(feature = "std", test))]
pub fn deserialize_hw_errors(
failed_modules: ModuleId,
buf: &[u8],
) -> Result<Vec<HwError>, ProtocolError> {
match failed_modules.selected_transceiver_count() {
0 => Ok(vec![]),
n => {
const ITEM_SIZE: usize = HwError::MAX_SIZE;
let n_bytes = n * ITEM_SIZE;
if buf.len() < n_bytes {
return Err(ProtocolError::WrongDataSize {
expected: u32::try_from(n_bytes).unwrap(),
actual: u32::try_from(buf.len()).unwrap(),
});
}
let mut out = Vec::with_capacity(n);
let chunks = buf.chunks_exact(ITEM_SIZE);
for chunk in chunks {
out.push(
hubpack::deserialize(chunk)
.map_err(|_| ProtocolError::Serialization)?
.0,
);
}
Ok(out)
}
}
}
/// A common header to all messages between host and SP.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub struct Header {
// The outer message protocol version.
version: u8,
/// An arbitrary message ID, shared between a request and its response.
pub message_id: u64,
/// The expected contents of the remainder of the UDP packet.
///
/// This helps understand how to handle failures to parse the remaining
/// contents of the packet.
pub message_kind: MessageKind,
}
impl Header {
/// Create a new `Header` from an ID and message kind, at the current
/// version.
pub const fn new(message_id: u64, message_kind: MessageKind) -> Self {
Self {
version: version::outer::CURRENT,
message_id,
message_kind,
}
}
/// Return the outer protocol version in `self`.
pub const fn version(&self) -> u8 {
self.version
}
}
/// Description of the remaining contents of a message from the peer.
///
/// This is useful for determining how to proceed in the event of a failure to
/// deserialize the remaining bytes. For example, suppose the host has an
/// outstanding request to the SP, and receives an `SpResponse` message with the
/// ID of that request. If the message fails to deserialize, we'd probably like
/// to fail the request as a whole, rather than retrying it.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum MessageKind {
/// The message is expected to contain a [`MessageBody::Error`].
Error,
/// The message is expected to contain a [`MessageBody::HostRequest`].
HostRequest,
/// The message is expected to contain a [`MessageBody::SpResponse`].
SpResponse,
/// The message is expected to contain a [`MessageBody::SpRequest`].
SpRequest,
/// The message is expected to contain a [`MessageBody::HostResponse`].
HostResponse,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub struct Message {
version: u8,
pub body: MessageBody,
}
impl Message {
/// Construct a new message, at the current version, from a body.
pub fn new(body: MessageBody) -> Self {
Self {
version: version::inner::CURRENT,
body,
}
}
/// Return the inner protocol version number.
pub const fn version(&self) -> u8 {
self.version
}
/// Return the kind of message this includes.
pub const fn kind(&self) -> MessageKind {
match self.body {
MessageBody::Error(_) => MessageKind::Error,
MessageBody::HostRequest(_) => MessageKind::HostRequest,
MessageBody::SpResponse(_) => MessageKind::SpResponse,
MessageBody::SpRequest(_) => MessageKind::SpRequest,
MessageBody::HostResponse(_) => MessageKind::HostResponse,
}
}
}
/// The body of a message between host and SP.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum MessageBody {
/// An error message from the peer, indicating a failure of the protocol,
/// such as an unexpected message, failed deserialization, or version
/// mismatch.
Error(ProtocolError),
/// A request sent from host to SP.
HostRequest(HostRequest),
/// A response sent from SP to host.
///
/// The intended reply type for a [`HostRequest`].
SpResponse(SpResponse),
/// A request sent from SP to host.
SpRequest(SpRequest),
/// A response sent from host to SP.
///
/// The intended reply type for an [`SpRequest`].
HostResponse(HostResponse),
}
impl From<ProtocolError> for Message {
fn from(e: ProtocolError) -> Self {
Self::new(MessageBody::Error(e))
}
}
impl From<HostRequest> for Message {
fn from(r: HostRequest) -> Self {
Self::new(MessageBody::HostRequest(r))
}
}
impl From<SpResponse> for Message {
fn from(r: SpResponse) -> Self {
Self::new(MessageBody::SpResponse(r))
}
}
impl From<SpRequest> for Message {
fn from(r: SpRequest) -> Self {
Self::new(MessageBody::SpRequest(r))
}
}
impl From<HostResponse> for Message {
fn from(r: HostResponse) -> Self {
Self::new(MessageBody::HostResponse(r))
}
}
/// A request from the host to the SP.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum HostRequest {
/// Request to read a region of the transceivers' memory maps.
Read { modules: ModuleId, read: MemoryRead },
/// Request to write to a region of the transceiver's memory map.
///
/// The data to be written is contained in the remainder of the UDP packet.
/// This data should be a single byte array of the size specified by the
/// contained `MemoryWrite`, and will broadcast to all transceiver modules
/// addressed by the message.
Write {
modules: ModuleId,
write: MemoryWrite,
},
/// Request to return the status of the transceiver modules.
Status(ModuleId),
/// Request that the ResetL line be asserted.
AssertReset(ModuleId),
/// Request that the ResetL line be de-asserted.
DeassertReset(ModuleId),
/// Request that the LpMode line be asserted.
AssertLpMode(ModuleId),
/// Request that the LpMode line be de-asserted.
DeassertLpMode(ModuleId),
/// Request that power be enabled to a module should it be inserted.
EnablePower(ModuleId),
/// Request that power be disabled to a module should it be inserted.
DisablePower(ModuleId),
/// Assert type of management interface that a set of modules uses.
///
/// This is used to allow the SP to read and interpret parts of the
/// transceivers' memory maps such as the temperature or power draw, for the
/// purposes of health and safety monitoring.
ManagementInterface {
modules: ModuleId,
interface: ManagementInterface,
},
/// Ask the SP to return the available MAC addresses for host system use.
MacAddrs,
/// Request that a latched power fault be cleared.
///
/// When a power fault has occurred, the transceiver's power supply will not
/// re-enable as long as the fault is latched. Clearing the fault allows the
/// power supply to be enabled again.
ClearPowerFault(ModuleId),
/// Get the current state of the modules' attention LEDs.
LedState(ModuleId),
/// Set the state of the modules' attention LEDs.
SetLedState {
/// The modules whose state should be set.
modules: ModuleId,
/// The state to set each module's LED to.
state: LedState,
},
/// Request to return the extended status of the transceiver modules.
ExtendedStatus(ModuleId),
/// Clears the disabled latch for the given modules
ClearDisableLatch(ModuleId),
}
impl HostRequest {
/// Return the number of bytes expected to follow `self`.
///
/// If `None` is returned, the message expects no data at all.
pub fn expected_data_len(&self) -> Option<usize> {
match self {
HostRequest::Write { modules, write } => {
Some(modules.selected_transceiver_count() * usize::from(write.len()))
}
_ => None,
}
}
}
/// A response to a host request, sent from SP to host.
///
/// Most operations are fallible. As such, they report the set of modules on
/// which the operation succeed, and the set on which it failed. Such messages
/// may have trailing data, which indicates the successful data first, followed
/// by data indicating the failure.
///
/// For example, suppose the host sends a request to read data from two modules.
/// The first succeeds, and the second fails. The trailing data then contains a
/// byte array with the successfully-read data, followed by a byte array
/// encoding the `HwError` variant corresponding to the message. Note that for
/// many failures, each error may be a different variant.
///
/// The [`SpResponse::expected_data_len()`] and
/// [`SpResponse::expected_error_data_len()`] can be used to determine the
/// number of _bytes_ of expected success data or error data.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum SpResponse {
/// The result of a read operation.
///
/// The actual data read from the transceivers' memory is contained in the
/// remaining bytes of the UDP packet. Note that this may actually contain a
/// sequence of byte arrays, one for each of the addressed modules.
Read {
/// The modules that were successfully read from.
modules: ModuleId,
/// The modules on which the read failed.
failed_modules: ModuleId,
/// The read operation performed.
read: MemoryRead,
},
/// The result of a write operation.
Write {
/// The modules that were successfully written to.
modules: ModuleId,
/// The modules on which the write failed.
failed_modules: ModuleId,
/// The write operation performed.
write: MemoryWrite,
},
/// The status of a set of transceiver modules.
///
/// Each module may have a different set of status flags set. The
/// [`Message`] type contains the mask identifying all the modules, and the
/// remaining bytes of the UDP packet are a list of [`Status`] objects for
/// each module specified.
///
/// The ordering of the status objects is from lowest index to highest --
/// that is, taking the output of the returned `ModuleId::to_indices()`
/// method.
Status {
/// The modules whose status was successfully read.
modules: ModuleId,
/// The modules on which fetching the status failed.
failed_modules: ModuleId,
},
/// A generic acknowledgement of a specific message, where no further data
/// is required from the SP. Errors are included in the trailing packet
/// space.
Ack {
/// The modules which successfully performed the requested operation.
modules: ModuleId,
/// The modules which failed to perform the requested operation.
failed_modules: ModuleId,
},
/// The requested MAC address information for the host.
MacAddrs(MacAddrResponse),
/// The response contains the LED state for the addressed modules.
///
/// The trailing data of the message contains a list of [`LedState`]s,
/// ordered by the indices in the requested modules. Any errors are
/// serialized following the successful states.
LedState {
/// The module whose led state was successfully read.
modules: ModuleId,
/// The modules on which fetching the LED state failed.
failed_modules: ModuleId,
},
/// The extended status of a set of transceiver modules.
///
/// Each module may have a different set of status flags set. The
/// [`Message`] type contains the mask identifying all the modules, and the
/// remaining bytes of the UDP packet are a list of [`ExtendedStatus`] objects for
/// each module specified.
///
/// The ordering of the status objects is from lowest index to highest --
/// that is, taking the output of the returned `ModuleId::to_indices()`
/// method.
ExtendedStatus {
/// The modules whose status was successfully read.
modules: ModuleId,
/// The modules on which fetching the status failed.
failed_modules: ModuleId,
},
}
impl SpResponse {
/// Return the number of _data_ bytes expected to follow `self`.
///
/// If `None`, the message expects no data at all.
pub fn expected_data_len(&self) -> Option<usize> {
match self {
SpResponse::Read { modules, read, .. } => {
Some(modules.selected_transceiver_count() * usize::from(read.len()))
}
SpResponse::Status { modules, .. } => {
// NOTE: We don't `hubpack::deserialize` the `Status` objects,
// those are directly constructed using `Status::from_bits()`.
// So using `size_of` is appropriate here.
Some(modules.selected_transceiver_count() * core::mem::size_of::<Status>())
}
SpResponse::ExtendedStatus { modules, .. } => {
Some(modules.selected_transceiver_count() * ExtendedStatus::MAX_SIZE)
}
SpResponse::LedState { modules, .. } => {
Some(modules.selected_transceiver_count() * LedState::MAX_SIZE)
}
_ => None,
}
}
/// Return the number of _error_ bytes expected to follow `self`.
///
/// If `None`, the message expects no error data at all.
pub fn expected_error_data_len(&self) -> Option<usize> {
match self {
SpResponse::Read { failed_modules, .. }
| SpResponse::Write { failed_modules, .. }
| SpResponse::Status { failed_modules, .. }
| SpResponse::ExtendedStatus { failed_modules, .. }
| SpResponse::Ack { failed_modules, .. }
| SpResponse::LedState { failed_modules, .. } => {
Some(failed_modules.selected_transceiver_count() * HwError::MAX_SIZE)
}
_ => None,
}
}
}
/// A response to a request for MAC addresses.
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum MacAddrResponse {
/// The validated MAC address range for the host.
Ok(MacAddrs),
/// The MAC address range is invalid.
Error(BadMacAddrRange),
}
/// A request from the SP to the host.
//
// TODO-implement
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum SpRequest {}
/// A response to a SP request, sent from host to SP.
//
// TODO-implement
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize, SerializedSize)]
pub enum HostResponse {}
bitflags::bitflags! {
/// The status of a single transceiver module.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Status: u8 {
/// The module is present in the receptacle.
///
/// This translates to the `ModPrsL` pin in SFF-8679 rev 1.8 section
/// 5.3.4.
const PRESENT = 0b0000_0001;
/// The module's power is enabled.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const ENABLED = 0b0000_0010;
/// The module is held in reset.
///
/// This translates to the `ResetL` pin in SFF-8679 rev 1.8 section
/// 5.3.2.
const RESET = 0b0000_0100;
/// The module is held in low-power mode.
///
/// This translates to the `LpMode` pin in SFF-8679 rev 1.8 section
/// 5.3.3.
const LOW_POWER_MODE = 0b0000_1000;
/// At least one interrupt is signaled on the module. The details of the
/// interrupt cause can be queried by reading various bytes of the QSFP
/// memory map, such as bytes 3-21 in the lower memory page. See
/// SFF-8636 rev 2.10a section 6.2.3 for details.
///
/// This translates to the `IntL` pin in SFF-8679 rev 1.8 section
/// 5.3.5.
const INTERRUPT = 0b0001_0000;
/// This module's power supply has come up successfully.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const POWER_GOOD = 0b0010_0000;
/// This module's power supply has not come up after being enabled.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const FAULT_POWER_TIMEOUT = 0b0100_0000;
/// This module unexpectedly lost power.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const FAULT_POWER_LOST = 0b1000_0000;
}
}
impl fmt::Display for Status {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl core::str::FromStr for Status {
type Err = bitflags::parser::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
impl Serialize for Status {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u8(self.bits())
}
}
struct StatusVisitor;
impl<'de> Visitor<'de> for StatusVisitor {
type Value = Status;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a single u8")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: SerdeError,
{
u8::try_from(v)
.map(Status::from_bits_truncate)
.map_err(|_| SerdeError::invalid_value(Unexpected::Unsigned(v), &self))
}
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: SerdeError,
{
u8::try_from(v)
.map(Status::from_bits_truncate)
.map_err(|_| SerdeError::invalid_value(Unexpected::Unsigned(u64::from(v)), &self))
}
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
where
E: SerdeError,
{
u8::try_from(v)
.map(Status::from_bits_truncate)
.map_err(|_| SerdeError::invalid_value(Unexpected::Unsigned(u64::from(v)), &self))
}
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: SerdeError,
{
Status::from_bits(v).ok_or(SerdeError::invalid_value(
Unexpected::Unsigned(u64::from(v)),
&self,
))
}
}
impl<'de> Deserialize<'de> for Status {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u8(StatusVisitor)
}
}
impl SerializedSize for Status {
const MAX_SIZE: usize = core::mem::size_of::<Status>();
}
bitflags::bitflags! {
/// Module status, including bits set by Hubris
///
/// This is a superset of [`Status`]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct ExtendedStatus: u32 {
/// The module is present in the receptacle.
///
/// This translates to the `ModPrsL` pin in SFF-8679 rev 1.8 section
/// 5.3.4.
const PRESENT = 0b0000_0000_0001;
/// The module's power is enabled.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const ENABLED = 0b0000_0000_0010;
/// The module is held in reset.
///
/// This translates to the `ResetL` pin in SFF-8679 rev 1.8 section
/// 5.3.2.
const RESET = 0b0000_0000_0100;
/// The module is held in low-power mode.
///
/// This translates to the `ResetL` pin in SFF-8679 rev 1.8 section
/// 5.3.3.
const LOW_POWER_MODE = 0b0000_0000_1000;
/// At least one interrupt is signaled on the module. The details of the
/// interrupt cause can be queried by reading various bytes of the QSFP
/// memory map, such as bytes 3-21 in the lower memory page. See
/// SFF-8636 rev 2.10a section 6.2.3 for details.
///
/// This translates to the `IntL` pin in SFF-8679 rev 1.8 section
/// 5.3.5.
const INTERRUPT = 0b0000_0001_0000;
/// This module's power supply has come up successfully.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const POWER_GOOD = 0b0000_0010_0000;
/// This module's power supply has not come up after being enabled.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const FAULT_POWER_TIMEOUT = 0b0000_0100_0000;
/// This module unexpectedly lost power.
///
/// Note that this is not part of the QSFP specification, but provided
/// by the Sidecar board design itself.
const FAULT_POWER_LOST = 0b0000_1000_0000;
/// The SP has disabled the port associated with this module
///
/// For example, this could occur if the module was initialized but is
/// now NACKing I2C requests for its temperature.
const DISABLED_BY_SP = 0b1_0000_0000;
}
}
impl fmt::Display for ExtendedStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Status> for ExtendedStatus {
fn from(s: Status) -> Self {
// The requirement that Status is a subset of ExtendedStatus is checked
// in a unit test below (`test_extended_status`)
Self::from_bits(s.bits() as u32).expect("Status must be a subset of ExtendedStatus")
}
}
impl core::str::FromStr for ExtendedStatus {
type Err = bitflags::parser::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self)
}
}
impl Serialize for ExtendedStatus {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u32(self.bits())
}
}
struct ExtendedStatusVisitor;
impl<'de> Visitor<'de> for ExtendedStatusVisitor {
type Value = ExtendedStatus;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a single u32")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: SerdeError,
{
u32::try_from(v)
.map(ExtendedStatus::from_bits_truncate)
.map_err(|_| SerdeError::invalid_value(Unexpected::Unsigned(v), &self))
}
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: SerdeError,
{
ExtendedStatus::from_bits(v).ok_or(SerdeError::invalid_value(
Unexpected::Unsigned(u64::from(v)),
&self,
))
}
}
impl<'de> Deserialize<'de> for ExtendedStatus {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u32(ExtendedStatusVisitor)
}
}
impl SerializedSize for ExtendedStatus {
const MAX_SIZE: usize = core::mem::size_of::<ExtendedStatus>();
}
/// The state of a module's attention LED, on the Sidecar front IO panel.
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize, SerializedSize,
)]
#[cfg_attr(any(test, feature = "api-traits"), derive(schemars::JsonSchema))]
#[cfg_attr(any(test, feature = "api-traits"), serde(rename_all = "snake_case"))]
#[cfg_attr(any(test, feature = "std"), derive(clap::ValueEnum))]
pub enum LedState {
/// The LED is off.
///
/// This indicates that the port is disabled or not working at all.
Off,
/// The LED is solid on.
///
/// This indicates that the port is working as expected and enabled.
On,
/// The LED is blinking.
///
/// This is used to draw attention to the port, such as to indicate a fault
/// or to locate a port for servicing.
Blink,
}
impl fmt::Display for LedState {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
LedState::Off => "off",
LedState::On => "on",
LedState::Blink => "blink",
};
s.fmt(f)
}
}
#[cfg(test)]
mod test {
use crate::check_invalid_variants;
use crate::mac::BadMacAddrRange;
use crate::mac::BadMacAddrReason;
use crate::mac::MacAddrs;
use crate::message::deserialize_hw_errors;
use crate::message::ExtendedStatus;
use crate::message::Header;
use crate::message::HostRequest;
use crate::message::HwError;
use crate::message::LedState;
use crate::message::MacAddrResponse;
use crate::message::Message;
use crate::message::MessageBody;
use crate::message::MessageKind;
use crate::message::ProtocolError;
use crate::message::SpResponse;
use crate::message::Status;
use crate::mgmt::sff8636;
use crate::mgmt::ManagementInterface;
use crate::mgmt::MemoryRead;
use crate::mgmt::MemoryWrite;
use crate::ModuleId;
use core::mem::size_of;
use hubpack::SerializedSize;
#[test]
fn test_deserialize_led_state() {
assert_eq!(LedState::Off, serde_json::from_str("\"off\"").unwrap());
assert_eq!(LedState::On, serde_json::from_str("\"on\"").unwrap());
assert_eq!(LedState::Blink, serde_json::from_str("\"blink\"").unwrap());
}
#[test]
fn test_serialize_led_state() {
assert_eq!(serde_json::to_string(&LedState::Off).unwrap(), "\"off\"");
assert_eq!(serde_json::to_string(&LedState::On).unwrap(), "\"on\"");
assert_eq!(
serde_json::to_string(&LedState::Blink).unwrap(),
"\"blink\""
);
}
// Tests that the current version has not broken serialization.
//
// This uses the fact that `hubpack` assigns IDs to each variant of an enum
// based on the order they appear. These are used by
// `hubpack::{serialize,deserialize}` to determine the enum variant encoded
// in a binary message, which are based on the _ordering_ of the enum
// variants. I.e, we're really testing if that ordering has changed in a
// meaningful way.
//
// If it _has_, we have two options:
//
// - Bump `MIN` -> `CURRENT`
// - Rework the changes to avoid that change.