@@ -21,13 +21,31 @@ const MCTP_I2C_HEADER: usize = 4;
2121// bytecount is limited to u8, includes MCTP payload + 1 byte i2c source
2222pub const MCTP_I2C_MAXMTU : usize = u8:: MAX as usize - 1 ;
2323
24+ /// MCTP I2C encapsulation header
2425pub struct MctpI2cHeader {
26+ /// Destination address
27+ ///
28+ /// The 7-bit destination address of the encapsulated packet.
29+ /// (Not including the SMBus R/W# bit)
2530 pub dest : u8 ,
31+ /// Source address
32+ ///
33+ /// The 7-bit source address of the encapsulated packet.
34+ /// (Not including fixed bit `[0]`)
2635 pub source : u8 ,
36+ /// Byte count
37+ ///
38+ /// The count of bytes that follow the _Byte Count_ field up to,
39+ /// but not including, the PEC byte.
2740 pub byte_count : usize ,
2841}
2942
3043impl MctpI2cHeader {
44+ /// Encode this header
45+ ///
46+ /// Creates a 4-byte header with destination, command code, byte count, and source.
47+ /// Returns a [BadArgument](Error::BadArgument) error when the addresses are not 7-bit,
48+ /// or when the byte count is larger than [u8::MAX].
3149 fn encode ( & self ) -> Result < [ u8 ; 4 ] > {
3250 if self . dest > 0x7f || self . source > 0x7f {
3351 return Err ( Error :: BadArgument ) ;
@@ -43,9 +61,13 @@ impl MctpI2cHeader {
4361 ] )
4462 }
4563
46- fn decode ( pkt : & [ u8 ] ) -> Result < Self > {
64+ /// Decode a 4-byte I2C header
65+ ///
66+ /// Checks and decodes destination and source address,
67+ /// command code, and byte count.
68+ fn decode ( header : & [ u8 ] ) -> Result < Self > {
4769 let [ dest, cmd, byte_count, source] =
48- pkt . try_into ( ) . map_err ( |_| Error :: BadArgument ) ?;
70+ header . try_into ( ) . map_err ( |_| Error :: BadArgument ) ?;
4971 if dest & 1 != 0 {
5072 trace ! ( "Bad i2c dest write bit" ) ;
5173 return Err ( Error :: InvalidInput ) ;
@@ -59,8 +81,8 @@ impl MctpI2cHeader {
5981 return Err ( Error :: InvalidInput ) ;
6082 }
6183 Ok ( Self {
62- dest,
63- source,
84+ dest : dest >> 1 ,
85+ source : source >> 1 ,
6486 byte_count : byte_count as usize ,
6587 } )
6688 }
@@ -81,16 +103,27 @@ impl MctpI2cEncap {
81103 self . own_addr
82104 }
83105
106+ /// Decode a I2C encapsulated packet
107+ ///
108+ /// Decodes and verifies the I2C transport header.
109+ /// Optionally an included _PEC_ will be verified.
110+ ///
111+ /// The inner MCTP packet and the decoded header are returned on success.
112+ ///
113+ /// ### Errors when
114+ /// - The _PEC_ is incorrect
115+ /// - Header decoding fails
116+ /// - The decoded byte count field does not match the packet size
84117 pub fn decode < ' f > (
85118 & self ,
86119 mut packet : & ' f [ u8 ] ,
87120 pec : bool ,
88- ) -> Result < ( & ' f [ u8 ] , u8 ) > {
121+ ) -> Result < ( & ' f [ u8 ] , MctpI2cHeader ) > {
122+ if packet. is_empty ( ) {
123+ return Err ( Error :: InvalidInput ) ;
124+ }
89125 if pec {
90126 // Remove the pec byte, check it.
91- if packet. is_empty ( ) {
92- return Err ( Error :: InvalidInput ) ;
93- }
94127 let packet_pec;
95128 ( packet_pec, packet) = packet. split_last ( ) . unwrap ( ) ;
96129 let calc_pec = smbus_pec:: pec ( packet) ;
@@ -100,13 +133,19 @@ impl MctpI2cEncap {
100133 }
101134 }
102135
103- let header = MctpI2cHeader :: decode ( packet) ?;
104- // +1 for i2c source address field
105- if header. byte_count != packet. len ( ) + 1 {
136+ let header =
137+ MctpI2cHeader :: decode ( packet. get ( ..4 ) . ok_or ( Error :: InvalidInput ) ?) ?;
138+ // total packet len == byte_count + 3 (destination, command code, byte count)
139+ // pec is not included
140+ if header. byte_count + 3 != packet. len ( ) {
141+ trace ! ( "Packet byte count mismatch" ) ;
106142 return Err ( Error :: InvalidInput ) ;
107143 }
144+ if header. dest != self . own_addr {
145+ trace ! ( "I2C destination address mismatch" ) ;
146+ }
108147
109- Ok ( ( & packet[ MCTP_I2C_HEADER ..] , header. source ) )
148+ Ok ( ( & packet[ MCTP_I2C_HEADER ..] , header) )
110149 }
111150
112151 /// Handles a MCTP fragment with the PEC already validated
@@ -117,7 +156,7 @@ impl MctpI2cEncap {
117156 & self ,
118157 packet : & [ u8 ] ,
119158 mctp : & ' f mut Stack ,
120- ) -> Result < Option < ( MctpMessage < ' f > , u8 ) > > {
159+ ) -> Result < Option < ( MctpMessage < ' f > , MctpI2cHeader ) > > {
121160 let ( mctp_packet, i2c_src) = self . decode ( packet, false ) ?;
122161
123162 // Pass to MCTP stack
@@ -252,7 +291,7 @@ impl MctpI2cHandler {
252291 & mut self ,
253292 packet : & [ u8 ] ,
254293 mctp : & ' f mut Stack ,
255- ) -> Result < Option < ( MctpMessage < ' f > , u8 ) > > {
294+ ) -> Result < Option < ( MctpMessage < ' f > , MctpI2cHeader ) > > {
256295 self . encap . receive_done_pec ( packet, mctp)
257296 }
258297
@@ -369,3 +408,51 @@ enum HandlerSendState {
369408 i2c_dest : u8 ,
370409 } ,
371410}
411+
412+ #[ cfg( test) ]
413+ mod tests {
414+ use super :: * ;
415+
416+ #[ test]
417+ fn i2c_codec_roundtrip ( ) {
418+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
419+ const PACKET : & [ u8 ] =
420+ & [ 0x01 , 0x00 , 0x09 , 0xc8 , 0x00 , 0x8a , 0x01 , 0x00 , 0x0a ] ;
421+
422+ let mut buf = [ 0 ; 128 ] ;
423+ let i2c_packet = codec. encode ( 0x0B , PACKET , & mut buf, false ) . unwrap ( ) ;
424+
425+ assert_eq ! ( & i2c_packet[ ..4 ] , [ 0x16 , 0x0f , 0x0a , 0x15 ] ) ;
426+
427+ let codec = MctpI2cEncap :: new ( 0x0B ) ;
428+ let ( decoded_packet, header) = codec. decode ( i2c_packet, false ) . unwrap ( ) ;
429+ assert_eq ! ( decoded_packet, PACKET ) ;
430+ assert_eq ! ( header. source, 0x0a ) ;
431+ assert_eq ! ( header. dest, 0x0b ) ;
432+ }
433+
434+ #[ test]
435+ fn test_partial_packet_decode ( ) {
436+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
437+
438+ // Test that empty packets are handled correctly
439+ let res = codec. decode ( & [ ] , false ) ;
440+ assert ! ( res. is_err( ) ) ;
441+ let res = codec. decode ( & [ ] , true ) ;
442+ assert ! ( res. is_err( ) ) ;
443+ // Test that packets with only partial header are handled correctly
444+ let res = codec. decode ( & [ 0x16 , 0x0f ] , false ) ;
445+ assert ! ( res. is_err( ) ) ;
446+ let res = codec. decode ( & [ 0x16 , 0x0f ] , true ) ;
447+ assert ! ( res. is_err( ) ) ;
448+ }
449+
450+ #[ test]
451+ fn test_decode_byte_count_mismatch ( ) {
452+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
453+
454+ // Try to decode a packet with a `byte count` of 0x0a followed by only 3 bytes
455+ let res = codec. decode ( & [ 0x16 , 0x0f , 0x0a , 0x15 , 0x01 , 0x02 ] , false ) ;
456+ assert ! ( res. is_err_and( |e| matches!( e, Error :: InvalidInput ) ) ) ;
457+ }
458+ }
0 commit comments