1
1
# !/usr/bin/perl -w
2
2
# vim:sw=4:ts=4:
3
3
#
4
- # Copyright (C) 2010, Nick Andrew <nick@nick-andrew.net>
4
+ # Copyright (C) 2010-2017 , Nick Andrew <nick@nick-andrew.net>
5
5
# Licensed under the terms of the GNU General Public License, Version 3
6
6
7
7
=head1 NAME
@@ -21,15 +21,16 @@ The frame structure is:
21
21
data (N bytes)
22
22
checksum (1 byte)
23
23
24
+ Packets longer than 255 bytes (by default) are ignored.
25
+
24
26
=head2 METHODS
25
27
26
28
=cut
27
29
28
30
package TullNet::XBee::API::Frame ;
29
31
30
32
use strict;
31
-
32
- my $DEBUG = 1;
33
+ use warnings;
33
34
34
35
35
36
=head2 I<new() >
@@ -42,8 +43,11 @@ sub new {
42
43
my ($class ) = @_ ;
43
44
44
45
my $self = {
46
+ debug => 0,
45
47
data => undef ,
48
+ leading_junk => ' ' ,
46
49
l_msb => undef ,
50
+ packet_max_length => 255,
47
51
to_read => undef ,
48
52
cksum => undef ,
49
53
state => 0,
@@ -67,27 +71,41 @@ If there's an error in the frame, call $self->checksumError().
67
71
sub addData {
68
72
my ($self , $buf ) = @_ ;
69
73
70
- my $start = chr (0x7e);
71
74
my $state = $self -> {' state' };
72
75
73
76
foreach my $c (split (// , $buf )) {
74
77
75
78
if ($state == 0) {
76
- if ($c eq $start ) {
79
+ if ($c eq chr (0x7e) ) {
77
80
$self -> {' data' } = undef ;
78
81
$self -> {' done' } = 0;
82
+ $self -> {' cksum' } = 0;
79
83
$state = 1;
84
+ if ($self -> {' leading_junk' } ne ' ' ) {
85
+ $self -> printHex(" Skipping junk pre frame start:" , $self -> {' leading_junk' });
86
+ }
87
+ $self -> {' leading_junk' } = ' ' ;
88
+ } else {
89
+ $self -> {' leading_junk' } .= $c ;
80
90
}
81
91
}
82
92
elsif ($state == 1) {
83
93
$self -> {' l_msb' } = ord ($c );
84
94
$state = 2;
85
95
}
86
96
elsif ($state == 2) {
87
- my $l_lsb = ord ($c );
88
- $self -> {' to_read' } = ($self -> {' l_msb' } << 8) + $l_lsb ;
89
- $self -> {' cksum' } = 0;
90
- $state = 3;
97
+ $self -> {' l_lsb' } = ord ($c );
98
+ my $length = ($self -> {' l_msb' } << 8) + $self -> {' l_lsb' };
99
+ if ($length > $self -> {' packet_max_length' }) {
100
+ # Don't allow arbitrarily long packets
101
+ $self -> error(" Long packet (length %d ) ignored, max is %d " ,
102
+ $length , $self -> {' packet_max_length' });
103
+ $state = 0;
104
+ } else {
105
+ $self -> {' length' } = $length ;
106
+ $self -> {' to_read' } = $length ;
107
+ $state = 3;
108
+ }
91
109
}
92
110
elsif ($state == 3) {
93
111
$self -> {' data' } .= $c ;
@@ -98,7 +116,9 @@ sub addData {
98
116
}
99
117
}
100
118
elsif ($state == 4) {
101
- $self -> {' cksum' } += ord ($c );
119
+ my $cksum_byte = ord ($c );
120
+ $self -> {' cksum_byte' } = $cksum_byte ;
121
+ $self -> {' cksum' } += $cksum_byte ;
102
122
103
123
if (($self -> {' cksum' } & 0xff) != 0xff) {
104
124
$self -> checksumError();
@@ -116,7 +136,7 @@ sub addData {
116
136
}
117
137
}
118
138
119
- # Remember state for next time
139
+ # Remember state within frame for next time
120
140
$self -> {' state' } = $state ;
121
141
122
142
return 1;
@@ -125,7 +145,7 @@ sub addData {
125
145
126
146
=head2 I<checksumError() >
127
147
128
- Called when an illegal frame has been detected.
148
+ Called when an invalid frame has been detected.
129
149
130
150
Override this in subclasses.
131
151
@@ -134,8 +154,14 @@ Override this in subclasses.
134
154
sub checksumError {
135
155
my ($self ) = @_ ;
136
156
137
- printf STDERR (" Checksum error: got %02x, expected 0xff\n " , $self -> {' cksum' });
138
- $self -> printHex(" Bad frame:" , $self -> {' data' });
157
+ my $err = sprintf (" Frame Checksum error: start=7e l_msb=%02x l_lsb=%02x (length %d ), cksum_byte=%02x, cksum=%02x (expected 0xff), data:" ,
158
+ $self -> {' l_msb' },
159
+ $self -> {' l_lsb' },
160
+ $self -> {' length' },
161
+ $self -> {' cksum_byte' },
162
+ $self -> {' cksum' },
163
+ );
164
+ $self -> printHex($err , $self -> {' data' });
139
165
}
140
166
141
167
@@ -175,9 +201,9 @@ sub serialise {
175
201
176
202
my $len = length ($buf );
177
203
178
- if ($len > 10000 ) {
204
+ if ($len > $self -> { ' packet_max_length ' } ) {
179
205
# Too long
180
- $@ = ' Packet too long' ;
206
+ $@ = sprintf ( ' Packet too long: length=%d, maximum=%d ' , $len , $self -> { ' packet_max_length ' }) ;
181
207
return undef ;
182
208
}
183
209
@@ -196,25 +222,54 @@ sub serialise {
196
222
}
197
223
198
224
199
- =head2 I<printHex($title, $string) >
225
+ =head2 I<debug($string, args...) >
226
+
227
+ If debugging is enabled, then printf supplied string and args to STDERR. A newline is appended.
228
+
229
+ =cut
230
+
231
+ sub debug {
232
+ my $self = shift ;
233
+
234
+ if ($self -> {' debug' }) {
235
+ printf STDERR (@_ );
236
+ print STDERR " \n " ;
237
+ }
238
+ }
239
+
240
+
241
+ =head2 I<error($string, args...) >
242
+
243
+ Printf supplied string to STDERR. A newline is appended.
244
+
245
+ =cut
246
+
247
+ sub error {
248
+ my $self = shift ;
249
+
250
+ printf STDERR (@_ );
251
+ print STDERR " \n " ;
252
+ }
253
+
254
+ =head2 I<printHex($title, $buf) >
200
255
201
- If debugging is enabled and a string is supplied,
202
- then print to STDOUT the title followed by the string in hex .
256
+ If a buffer is supplied, then print to STDERR the title followed
257
+ by the buffer contents in hex, then a newline .
203
258
204
259
=cut
205
260
206
261
sub printHex {
207
- my ($self , $heading , $s ) = @_ ;
262
+ my ($self , $title , $buf ) = @_ ;
208
263
209
- if ($DEBUG && defined ($s )) {
210
- my $str = $heading ;
264
+ if (defined ($buf )) {
265
+ my $str = $title ;
211
266
212
- my @chars = unpack (' C*' , $s );
267
+ my @chars = unpack (' C*' , $buf );
213
268
foreach my $i (@chars ) {
214
269
$str .= sprintf (" %02x" , $i );
215
270
}
216
271
217
- print STDERR " $str \n " ;
272
+ print STDERR $str , " \n " ;
218
273
}
219
274
}
220
275
0 commit comments