11// SPDX-FileCopyrightText: Copyright (c) 2017-2025 slowtec GmbH <post@slowtec.de>
22// SPDX-License-Identifier: MIT OR Apache-2.0
33
4- use std:: io:: { Cursor , Error , ErrorKind , Result } ;
4+ use std:: io;
55
6- use byteorder:: { BigEndian , ReadBytesExt as _} ;
6+ use byteorder:: { LittleEndian , ReadBytesExt as _} ;
77use smallvec:: SmallVec ;
88use tokio_util:: codec:: { Decoder , Encoder } ;
99
@@ -15,6 +15,8 @@ use crate::{
1515
1616use super :: { encode_request_pdu, request_pdu_size, RequestPdu } ;
1717
18+ const MODBUS_CRC : crc:: Crc < u16 > = crc:: Crc :: < u16 > :: new ( & crc:: CRC_16_MODBUS ) ;
19+
1820// [Modbus over Serial Line Specification and Implementation Guide V1.02](http://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf), page 13
1921// "The maximum size of a Modbus RTU frame is 256 bytes."
2022const MAX_FRAME_LEN : usize = 256 ;
@@ -39,7 +41,7 @@ impl FrameDecoder {
3941 & mut self ,
4042 buf : & mut BytesMut ,
4143 pdu_len : usize ,
42- ) -> Result < Option < ( SlaveId , Bytes ) > > {
44+ ) -> io :: Result < Option < ( SlaveId , Bytes ) > > {
4345 const CRC_BYTE_COUNT : usize = 2 ;
4446
4547 let adu_len = 1 + pdu_len;
@@ -53,9 +55,8 @@ impl FrameDecoder {
5355 let crc_buf = buf. split_to ( CRC_BYTE_COUNT ) ;
5456
5557 // Read trailing CRC and verify ADU
56- let crc_result = Cursor :: new ( & crc_buf)
57- . read_u16 :: < BigEndian > ( )
58- . and_then ( |crc| check_crc ( & adu_buf, crc) ) ;
58+ let crc_result =
59+ read_crc ( & mut io:: Cursor :: new ( & crc_buf) ) . and_then ( |crc| check_crc ( & adu_buf, crc) ) ;
5960
6061 if let Err ( err) = crc_result {
6162 // CRC is invalid - restore the input buffer
@@ -126,7 +127,7 @@ pub(crate) struct ServerCodec {
126127}
127128
128129#[ cfg( any( feature = "rtu-over-tcp-server" , feature = "rtu-server" ) ) ]
129- fn get_request_pdu_len ( adu_buf : & BytesMut ) -> Result < Option < usize > > {
130+ fn get_request_pdu_len ( adu_buf : & BytesMut ) -> io :: Result < Option < usize > > {
130131 if let Some ( fn_code) = adu_buf. get ( 1 ) {
131132 let len = match fn_code {
132133 0x01 ..=0x06 => 5 ,
@@ -150,8 +151,8 @@ fn get_request_pdu_len(adu_buf: &BytesMut) -> Result<Option<usize>> {
150151 4
151152 }
152153 _ => {
153- return Err ( Error :: new (
154- ErrorKind :: InvalidData ,
154+ return Err ( io :: Error :: new (
155+ io :: ErrorKind :: InvalidData ,
155156 format ! ( "Invalid function code: 0x{fn_code:0>2X}" ) ,
156157 ) ) ;
157158 }
@@ -162,7 +163,7 @@ fn get_request_pdu_len(adu_buf: &BytesMut) -> Result<Option<usize>> {
162163 }
163164}
164165
165- fn get_response_pdu_len ( adu_buf : & BytesMut ) -> Result < Option < usize > > {
166+ fn get_response_pdu_len ( adu_buf : & BytesMut ) -> io :: Result < Option < usize > > {
166167 if let Some ( fn_code) = adu_buf. get ( 1 ) {
167168 #[ allow( clippy:: match_same_arms) ]
168169 let len = match fn_code {
@@ -215,8 +216,8 @@ fn get_response_pdu_len(adu_buf: &BytesMut) -> Result<Option<usize>> {
215216 offset - 1 // remove slave address byte
216217 }
217218 _ => {
218- return Err ( Error :: new (
219- ErrorKind :: InvalidData ,
219+ return Err ( io :: Error :: new (
220+ io :: ErrorKind :: InvalidData ,
220221 format ! ( "Invalid function code: 0x{fn_code:0>2X}" ) ,
221222 ) ) ;
222223 }
@@ -228,50 +229,45 @@ fn get_response_pdu_len(adu_buf: &BytesMut) -> Result<Option<usize>> {
228229}
229230
230231fn calc_crc ( data : & [ u8 ] ) -> u16 {
231- let mut crc = 0xFFFF ;
232- for x in data {
233- crc ^= u16:: from ( * x) ;
234- for _ in 0 ..8 {
235- let crc_odd = ( crc & 0x0001 ) != 0 ;
236- crc >>= 1 ;
237- if crc_odd {
238- crc ^= 0xA001 ;
239- }
240- }
241- }
242- // In contrast to all other 16-bit data the CRC is stored in
243- // little-endian instead of big-endian byte order. We account for
244- // this oddity right here in the calculation and read/write all
245- // 16-bit values consistently in big-endian byte order.
246- crc. rotate_right ( 8 )
232+ MODBUS_CRC . checksum ( data)
247233}
248234
249- fn check_crc ( adu_data : & [ u8 ] , expected_crc : u16 ) -> Result < ( ) > {
235+ fn check_crc ( adu_data : & [ u8 ] , expected_crc : u16 ) -> io :: Result < ( ) > {
250236 let actual_crc = calc_crc ( adu_data) ;
251237 if expected_crc != actual_crc {
252- return Err ( Error :: new (
253- ErrorKind :: InvalidData ,
238+ return Err ( io :: Error :: new (
239+ io :: ErrorKind :: InvalidData ,
254240 format ! ( "Invalid CRC: expected = 0x{expected_crc:0>4X}, actual = 0x{actual_crc:0>4X}" ) ,
255241 ) ) ;
256242 }
257243 Ok ( ( ) )
258244}
259245
246+ fn write_crc ( buf : & mut BytesMut , crc : u16 ) {
247+ // The CRC is encoded with Little Endian byte order.
248+ buf. put_u16_le ( crc) ;
249+ }
250+
251+ fn read_crc ( read : & mut impl io:: Read ) -> io:: Result < u16 > {
252+ // The CRC is encoded with Little Endian byte order.
253+ read. read_u16 :: < LittleEndian > ( )
254+ }
255+
260256#[ cfg( any( feature = "rtu-over-tcp-server" , feature = "rtu-server" ) ) ]
261257impl Decoder for RequestDecoder {
262258 type Item = ( SlaveId , Bytes ) ;
263- type Error = Error ;
259+ type Error = io :: Error ;
264260
265- fn decode ( & mut self , buf : & mut BytesMut ) -> Result < Option < ( SlaveId , Bytes ) > > {
261+ fn decode ( & mut self , buf : & mut BytesMut ) -> io :: Result < Option < ( SlaveId , Bytes ) > > {
266262 decode ( "request" , & mut self . frame_decoder , get_request_pdu_len, buf)
267263 }
268264}
269265
270266impl Decoder for ResponseDecoder {
271267 type Item = ( SlaveId , Bytes ) ;
272- type Error = Error ;
268+ type Error = io :: Error ;
273269
274- fn decode ( & mut self , buf : & mut BytesMut ) -> Result < Option < ( SlaveId , Bytes ) > > {
270+ fn decode ( & mut self , buf : & mut BytesMut ) -> io :: Result < Option < ( SlaveId , Bytes ) > > {
275271 decode (
276272 "response" ,
277273 & mut self . frame_decoder ,
@@ -286,9 +282,9 @@ fn decode<F>(
286282 frame_decoder : & mut FrameDecoder ,
287283 get_pdu_len : F ,
288284 buf : & mut BytesMut ,
289- ) -> Result < Option < ( SlaveId , Bytes ) > >
285+ ) -> io :: Result < Option < ( SlaveId , Bytes ) > >
290286where
291- F : Fn ( & BytesMut ) -> Result < Option < usize > > ,
287+ F : Fn ( & BytesMut ) -> io :: Result < Option < usize > > ,
292288{
293289 const MAX_RETRIES : usize = 20 ;
294290
@@ -313,14 +309,17 @@ where
313309
314310 // Maximum number of retries exceeded.
315311 log:: error!( "Giving up to decode frame after {MAX_RETRIES} retries" ) ;
316- Err ( Error :: new ( ErrorKind :: InvalidData , "Too many retries" ) )
312+ Err ( io:: Error :: new (
313+ io:: ErrorKind :: InvalidData ,
314+ "Too many retries" ,
315+ ) )
317316}
318317
319318impl Decoder for ClientCodec {
320319 type Item = ResponseAdu ;
321- type Error = Error ;
320+ type Error = io :: Error ;
322321
323- fn decode ( & mut self , buf : & mut BytesMut ) -> Result < Option < ResponseAdu > > {
322+ fn decode ( & mut self , buf : & mut BytesMut ) -> io :: Result < Option < ResponseAdu > > {
324323 let Some ( ( slave_id, pdu_data) ) = self . decoder . decode ( buf) ? else {
325324 return Ok ( None ) ;
326325 } ;
@@ -343,9 +342,9 @@ impl Decoder for ClientCodec {
343342#[ cfg( any( feature = "rtu-over-tcp-server" , feature = "rtu-server" ) ) ]
344343impl Decoder for ServerCodec {
345344 type Item = RequestAdu < ' static > ;
346- type Error = Error ;
345+ type Error = io :: Error ;
347346
348- fn decode ( & mut self , buf : & mut BytesMut ) -> Result < Option < RequestAdu < ' static > > > {
347+ fn decode ( & mut self , buf : & mut BytesMut ) -> io :: Result < Option < RequestAdu < ' static > > > {
349348 let Some ( ( slave_id, pdu_data) ) = self . decoder . decode ( buf) ? else {
350349 return Ok ( None ) ;
351350 } ;
@@ -366,9 +365,9 @@ impl Decoder for ServerCodec {
366365}
367366
368367impl < ' a > Encoder < RequestAdu < ' a > > for ClientCodec {
369- type Error = Error ;
368+ type Error = io :: Error ;
370369
371- fn encode ( & mut self , adu : RequestAdu < ' a > , buf : & mut BytesMut ) -> Result < ( ) > {
370+ fn encode ( & mut self , adu : RequestAdu < ' a > , buf : & mut BytesMut ) -> io :: Result < ( ) > {
372371 let RequestAdu {
373372 hdr,
374373 pdu : RequestPdu ( request) ,
@@ -379,16 +378,16 @@ impl<'a> Encoder<RequestAdu<'a>> for ClientCodec {
379378 buf. put_u8 ( hdr. slave_id ) ;
380379 encode_request_pdu ( buf, & request) ;
381380 let crc = calc_crc ( & buf[ buf_offset..] ) ;
382- buf . put_u16 ( crc) ;
381+ write_crc ( buf , crc) ;
383382 Ok ( ( ) )
384383 }
385384}
386385
387386#[ cfg( any( feature = "rtu-over-tcp-server" , feature = "rtu-server" ) ) ]
388387impl Encoder < ResponseAdu > for ServerCodec {
389- type Error = Error ;
388+ type Error = io :: Error ;
390389
391- fn encode ( & mut self , adu : ResponseAdu , buf : & mut BytesMut ) -> Result < ( ) > {
390+ fn encode ( & mut self , adu : ResponseAdu , buf : & mut BytesMut ) -> io :: Result < ( ) > {
392391 let ResponseAdu {
393392 hdr,
394393 pdu : super :: ResponsePdu ( pdu_res) ,
@@ -399,7 +398,7 @@ impl Encoder<ResponseAdu> for ServerCodec {
399398 buf. put_u8 ( hdr. slave_id ) ;
400399 super :: encode_response_result_pdu ( buf, & pdu_res) ;
401400 let crc = calc_crc ( & buf[ buf_offset..] ) ;
402- buf . put_u16 ( crc) ;
401+ write_crc ( buf , crc) ;
403402 Ok ( ( ) )
404403 }
405404}
@@ -411,11 +410,13 @@ mod tests {
411410
412411 #[ test]
413412 fn test_calc_crc ( ) {
413+ // See also: <https://crccalc.com/>
414+
414415 let msg = [ 0x01 , 0x03 , 0x08 , 0x2B , 0x00 , 0x02 ] ;
415- assert_eq ! ( calc_crc( & msg) , 0xB663 ) ;
416+ assert_eq ! ( calc_crc( & msg) , 0x63B6 ) ;
416417
417418 let msg = [ 0x01 , 0x03 , 0x04 , 0x00 , 0x20 , 0x00 , 0x00 ] ;
418- assert_eq ! ( calc_crc( & msg) , 0xFBF9 ) ;
419+ assert_eq ! ( calc_crc( & msg) , 0xF9FB ) ;
419420 }
420421
421422 #[ test]
0 commit comments