@@ -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,20 @@ 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 = MctpI2cHeader :: decode (
137+ & packet. get ( ..4 ) . ok_or ( Error :: InvalidInput ) ?,
138+ ) ?;
139+ // total packet len == byte_count + 3 (destination, command code, byte count)
140+ // pec is not included
141+ if header. byte_count + 3 != packet. len ( ) {
142+ trace ! ( "Packet byte count mismatch" ) ;
106143 return Err ( Error :: InvalidInput ) ;
107144 }
145+ if header. dest != self . own_addr {
146+ trace ! ( "I2C destination address mismatch" ) ;
147+ }
108148
109- Ok ( ( & packet[ MCTP_I2C_HEADER ..] , header. source ) )
149+ Ok ( ( & packet[ MCTP_I2C_HEADER ..] , header) )
110150 }
111151
112152 /// Handles a MCTP fragment with the PEC already validated
@@ -117,7 +157,7 @@ impl MctpI2cEncap {
117157 & self ,
118158 packet : & [ u8 ] ,
119159 mctp : & ' f mut Stack ,
120- ) -> Result < Option < ( MctpMessage < ' f > , u8 ) > > {
160+ ) -> Result < Option < ( MctpMessage < ' f > , MctpI2cHeader ) > > {
121161 let ( mctp_packet, i2c_src) = self . decode ( packet, false ) ?;
122162
123163 // Pass to MCTP stack
@@ -252,7 +292,7 @@ impl MctpI2cHandler {
252292 & mut self ,
253293 packet : & [ u8 ] ,
254294 mctp : & ' f mut Stack ,
255- ) -> Result < Option < ( MctpMessage < ' f > , u8 ) > > {
295+ ) -> Result < Option < ( MctpMessage < ' f > , MctpI2cHeader ) > > {
256296 self . encap . receive_done_pec ( packet, mctp)
257297 }
258298
@@ -369,3 +409,51 @@ enum HandlerSendState {
369409 i2c_dest : u8 ,
370410 } ,
371411}
412+
413+ #[ cfg( test) ]
414+ mod tests {
415+ use super :: * ;
416+
417+ #[ test]
418+ fn i2c_codec_roundtrip ( ) {
419+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
420+ const PACKET : & [ u8 ] =
421+ & [ 0x01 , 0x00 , 0x09 , 0xc8 , 0x00 , 0x8a , 0x01 , 0x00 , 0x0a ] ;
422+
423+ let mut buf = [ 0 ; 128 ] ;
424+ let i2c_packet = codec. encode ( 0x0B , & PACKET , & mut buf, false ) . unwrap ( ) ;
425+
426+ assert_eq ! ( & i2c_packet[ ..4 ] , [ 0x16 , 0x0f , 0x0a , 0x15 ] ) ;
427+
428+ let codec = MctpI2cEncap :: new ( 0x0B ) ;
429+ let ( decoded_packet, header) = codec. decode ( i2c_packet, false ) . unwrap ( ) ;
430+ assert_eq ! ( decoded_packet, PACKET ) ;
431+ assert_eq ! ( header. source, 0x0a ) ;
432+ assert_eq ! ( header. dest, 0x0b ) ;
433+ }
434+
435+ #[ test]
436+ fn test_partial_packet_decode ( ) {
437+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
438+
439+ // Test that empty packets are handled correctly
440+ let res = codec. decode ( & [ ] , false ) ;
441+ assert ! ( res. is_err( ) ) ;
442+ let res = codec. decode ( & [ ] , true ) ;
443+ assert ! ( res. is_err( ) ) ;
444+ // Test that packets with only partial header are handled correctly
445+ let res = codec. decode ( & [ 0x16 , 0x0f ] , false ) ;
446+ assert ! ( res. is_err( ) ) ;
447+ let res = codec. decode ( & [ 0x16 , 0x0f ] , true ) ;
448+ assert ! ( res. is_err( ) ) ;
449+ }
450+
451+ #[ test]
452+ fn test_decode_byte_count_mismatch ( ) {
453+ let codec = MctpI2cEncap :: new ( 0x0A ) ;
454+
455+ // Try to decode a packet with a `byte count` of 0x0a followed by only 3 bytes
456+ let res = codec. decode ( & [ 0x16 , 0x0f , 0x0a , 0x15 , 0x01 , 0x02 ] , false ) ;
457+ assert ! ( res. is_err_and( |e| matches!( e, Error :: InvalidInput ) ) ) ;
458+ }
459+ }
0 commit comments