/[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 85 - (hide annotations)
Mon Jul 12 12:00:39 2010 UTC (13 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 20628 byte(s)
cleanup code

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26