/[RFID]/cpr-m02.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /cpr-m02.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 82 - (show annotations)
Fri Jul 9 23:10:05 2010 UTC (13 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 20125 byte(s)
first succeful command (detect boudrate) on CPR-M02

1 #!/usr/bin/perl
2
3 use Device::SerialPort qw (:STAT);
4 use strict;
5 use warnings;
6
7 use Data::Dump qw/dump/;
8 use Carp qw/confess/;
9 use Getopt::Long;
10 use File::Slurp;
11 use JSON;
12 use POSIX qw(strftime);
13
14 use IO::Socket::INET;
15
16 my $debug = 2;
17
18 my $tags_data;
19 my $tags_security;
20 my $visible_tags;
21
22 my $listen_port = 9000; # pick something not in use
23 my $server_url = "http://localhost:$listen_port";
24
25 sub http_server {
26
27 my $server = IO::Socket::INET->new(
28 Proto => 'tcp',
29 LocalPort => $listen_port,
30 Listen => SOMAXCONN,
31 Reuse => 1
32 );
33
34 die "can't setup server: $!" unless $server;
35
36 print "Server $0 ready at $server_url\n";
37
38 sub static {
39 my ($client,$path) = @_;
40
41 $path = "www/$path";
42 $path .= 'rfid.html' if $path =~ m{/$};
43
44 return unless -e $path;
45
46 my $type = 'text/plain';
47 $type = 'text/html' if $path =~ m{\.htm};
48 $type = 'application/javascript' if $path =~ m{\.js};
49
50 print $client "HTTP/1.0 200 OK\r\nContent-Type: $type\r\n\r\n";
51 open(my $html, $path);
52 while(<$html>) {
53 print $client $_;
54 }
55 close($html);
56
57 return $path;
58 }
59
60 while (my $client = $server->accept()) {
61 $client->autoflush(1);
62 my $request = <$client>;
63
64 warn "WEB << $request\n" if $debug;
65
66 if ($request =~ m{^GET (/.*) HTTP/1.[01]}) {
67 my $method = $1;
68 my $param;
69 if ( $method =~ s{\?(.+)}{} ) {
70 foreach my $p ( split(/[&;]/, $1) ) {
71 my ($n,$v) = split(/=/, $p, 2);
72 $param->{$n} = $v;
73 }
74 warn "WEB << param: ",dump( $param ) if $debug;
75 }
76 if ( my $path = static( $client,$1 ) ) {
77 warn "WEB >> $path" if $debug;
78 } elsif ( $method =~ m{/scan} ) {
79 my $tags = scan_for_tags();
80 my $json = { time => time() };
81 map {
82 my $d = decode_tag($_);
83 $d->{sid} = $_;
84 $d->{security} = $tags_security->{$_};
85 push @{ $json->{tags} }, $d;
86 } keys %$tags;
87 print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n",
88 $param->{callback}, "(", to_json($json), ")\r\n";
89 } elsif ( $method =~ m{/program} ) {
90
91 my $status = 501; # Not implementd
92
93 foreach my $p ( keys %$param ) {
94 next unless $p =~ m/^(E[0-9A-F]{15})$/;
95 my $tag = $1;
96 my $content = "\x04\x11\x00\x01" . $param->{$p};
97 $content = "\x00" if $param->{$p} eq 'blank';
98 $status = 302;
99
100 warn "PROGRAM $tag $content\n";
101 write_tag( $tag, $content );
102 secure_tag_with( $tag, $param->{$p} =~ /^130/ ? 'DA' : 'D7' );
103 }
104
105 print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
106
107 } elsif ( $method =~ m{/secure(.js)} ) {
108
109 my $json = $1;
110
111 my $status = 501; # Not implementd
112
113 foreach my $p ( keys %$param ) {
114 next unless $p =~ m/^(E[0-9A-F]{15})$/;
115 my $tag = $1;
116 my $data = $param->{$p};
117 $status = 302;
118
119 warn "SECURE $tag $data\n";
120 secure_tag_with( $tag, $data );
121 }
122
123 if ( $json ) {
124 print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n",
125 $param->{callback}, "({ ok: 1 })\r\n";
126 } else {
127 print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
128 }
129
130 } else {
131 print $client "HTTP/1.0 404 Unkown method\r\n\r\n";
132 }
133 } else {
134 print $client "HTTP/1.0 500 No method\r\n\r\n";
135 }
136 close $client;
137 }
138
139 die "server died";
140 }
141
142
143 my $last_message = {};
144 sub _message {
145 my $type = shift @_;
146 my $text = join(' ',@_);
147 my $last = $last_message->{$type};
148 if ( $text ne $last ) {
149 warn $type eq 'diag' ? '# ' : '', $text, "\n";
150 $last_message->{$type} = $text;
151 }
152 }
153
154 sub _log { _message('log',@_) };
155 sub diag { _message('diag',@_) };
156
157 my $device = "/dev/ttyUSB0";
158 my $baudrate = "38400";
159 my $databits = "8";
160 my $parity = "even";
161 my $stopbits = "1";
162 my $handshake = "none";
163
164 my $program_path = './program/';
165 my $secure_path = './secure/';
166
167 # http server
168 my $http_server = 1;
169
170 # 3M defaults: 8,4
171 # cards 16, stickers: 8
172 my $max_rfid_block = 8;
173 my $read_blocks = 8;
174
175 my $response = {
176 'd500090400110a0500027250' => 'version?',
177 'd60007fe00000500c97b' => 'no tag in range',
178
179 'd6000ffe00000501e00401003123aa26941a' => 'tag #1',
180 'd6000ffe00000501e0040100017c0c388e2b' => 'rfid card',
181 'd6000ffe00000501e00401003123aa2875d4' => 'tag red-stripe',
182
183 'd60017fe00000502e00401003123aa26e0040100017c0c38cadb' => 'tag #1 + card',
184 'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',
185 };
186
187 GetOptions(
188 'd|debug+' => \$debug,
189 'device=s' => \$device,
190 'baudrate=i' => \$baudrate,
191 'databits=i' => \$databits,
192 'parity=s' => \$parity,
193 'stopbits=i' => \$stopbits,
194 'handshake=s' => \$handshake,
195 'http-server!' => \$http_server,
196 ) or die $!;
197
198 my $verbose = $debug > 0 ? $debug-- : 0;
199
200 =head1 NAME
201
202 3m-810 - support for 3M 810 RFID reader
203
204 =head1 SYNOPSIS
205
206 3m-810.pl --device /dev/ttyUSB0
207
208 =head1 DESCRIPTION
209
210 Communicate with 3M 810 RFID reader and document it's protocol
211
212 =head1 SEE ALSO
213
214 L<Device::SerialPort(3)>
215
216 L<perl(1)>
217
218 L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
219
220 =head1 AUTHOR
221
222 Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
223
224 =head1 COPYRIGHT AND LICENSE
225
226 This program is free software; you may redistribute it and/or modify
227 it under the same terms ans Perl itself.
228
229 =cut
230
231 my $item_type = {
232 1 => 'Book',
233 6 => 'CD/CD ROM',
234 2 => 'Magazine',
235 13 => 'Book with Audio Tape',
236 9 => 'Book with CD/CD ROM',
237 0 => 'Other',
238
239 5 => 'Video',
240 4 => 'Audio Tape',
241 3 => 'Bound Journal',
242 8 => 'Book with Diskette',
243 7 => 'Diskette',
244 };
245
246 warn "## known item type: ",dump( $item_type ) if $debug;
247
248 my $port=new Device::SerialPort($device) || die "can't open serial port $device: $!\n";
249 warn "using $device $handshake $baudrate $databits $parity $stopbits" if $debug;
250 $handshake=$port->handshake($handshake);
251 $baudrate=$port->baudrate($baudrate);
252 $databits=$port->databits($databits);
253 $parity=$port->parity($parity);
254 $stopbits=$port->stopbits($stopbits);
255
256 warn "## using $device $baudrate $databits $parity $stopbits debug: $debug verbose: $verbose\n";
257
258 # Just in case: reset our timing and buffers
259 $port->lookclear();
260 $port->read_const_time(100);
261 $port->read_char_time(5);
262
263 # Turn on parity checking:
264 #$port->stty_inpck(1);
265 #$port->stty_istrip(1);
266
267 sub cpr_m02_checksum {
268 my $data = shift;
269
270 my $preset = 0xffff;
271 my $polynom = 0x8408;
272
273 my $crc = $preset;
274 foreach my $i ( 0 .. length($data) - 1 ) {
275 $crc ^= ord(substr($data,$i,1));
276 for my $j ( 0 .. 7 ) {
277 if ( $crc & 0x0001 ) {
278 $crc = ( $crc >> 1 ) ^ $polynom;
279 } else {
280 $crc = $crc >> 1;
281 }
282 }
283 warn sprintf('%d %04x', $i, $crc & 0xffff);
284 }
285
286 return pack('v', $crc);
287 }
288
289 sub cpr {
290 my $hex = shift;
291 my $bytes = str2bytes($hex);
292 my $len = pack( 'c', length( $bytes ) + 3 );
293 my $send = $len . $bytes;
294 my $checksum = cpr_m02_checksum($send);
295 $send .= $checksum;
296
297 warn ">> ", as_hex( $send );
298 writechunk( $send );
299 my $r_len = read_bytes( 1, 'response length' );
300 $r_len = ord($r_len) - 1;
301 my $data = read_bytes( $r_len, 'data' );
302 warn "<< ", as_hex( $data );
303 }
304
305 cpr( '00 52 00' );
306
307 exit;
308 # initial hand-shake with device
309
310 cmd( 'D5 00 05 04 00 11 8C66', 'hw version',
311 'D5 00 09 04 00 11 0A 05 00 02 7250', sub {
312 my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
313 print "hardware version $hw_ver\n";
314 });
315
316 cmd( 'D6 00 0C 13 04 01 00 02 00 03 00 04 00 AAF2','FIXME: stats?',
317 'D6 00 0C 13 00 02 01 01 03 02 02 03 00 E778', sub { assert() } );
318
319 sub scan_for_tags {
320
321 my @tags;
322
323 cmd( 'D6 00 05 FE 00 05 FA40', "scan for tags",
324 'D6 00 0F FE 00 00 05 ', sub { # 01 E00401003123AA26 941A # seen, serial length: 8
325 my $rest = shift || die "no rest?";
326 my $nr = ord( substr( $rest, 0, 1 ) );
327
328 if ( ! $nr ) {
329 _log "no tags in range\n";
330 update_visible_tags();
331 $tags_data = {};
332 } else {
333
334 my $tags = substr( $rest, 1 );
335 my $tl = length( $tags );
336 die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
337
338 push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
339 warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
340 _log "$nr tags in range: ", join(',', @tags ) , "\n";
341
342 update_visible_tags( @tags );
343 }
344 }
345 );
346
347 diag "tags: ",dump( @tags );
348 return $tags_data;
349
350 }
351
352 # start scanning for tags
353
354 if ( $http_server ) {
355 http_server;
356 } else {
357 while (1) {
358 scan_for_tags;
359 sleep 1;
360 }
361 }
362
363 die "over and out";
364
365 sub update_visible_tags {
366 my @tags = @_;
367
368 my $last_visible_tags = $visible_tags;
369 $visible_tags = {};
370
371 foreach my $tag ( @tags ) {
372 $visible_tags->{$tag}++;
373 if ( ! defined $last_visible_tags->{$tag} ) {
374 if ( defined $tags_data->{$tag} ) {
375 warn "$tag in range\n";
376 } else {
377 read_tag( $tag );
378 }
379 } else {
380 warn "## using cached data for $tag" if $debug;
381 }
382 delete $last_visible_tags->{$tag}; # leave just missing tags
383
384 if ( -e "$program_path/$tag" ) {
385 write_tag( $tag );
386 }
387 if ( -e "$secure_path/$tag" ) {
388 secure_tag( $tag );
389 }
390 }
391
392 foreach my $tag ( keys %$last_visible_tags ) {
393 my $data = delete $tags_data->{$tag};
394 warn "$tag removed ", dump($data), $/;
395 }
396
397 warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
398 }
399
400 my $tag_data_block;
401
402 sub read_tag_data {
403 my ($start_block,$rest) = @_;
404 die "no rest?" unless $rest;
405
406 my $last_block = 0;
407
408 warn "## DATA [$start_block] ", dump( $rest ) if $debug;
409 my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
410 my $blocks = ord(substr($rest,8,1));
411 $rest = substr($rest,9); # leave just data blocks
412 foreach my $nr ( 0 .. $blocks - 1 ) {
413 my $block = substr( $rest, $nr * 6, 6 );
414 warn "## block ",as_hex( $block ) if $debug;
415 my $ord = unpack('v',substr( $block, 0, 2 ));
416 my $expected_ord = $nr + $start_block;
417 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
418 my $data = substr( $block, 2 );
419 die "data payload should be 4 bytes" if length($data) != 4;
420 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
421 $tag_data_block->{$tag}->[ $ord ] = $data;
422 $last_block = $ord;
423 }
424 $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
425
426 my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
427 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
428
429 return $last_block + 1;
430 }
431
432 my $saved_in_log;
433
434 sub decode_tag {
435 my $tag = shift;
436
437 my $data = $tags_data->{$tag};
438 if ( ! $data ) {
439 warn "no data for $tag\n";
440 return;
441 }
442
443 my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
444 my $hash = {
445 u1 => $u1,
446 u2 => $u2,
447 set => ( $set_item & 0xf0 ) >> 4,
448 total => ( $set_item & 0x0f ),
449
450 type => $type,
451 content => $content,
452
453 branch => $br_lib >> 20,
454 library => $br_lib & 0x000fffff,
455
456 custom => $custom,
457 };
458
459 if ( ! $saved_in_log->{$tag}++ ) {
460 open(my $log, '>>', 'rfid-log.txt');
461 print $log strftime( "%Y-%m-%d %H:%M:%S", localtime ), ",$tag,$content\n";
462 close($log);
463 }
464
465 return $hash;
466 }
467
468 sub forget_tag {
469 my $tag = shift;
470 delete $tags_data->{$tag};
471 delete $visible_tags->{$tag};
472 }
473
474 sub read_tag {
475 my ( $tag ) = @_;
476
477 confess "no tag?" unless $tag;
478
479 print "read_tag $tag\n";
480
481 my $start_block = 0;
482
483 while ( $start_block < $max_rfid_block ) {
484
485 cmd(
486 sprintf( "D6 00 0D 02 $tag %02x %02x BEEF", $start_block, $read_blocks ),
487 "read $tag offset: $start_block blocks: $read_blocks",
488 "D6 00 1F 02 00", sub { # $tag 03 00 00 04 11 00 01 01 00 31 32 33 34 02 00 35 36 37 38 531F\n";
489 $start_block = read_tag_data( $start_block, @_ );
490 warn "# read tag upto $start_block\n";
491 },
492 "D6 00 0F FE 00 00 05 01 $tag BEEF", sub {
493 print "FIXME: tag $tag ready? (expected block read instead)\n";
494 },
495 "D6 00 0D 02 06 $tag", sub {
496 my $rest = shift;
497 print "ERROR reading $tag ", as_hex($rest), $/;
498 forget_tag $tag;
499 $start_block = $max_rfid_block; # XXX break out of while
500 },
501 );
502
503 }
504
505 my $security;
506
507 cmd(
508 "D6 00 0B 0A $tag BEEF", "check security $tag",
509 "D6 00 0D 0A 00", sub {
510 my $rest = shift;
511 my $from_tag;
512 ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
513 die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
514 $security = as_hex( $security );
515 $tags_security->{$tag} = $security;
516 warn "# SECURITY $tag = $security\n";
517 },
518 "D6 00 0C 0A 06", sub {
519 my $rest = shift;
520 warn "ERROR reading security from $rest\n";
521 forget_tag $tag;
522 },
523 );
524
525 print "TAG $tag ", dump(decode_tag( $tag ));
526 }
527
528 sub write_tag {
529 my ($tag,$data) = @_;
530
531 my $path = "$program_path/$tag";
532 $data = read_file( $path ) if -e $path;
533
534 die "no data" unless $data;
535
536 my $hex_data;
537
538 if ( $data =~ s{^hex\s+}{} ) {
539 $hex_data = $data;
540 $hex_data =~ s{\s+}{}g;
541 } else {
542
543 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
544
545 my $max_len = $max_rfid_block * 4;
546
547 if ( length($data) > $max_len ) {
548 $data = substr($data,0,$max_len);
549 warn "strip content to $max_len bytes\n";
550 }
551
552 $hex_data = unpack('H*', $data);
553 }
554
555 my $len = length($hex_data) / 2;
556 # pad to block size
557 $hex_data .= '00' x ( 4 - $len % 4 );
558 my $blocks = sprintf('%02x', length($hex_data) / 4);
559
560 print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
561
562 cmd(
563 "d6 00 ff 04 $tag 00 $blocks 00 $hex_data BEEF", "write $tag",
564 "d6 00 0d 04 00 $tag $blocks BEEF", sub { assert() },
565 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
566
567 my $to = $path;
568 $to .= '.' . time();
569
570 rename $path, $to;
571 print ">> $to\n";
572
573 forget_tag $tag;
574 }
575
576 sub secure_tag_with {
577 my ( $tag, $data ) = @_;
578
579 cmd(
580 "d6 00 0c 09 $tag $data BEEF", "secure $tag -> $data",
581 "d6 00 0c 09 00 $tag BEEF", sub { assert() },
582 );
583
584 forget_tag $tag;
585 }
586
587 sub secure_tag {
588 my ($tag) = @_;
589
590 my $path = "$secure_path/$tag";
591 my $data = substr(read_file( $path ),0,2);
592
593 secure_tag_with( $tag, $data );
594
595 my $to = $path;
596 $to .= '.' . time();
597
598 rename $path, $to;
599 print ">> $to\n";
600 }
601
602 exit;
603
604 for ( 1 .. 3 ) {
605
606 # ++-->type 00-0a
607 # D6 00 2A 04 E00401003123AA26 00 07 00 04 11 00 01 31 31 31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1C D4
608 # D6 00 2A 04 E0 04 01 00 31 23 AA 26 00 07 00 04 11 00 06 32 32 32 32 32 32 32 32 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 32B7
609 # D6 00 2A 04 E0 04 01 00 31 23 AA 26 00 07 00 04 11 00 02 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 00 00 00 00 00 00 00 00 42 1F
610
611 cmd(' D6 00 2A 04 E00401003123AA26 00 07 00 04 11 00 01 30 30 30 30 30 30 30 30 30 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 8843', "write offset 0, block: 7 -- 0000000000 $_" );
612 warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
613
614 }
615 warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
616
617 cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
618
619 cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
620 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
621 cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
622 'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
623
624 cmd('D6 00 26 04 E00401003123AA26 00 06 00 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 A98B', 'blank offset: 0 blocks: 6',
625 'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
626
627 undef $port;
628 print "Port closed\n";
629
630 sub writechunk
631 {
632 my $str=shift;
633 my $count = $port->write($str);
634 my $len = length($str);
635 die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
636 print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
637 }
638
639 sub as_hex {
640 my @out;
641 foreach my $str ( @_ ) {
642 my $hex = uc unpack( 'H*', $str );
643 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
644 $hex =~ s/\s+$//;
645 push @out, $hex;
646 }
647 return join(' | ', @out);
648 }
649
650 sub read_bytes {
651 my ( $len, $desc ) = @_;
652 my $data = '';
653 while ( length( $data ) < $len ) {
654 my ( $c, $b ) = $port->read(1);
655 die "no bytes on port: $!" unless defined $b;
656 warn "## got $c bytes: ", as_hex($b), "\n";
657 $data .= $b;
658 }
659 $desc ||= '?';
660 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
661 return $data;
662 }
663
664 our $assert;
665
666 # my $rest = skip_assert( 3 );
667 sub skip_assert {
668 assert( 0, shift );
669 }
670
671 sub assert {
672 my ( $from, $to ) = @_;
673
674 $from ||= 0;
675 $to = length( $assert->{expect} ) if ! defined $to;
676
677 my $p = substr( $assert->{payload}, $from, $to );
678 my $e = substr( $assert->{expect}, $from, $to );
679 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
680
681 # return the rest
682 return substr( $assert->{payload}, $to );
683 }
684
685 use Digest::CRC;
686
687 sub crcccitt {
688 my $bytes = shift;
689 my $crc = Digest::CRC->new(
690 # midified CCITT to xor with 0xffff instead of 0x0000
691 width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
692 ) or die $!;
693 $crc->add( $bytes );
694 pack('n', $crc->digest);
695 }
696
697 # my $checksum = checksum( $bytes );
698 # my $checksum = checksum( $bytes, $original_checksum );
699 sub checksum {
700 my ( $bytes, $checksum ) = @_;
701
702 my $len = ord(substr($bytes,2,1));
703 my $len_real = length($bytes) - 1;
704
705 if ( $len_real != $len ) {
706 print "length wrong: $len_real != $len\n";
707 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
708 }
709
710 my $xor = crcccitt( substr($bytes,1) ); # skip D6
711 warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
712
713 if ( defined $checksum && $xor ne $checksum ) {
714 warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
715 return $bytes . $xor;
716 }
717 return $bytes . $checksum;
718 }
719
720 our $dispatch;
721
722 sub readchunk {
723 # sleep 1; # FIXME remove
724
725 # read header of packet
726 my $header = read_bytes( 2, 'header' );
727 my $length = read_bytes( 1, 'length' );
728 my $len = ord($length);
729 my $data = read_bytes( $len, 'data' );
730
731 my $payload = substr( $data, 0, -2 );
732 my $payload_len = length($data);
733 warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
734
735 my $checksum = substr( $data, -2, 2 );
736 checksum( $header . $length . $payload , $checksum );
737
738 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
739
740 $assert->{len} = $len;
741 $assert->{payload} = $payload;
742
743 my $full = $header . $length . $data; # full
744 # find longest match for incomming data
745 my ($to) = grep {
746 my $match = substr($payload,0,length($_));
747 m/^\Q$match\E/
748 } sort { length($a) <=> length($b) } keys %$dispatch;
749 warn "?? payload dispatch to ",dump( $payload, $dispatch, $to ) if $debug;
750
751 if ( defined $to ) {
752 my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
753 warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
754 $dispatch->{ $to }->( $rest );
755 } else {
756 die "NO DISPATCH for ",as_hex( $full ),"\n";
757 }
758
759 return $data;
760 }
761
762 sub str2bytes {
763 my $str = shift || confess "no str?";
764 my $b = $str;
765 $b =~ s/\s+//g;
766 $b =~ s/(..)/\\x$1/g;
767 $b = "\"$b\"";
768 my $bytes = eval $b;
769 die $@ if $@;
770 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
771 return $bytes;
772 }
773
774 sub cmd {
775 my $cmd = shift || confess "no cmd?";
776 my $cmd_desc = shift || confess "no description?";
777 my @expect = @_;
778
779 my $bytes = str2bytes( $cmd );
780
781 # fix checksum if needed
782 $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
783
784 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
785 $assert->{send} = $cmd;
786 writechunk( $bytes );
787
788 while ( @expect ) {
789 my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
790 my $coderef = shift @expect || confess "no coderef?";
791 confess "not coderef" unless ref $coderef eq 'CODE';
792
793 next if defined $dispatch->{ $pattern };
794
795 $dispatch->{ substr($pattern,3) } = $coderef;
796 warn "++ dispatch ", as_hex($pattern) ,dump( $dispatch ) if $debug;
797 }
798
799 readchunk;
800 }
801

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26