/[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

Annotation of /cpr-m02.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 82 - (hide 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 dpavlin 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 dpavlin 2 use Carp qw/confess/;
9 dpavlin 19 use Getopt::Long;
10 dpavlin 29 use File::Slurp;
11 dpavlin 44 use JSON;
12 dpavlin 59 use POSIX qw(strftime);
13 dpavlin 1
14 dpavlin 23 use IO::Socket::INET;
15    
16 dpavlin 82 my $debug = 2;
17 dpavlin 50
18 dpavlin 54 my $tags_data;
19     my $tags_security;
20     my $visible_tags;
21    
22 dpavlin 43 my $listen_port = 9000; # pick something not in use
23 dpavlin 59 my $server_url = "http://localhost:$listen_port";
24    
25 dpavlin 43 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 dpavlin 80 die "can't setup server: $!" unless $server;
35 dpavlin 43
36 dpavlin 59 print "Server $0 ready at $server_url\n";
37 dpavlin 43
38     sub static {
39     my ($client,$path) = @_;
40    
41     $path = "www/$path";
42 dpavlin 56 $path .= 'rfid.html' if $path =~ m{/$};
43 dpavlin 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 dpavlin 50 warn "WEB << $request\n" if $debug;
65 dpavlin 43
66     if ($request =~ m{^GET (/.*) HTTP/1.[01]}) {
67     my $method = $1;
68 dpavlin 46 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 dpavlin 50 warn "WEB << param: ",dump( $param ) if $debug;
75 dpavlin 46 }
76 dpavlin 43 if ( my $path = static( $client,$1 ) ) {
77 dpavlin 50 warn "WEB >> $path" if $debug;
78 dpavlin 43 } elsif ( $method =~ m{/scan} ) {
79     my $tags = scan_for_tags();
80 dpavlin 52 my $json = { time => time() };
81 dpavlin 44 map {
82     my $d = decode_tag($_);
83     $d->{sid} = $_;
84 dpavlin 54 $d->{security} = $tags_security->{$_};
85 dpavlin 44 push @{ $json->{tags} }, $d;
86     } keys %$tags;
87 dpavlin 71 print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n",
88 dpavlin 46 $param->{callback}, "(", to_json($json), ")\r\n";
89 dpavlin 59 } elsif ( $method =~ m{/program} ) {
90    
91     my $status = 501; # Not implementd
92    
93     foreach my $p ( keys %$param ) {
94 dpavlin 66 next unless $p =~ m/^(E[0-9A-F]{15})$/;
95 dpavlin 59 my $tag = $1;
96 dpavlin 61 my $content = "\x04\x11\x00\x01" . $param->{$p};
97 dpavlin 63 $content = "\x00" if $param->{$p} eq 'blank';
98 dpavlin 59 $status = 302;
99    
100     warn "PROGRAM $tag $content\n";
101     write_tag( $tag, $content );
102 dpavlin 68 secure_tag_with( $tag, $param->{$p} =~ /^130/ ? 'DA' : 'D7' );
103 dpavlin 59 }
104    
105     print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n";
106    
107 dpavlin 71 } elsif ( $method =~ m{/secure(.js)} ) {
108 dpavlin 67
109 dpavlin 71 my $json = $1;
110    
111 dpavlin 67 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 dpavlin 71 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 dpavlin 67
130 dpavlin 43 } else {
131 dpavlin 71 print $client "HTTP/1.0 404 Unkown method\r\n\r\n";
132 dpavlin 43 }
133     } else {
134 dpavlin 71 print $client "HTTP/1.0 500 No method\r\n\r\n";
135 dpavlin 43 }
136     close $client;
137     }
138    
139     die "server died";
140     }
141    
142 dpavlin 48
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 dpavlin 19 my $device = "/dev/ttyUSB0";
158 dpavlin 82 my $baudrate = "38400";
159 dpavlin 19 my $databits = "8";
160 dpavlin 82 my $parity = "even";
161 dpavlin 19 my $stopbits = "1";
162     my $handshake = "none";
163    
164 dpavlin 29 my $program_path = './program/';
165 dpavlin 34 my $secure_path = './secure/';
166 dpavlin 29
167 dpavlin 43 # http server
168     my $http_server = 1;
169    
170 dpavlin 41 # 3M defaults: 8,4
171 dpavlin 75 # cards 16, stickers: 8
172     my $max_rfid_block = 8;
173 dpavlin 41 my $read_blocks = 8;
174    
175 dpavlin 1 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 dpavlin 19 GetOptions(
188 dpavlin 22 'd|debug+' => \$debug,
189 dpavlin 19 'device=s' => \$device,
190     'baudrate=i' => \$baudrate,
191     'databits=i' => \$databits,
192     'parity=s' => \$parity,
193     'stopbits=i' => \$stopbits,
194     'handshake=s' => \$handshake,
195 dpavlin 45 'http-server!' => \$http_server,
196 dpavlin 19 ) or die $!;
197    
198 dpavlin 22 my $verbose = $debug > 0 ? $debug-- : 0;
199    
200 dpavlin 1 =head1 NAME
201    
202     3m-810 - support for 3M 810 RFID reader
203    
204     =head1 SYNOPSIS
205    
206 dpavlin 19 3m-810.pl --device /dev/ttyUSB0
207 dpavlin 1
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 dpavlin 15 L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
219    
220 dpavlin 1 =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 dpavlin 31 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 dpavlin 19 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 dpavlin 1 $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 dpavlin 48 warn "## using $device $baudrate $databits $parity $stopbits debug: $debug verbose: $verbose\n";
257 dpavlin 1
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 dpavlin 82 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 dpavlin 4 # initial hand-shake with device
309    
310 dpavlin 20 cmd( 'D5 00 05 04 00 11 8C66', 'hw version',
311     'D5 00 09 04 00 11 0A 05 00 02 7250', sub {
312 dpavlin 23 my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
313     print "hardware version $hw_ver\n";
314 dpavlin 2 });
315 dpavlin 1
316 dpavlin 20 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 dpavlin 1
319 dpavlin 43 sub scan_for_tags {
320 dpavlin 1
321 dpavlin 43 my @tags;
322 dpavlin 20
323 dpavlin 48 cmd( 'D6 00 05 FE 00 05 FA40', "scan for tags",
324 dpavlin 43 '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 dpavlin 20
328 dpavlin 43 if ( ! $nr ) {
329 dpavlin 48 _log "no tags in range\n";
330 dpavlin 43 update_visible_tags();
331     $tags_data = {};
332     } else {
333 dpavlin 1
334 dpavlin 43 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 dpavlin 16
338 dpavlin 43 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 dpavlin 48 _log "$nr tags in range: ", join(',', @tags ) , "\n";
341 dpavlin 25
342 dpavlin 43 update_visible_tags( @tags );
343     }
344 dpavlin 5 }
345 dpavlin 43 );
346 dpavlin 5
347 dpavlin 48 diag "tags: ",dump( @tags );
348 dpavlin 43 return $tags_data;
349 dpavlin 22
350 dpavlin 43 }
351 dpavlin 22
352 dpavlin 43 # start scanning for tags
353    
354     if ( $http_server ) {
355     http_server;
356     } else {
357 dpavlin 58 while (1) {
358     scan_for_tags;
359     sleep 1;
360     }
361 dpavlin 43 }
362    
363     die "over and out";
364    
365 dpavlin 22 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 dpavlin 51 $visible_tags->{$tag}++;
373 dpavlin 22 if ( ! defined $last_visible_tags->{$tag} ) {
374 dpavlin 25 if ( defined $tags_data->{$tag} ) {
375 dpavlin 64 warn "$tag in range\n";
376 dpavlin 25 } else {
377     read_tag( $tag );
378     }
379 dpavlin 22 } else {
380     warn "## using cached data for $tag" if $debug;
381     }
382     delete $last_visible_tags->{$tag}; # leave just missing tags
383 dpavlin 29
384     if ( -e "$program_path/$tag" ) {
385     write_tag( $tag );
386     }
387 dpavlin 34 if ( -e "$secure_path/$tag" ) {
388     secure_tag( $tag );
389     }
390 dpavlin 22 }
391    
392     foreach my $tag ( keys %$last_visible_tags ) {
393 dpavlin 23 my $data = delete $tags_data->{$tag};
394 dpavlin 64 warn "$tag removed ", dump($data), $/;
395 dpavlin 22 }
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 dpavlin 28 my $tag_data_block;
401 dpavlin 22
402 dpavlin 28 sub read_tag_data {
403     my ($start_block,$rest) = @_;
404     die "no rest?" unless $rest;
405 dpavlin 41
406     my $last_block = 0;
407    
408 dpavlin 28 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 dpavlin 41 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
418 dpavlin 28 my $data = substr( $block, 2 );
419     die "data payload should be 4 bytes" if length($data) != 4;
420 dpavlin 40 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
421 dpavlin 28 $tag_data_block->{$tag}->[ $ord ] = $data;
422 dpavlin 41 $last_block = $ord;
423 dpavlin 28 }
424     $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
425 dpavlin 31
426     my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
427 dpavlin 42 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
428 dpavlin 41
429 dpavlin 42 return $last_block + 1;
430 dpavlin 28 }
431    
432 dpavlin 59 my $saved_in_log;
433    
434 dpavlin 43 sub decode_tag {
435     my $tag = shift;
436    
437 dpavlin 78 my $data = $tags_data->{$tag};
438     if ( ! $data ) {
439     warn "no data for $tag\n";
440     return;
441     }
442 dpavlin 43
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 dpavlin 59 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 dpavlin 43 return $hash;
466     }
467    
468 dpavlin 67 sub forget_tag {
469     my $tag = shift;
470     delete $tags_data->{$tag};
471     delete $visible_tags->{$tag};
472     }
473    
474 dpavlin 16 sub read_tag {
475     my ( $tag ) = @_;
476 dpavlin 1
477 dpavlin 22 confess "no tag?" unless $tag;
478    
479 dpavlin 16 print "read_tag $tag\n";
480 dpavlin 1
481 dpavlin 41 my $start_block = 0;
482 dpavlin 28
483 dpavlin 41 while ( $start_block < $max_rfid_block ) {
484 dpavlin 1
485 dpavlin 41 cmd(
486 dpavlin 65 sprintf( "D6 00 0D 02 $tag %02x %02x BEEF", $start_block, $read_blocks ),
487 dpavlin 41 "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 dpavlin 65 "D6 00 0F FE 00 00 05 01 $tag BEEF", sub {
493 dpavlin 41 print "FIXME: tag $tag ready? (expected block read instead)\n";
494     },
495 dpavlin 78 "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 dpavlin 41 );
502    
503     }
504    
505 dpavlin 33 my $security;
506    
507     cmd(
508 dpavlin 65 "D6 00 0B 0A $tag BEEF", "check security $tag",
509 dpavlin 33 "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 dpavlin 54 $tags_security->{$tag} = $security;
516 dpavlin 33 warn "# SECURITY $tag = $security\n";
517 dpavlin 78 },
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 dpavlin 33 );
524    
525 dpavlin 43 print "TAG $tag ", dump(decode_tag( $tag ));
526 dpavlin 16 }
527    
528 dpavlin 29 sub write_tag {
529 dpavlin 59 my ($tag,$data) = @_;
530 dpavlin 29
531     my $path = "$program_path/$tag";
532 dpavlin 59 $data = read_file( $path ) if -e $path;
533 dpavlin 29
534 dpavlin 59 die "no data" unless $data;
535    
536 dpavlin 38 my $hex_data;
537 dpavlin 29
538 dpavlin 38 if ( $data =~ s{^hex\s+}{} ) {
539     $hex_data = $data;
540     $hex_data =~ s{\s+}{}g;
541     } else {
542 dpavlin 29
543 dpavlin 38 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
544 dpavlin 30
545 dpavlin 41 my $max_len = $max_rfid_block * 4;
546 dpavlin 30
547 dpavlin 38 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 dpavlin 40 # pad to block size
557     $hex_data .= '00' x ( 4 - $len % 4 );
558     my $blocks = sprintf('%02x', length($hex_data) / 4);
559 dpavlin 38
560     print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
561    
562 dpavlin 29 cmd(
563 dpavlin 65 "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 dpavlin 40 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
566 dpavlin 29
567     my $to = $path;
568     $to .= '.' . time();
569    
570     rename $path, $to;
571     print ">> $to\n";
572    
573 dpavlin 67 forget_tag $tag;
574 dpavlin 29 }
575    
576 dpavlin 67 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 dpavlin 34 sub secure_tag {
588     my ($tag) = @_;
589    
590     my $path = "$secure_path/$tag";
591     my $data = substr(read_file( $path ),0,2);
592    
593 dpavlin 67 secure_tag_with( $tag, $data );
594 dpavlin 34
595     my $to = $path;
596     $to .= '.' . time();
597    
598     rename $path, $to;
599     print ">> $to\n";
600     }
601    
602 dpavlin 19 exit;
603    
604 dpavlin 1 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 dpavlin 38 my $len = length($str);
635     die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
636 dpavlin 19 print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
637 dpavlin 1 }
638    
639     sub as_hex {
640     my @out;
641     foreach my $str ( @_ ) {
642 dpavlin 78 my $hex = uc unpack( 'H*', $str );
643 dpavlin 2 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
644 dpavlin 8 $hex =~ s/\s+$//;
645 dpavlin 1 push @out, $hex;
646     }
647 dpavlin 8 return join(' | ', @out);
648 dpavlin 1 }
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 dpavlin 28 die "no bytes on port: $!" unless defined $b;
656 dpavlin 82 warn "## got $c bytes: ", as_hex($b), "\n";
657 dpavlin 1 $data .= $b;
658     }
659     $desc ||= '?';
660 dpavlin 4 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
661 dpavlin 1 return $data;
662     }
663    
664 dpavlin 5 our $assert;
665 dpavlin 2
666 dpavlin 5 # my $rest = skip_assert( 3 );
667     sub skip_assert {
668     assert( 0, shift );
669     }
670    
671 dpavlin 2 sub assert {
672     my ( $from, $to ) = @_;
673    
674 dpavlin 5 $from ||= 0;
675 dpavlin 4 $to = length( $assert->{expect} ) if ! defined $to;
676    
677 dpavlin 2 my $p = substr( $assert->{payload}, $from, $to );
678     my $e = substr( $assert->{expect}, $from, $to );
679 dpavlin 3 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
680 dpavlin 5
681     # return the rest
682     return substr( $assert->{payload}, $to );
683 dpavlin 2 }
684    
685 dpavlin 15 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 dpavlin 8 # my $checksum = checksum( $bytes );
698     # my $checksum = checksum( $bytes, $original_checksum );
699     sub checksum {
700     my ( $bytes, $checksum ) = @_;
701    
702 dpavlin 16 my $len = ord(substr($bytes,2,1));
703 dpavlin 17 my $len_real = length($bytes) - 1;
704 dpavlin 16
705 dpavlin 17 if ( $len_real != $len ) {
706     print "length wrong: $len_real != $len\n";
707 dpavlin 38 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
708 dpavlin 17 }
709    
710 dpavlin 38 my $xor = crcccitt( substr($bytes,1) ); # skip D6
711     warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
712    
713 dpavlin 8 if ( defined $checksum && $xor ne $checksum ) {
714 dpavlin 65 warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
715 dpavlin 16 return $bytes . $xor;
716 dpavlin 8 }
717 dpavlin 16 return $bytes . $checksum;
718 dpavlin 8 }
719    
720 dpavlin 20 our $dispatch;
721    
722 dpavlin 1 sub readchunk {
723 dpavlin 43 # sleep 1; # FIXME remove
724 dpavlin 2
725 dpavlin 1 # read header of packet
726     my $header = read_bytes( 2, 'header' );
727 dpavlin 2 my $length = read_bytes( 1, 'length' );
728     my $len = ord($length);
729 dpavlin 1 my $data = read_bytes( $len, 'data' );
730    
731 dpavlin 2 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 dpavlin 8
735 dpavlin 2 my $checksum = substr( $data, -2, 2 );
736 dpavlin 20 checksum( $header . $length . $payload , $checksum );
737 dpavlin 1
738 dpavlin 22 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
739 dpavlin 2
740     $assert->{len} = $len;
741     $assert->{payload} = $payload;
742    
743 dpavlin 20 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 dpavlin 2
751 dpavlin 42 if ( defined $to ) {
752     my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
753 dpavlin 20 warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
754     $dispatch->{ $to }->( $rest );
755     } else {
756 dpavlin 64 die "NO DISPATCH for ",as_hex( $full ),"\n";
757 dpavlin 20 }
758    
759 dpavlin 2 return $data;
760 dpavlin 1 }
761    
762 dpavlin 2 sub str2bytes {
763     my $str = shift || confess "no str?";
764 dpavlin 5 my $b = $str;
765 dpavlin 17 $b =~ s/\s+//g;
766     $b =~ s/(..)/\\x$1/g;
767     $b = "\"$b\"";
768 dpavlin 5 my $bytes = eval $b;
769 dpavlin 2 die $@ if $@;
770 dpavlin 5 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
771 dpavlin 2 return $bytes;
772     }
773    
774     sub cmd {
775 dpavlin 20 my $cmd = shift || confess "no cmd?";
776     my $cmd_desc = shift || confess "no description?";
777     my @expect = @_;
778    
779 dpavlin 2 my $bytes = str2bytes( $cmd );
780    
781 dpavlin 16 # fix checksum if needed
782     $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
783    
784 dpavlin 22 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
785 dpavlin 2 $assert->{send} = $cmd;
786     writechunk( $bytes );
787    
788 dpavlin 20 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 dpavlin 2 }
798 dpavlin 20
799     readchunk;
800 dpavlin 2 }
801    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26