/[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 83 - (hide annotations)
Mon Jul 12 10:59:59 2010 UTC (13 years, 9 months ago) by dpavlin
File MIME type: text/plain
File size: 20443 byte(s)
use serial port directly

First step to fix our problem that only first command
sent to reader generates response

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 dpavlin 83 my ( $hex, $description ) = shift;
291 dpavlin 82 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 dpavlin 83 warn ">> ", as_hex( $send ), "[$description]\n";
298     $port->write( $send );
299     my $r_len = $port->read(1);
300     warn "<< response len: ", as_hex($r_len), "\n";
301 dpavlin 82 $r_len = ord($r_len) - 1;
302 dpavlin 83 my $data = $port->read( $r_len );
303 dpavlin 82 warn "<< ", as_hex( $data );
304 dpavlin 83
305     warn "## ",dump( $port->read(1) );
306 dpavlin 82 }
307    
308 dpavlin 83 #cpr( 'FF 52 00', 'detect boud rate' );
309 dpavlin 82
310 dpavlin 83 #cpr( '00 65', 'software version' );
311    
312     cpr( 'FF 65', 'get ? info' );
313    
314     cpr( 'FF 69 00', 'get reader info' );
315    
316     cpr( 'FF B0 01 00', '?' );
317    
318     cpr( 'FF 69', '?' );
319    
320     #cpr( '', '?' );
321    
322 dpavlin 82 exit;
323 dpavlin 4 # initial hand-shake with device
324    
325 dpavlin 20 cmd( 'D5 00 05 04 00 11 8C66', 'hw version',
326     'D5 00 09 04 00 11 0A 05 00 02 7250', sub {
327 dpavlin 23 my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
328     print "hardware version $hw_ver\n";
329 dpavlin 2 });
330 dpavlin 1
331 dpavlin 20 cmd( 'D6 00 0C 13 04 01 00 02 00 03 00 04 00 AAF2','FIXME: stats?',
332     'D6 00 0C 13 00 02 01 01 03 02 02 03 00 E778', sub { assert() } );
333 dpavlin 1
334 dpavlin 43 sub scan_for_tags {
335 dpavlin 1
336 dpavlin 43 my @tags;
337 dpavlin 20
338 dpavlin 48 cmd( 'D6 00 05 FE 00 05 FA40', "scan for tags",
339 dpavlin 43 'D6 00 0F FE 00 00 05 ', sub { # 01 E00401003123AA26 941A # seen, serial length: 8
340     my $rest = shift || die "no rest?";
341     my $nr = ord( substr( $rest, 0, 1 ) );
342 dpavlin 20
343 dpavlin 43 if ( ! $nr ) {
344 dpavlin 48 _log "no tags in range\n";
345 dpavlin 43 update_visible_tags();
346     $tags_data = {};
347     } else {
348 dpavlin 1
349 dpavlin 43 my $tags = substr( $rest, 1 );
350     my $tl = length( $tags );
351     die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
352 dpavlin 16
353 dpavlin 43 push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
354     warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
355 dpavlin 48 _log "$nr tags in range: ", join(',', @tags ) , "\n";
356 dpavlin 25
357 dpavlin 43 update_visible_tags( @tags );
358     }
359 dpavlin 5 }
360 dpavlin 43 );
361 dpavlin 5
362 dpavlin 48 diag "tags: ",dump( @tags );
363 dpavlin 43 return $tags_data;
364 dpavlin 22
365 dpavlin 43 }
366 dpavlin 22
367 dpavlin 43 # start scanning for tags
368    
369     if ( $http_server ) {
370     http_server;
371     } else {
372 dpavlin 58 while (1) {
373     scan_for_tags;
374     sleep 1;
375     }
376 dpavlin 43 }
377    
378     die "over and out";
379    
380 dpavlin 22 sub update_visible_tags {
381     my @tags = @_;
382    
383     my $last_visible_tags = $visible_tags;
384     $visible_tags = {};
385    
386     foreach my $tag ( @tags ) {
387 dpavlin 51 $visible_tags->{$tag}++;
388 dpavlin 22 if ( ! defined $last_visible_tags->{$tag} ) {
389 dpavlin 25 if ( defined $tags_data->{$tag} ) {
390 dpavlin 64 warn "$tag in range\n";
391 dpavlin 25 } else {
392     read_tag( $tag );
393     }
394 dpavlin 22 } else {
395     warn "## using cached data for $tag" if $debug;
396     }
397     delete $last_visible_tags->{$tag}; # leave just missing tags
398 dpavlin 29
399     if ( -e "$program_path/$tag" ) {
400     write_tag( $tag );
401     }
402 dpavlin 34 if ( -e "$secure_path/$tag" ) {
403     secure_tag( $tag );
404     }
405 dpavlin 22 }
406    
407     foreach my $tag ( keys %$last_visible_tags ) {
408 dpavlin 23 my $data = delete $tags_data->{$tag};
409 dpavlin 64 warn "$tag removed ", dump($data), $/;
410 dpavlin 22 }
411    
412     warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
413     }
414    
415 dpavlin 28 my $tag_data_block;
416 dpavlin 22
417 dpavlin 28 sub read_tag_data {
418     my ($start_block,$rest) = @_;
419     die "no rest?" unless $rest;
420 dpavlin 41
421     my $last_block = 0;
422    
423 dpavlin 28 warn "## DATA [$start_block] ", dump( $rest ) if $debug;
424     my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
425     my $blocks = ord(substr($rest,8,1));
426     $rest = substr($rest,9); # leave just data blocks
427     foreach my $nr ( 0 .. $blocks - 1 ) {
428     my $block = substr( $rest, $nr * 6, 6 );
429     warn "## block ",as_hex( $block ) if $debug;
430     my $ord = unpack('v',substr( $block, 0, 2 ));
431     my $expected_ord = $nr + $start_block;
432 dpavlin 41 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
433 dpavlin 28 my $data = substr( $block, 2 );
434     die "data payload should be 4 bytes" if length($data) != 4;
435 dpavlin 40 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
436 dpavlin 28 $tag_data_block->{$tag}->[ $ord ] = $data;
437 dpavlin 41 $last_block = $ord;
438 dpavlin 28 }
439     $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
440 dpavlin 31
441     my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
442 dpavlin 42 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
443 dpavlin 41
444 dpavlin 42 return $last_block + 1;
445 dpavlin 28 }
446    
447 dpavlin 59 my $saved_in_log;
448    
449 dpavlin 43 sub decode_tag {
450     my $tag = shift;
451    
452 dpavlin 78 my $data = $tags_data->{$tag};
453     if ( ! $data ) {
454     warn "no data for $tag\n";
455     return;
456     }
457 dpavlin 43
458     my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
459     my $hash = {
460     u1 => $u1,
461     u2 => $u2,
462     set => ( $set_item & 0xf0 ) >> 4,
463     total => ( $set_item & 0x0f ),
464    
465     type => $type,
466     content => $content,
467    
468     branch => $br_lib >> 20,
469     library => $br_lib & 0x000fffff,
470    
471     custom => $custom,
472     };
473    
474 dpavlin 59 if ( ! $saved_in_log->{$tag}++ ) {
475     open(my $log, '>>', 'rfid-log.txt');
476     print $log strftime( "%Y-%m-%d %H:%M:%S", localtime ), ",$tag,$content\n";
477     close($log);
478     }
479    
480 dpavlin 43 return $hash;
481     }
482    
483 dpavlin 67 sub forget_tag {
484     my $tag = shift;
485     delete $tags_data->{$tag};
486     delete $visible_tags->{$tag};
487     }
488    
489 dpavlin 16 sub read_tag {
490     my ( $tag ) = @_;
491 dpavlin 1
492 dpavlin 22 confess "no tag?" unless $tag;
493    
494 dpavlin 16 print "read_tag $tag\n";
495 dpavlin 1
496 dpavlin 41 my $start_block = 0;
497 dpavlin 28
498 dpavlin 41 while ( $start_block < $max_rfid_block ) {
499 dpavlin 1
500 dpavlin 41 cmd(
501 dpavlin 65 sprintf( "D6 00 0D 02 $tag %02x %02x BEEF", $start_block, $read_blocks ),
502 dpavlin 41 "read $tag offset: $start_block blocks: $read_blocks",
503     "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";
504     $start_block = read_tag_data( $start_block, @_ );
505     warn "# read tag upto $start_block\n";
506     },
507 dpavlin 65 "D6 00 0F FE 00 00 05 01 $tag BEEF", sub {
508 dpavlin 41 print "FIXME: tag $tag ready? (expected block read instead)\n";
509     },
510 dpavlin 78 "D6 00 0D 02 06 $tag", sub {
511     my $rest = shift;
512     print "ERROR reading $tag ", as_hex($rest), $/;
513     forget_tag $tag;
514     $start_block = $max_rfid_block; # XXX break out of while
515     },
516 dpavlin 41 );
517    
518     }
519    
520 dpavlin 33 my $security;
521    
522     cmd(
523 dpavlin 65 "D6 00 0B 0A $tag BEEF", "check security $tag",
524 dpavlin 33 "D6 00 0D 0A 00", sub {
525     my $rest = shift;
526     my $from_tag;
527     ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
528     die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
529     $security = as_hex( $security );
530 dpavlin 54 $tags_security->{$tag} = $security;
531 dpavlin 33 warn "# SECURITY $tag = $security\n";
532 dpavlin 78 },
533     "D6 00 0C 0A 06", sub {
534     my $rest = shift;
535     warn "ERROR reading security from $rest\n";
536     forget_tag $tag;
537     },
538 dpavlin 33 );
539    
540 dpavlin 43 print "TAG $tag ", dump(decode_tag( $tag ));
541 dpavlin 16 }
542    
543 dpavlin 29 sub write_tag {
544 dpavlin 59 my ($tag,$data) = @_;
545 dpavlin 29
546     my $path = "$program_path/$tag";
547 dpavlin 59 $data = read_file( $path ) if -e $path;
548 dpavlin 29
549 dpavlin 59 die "no data" unless $data;
550    
551 dpavlin 38 my $hex_data;
552 dpavlin 29
553 dpavlin 38 if ( $data =~ s{^hex\s+}{} ) {
554     $hex_data = $data;
555     $hex_data =~ s{\s+}{}g;
556     } else {
557 dpavlin 29
558 dpavlin 38 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
559 dpavlin 30
560 dpavlin 41 my $max_len = $max_rfid_block * 4;
561 dpavlin 30
562 dpavlin 38 if ( length($data) > $max_len ) {
563     $data = substr($data,0,$max_len);
564     warn "strip content to $max_len bytes\n";
565     }
566    
567     $hex_data = unpack('H*', $data);
568     }
569    
570     my $len = length($hex_data) / 2;
571 dpavlin 40 # pad to block size
572     $hex_data .= '00' x ( 4 - $len % 4 );
573     my $blocks = sprintf('%02x', length($hex_data) / 4);
574 dpavlin 38
575     print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
576    
577 dpavlin 29 cmd(
578 dpavlin 65 "d6 00 ff 04 $tag 00 $blocks 00 $hex_data BEEF", "write $tag",
579     "d6 00 0d 04 00 $tag $blocks BEEF", sub { assert() },
580 dpavlin 40 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
581 dpavlin 29
582     my $to = $path;
583     $to .= '.' . time();
584    
585     rename $path, $to;
586     print ">> $to\n";
587    
588 dpavlin 67 forget_tag $tag;
589 dpavlin 29 }
590    
591 dpavlin 67 sub secure_tag_with {
592     my ( $tag, $data ) = @_;
593    
594     cmd(
595     "d6 00 0c 09 $tag $data BEEF", "secure $tag -> $data",
596     "d6 00 0c 09 00 $tag BEEF", sub { assert() },
597     );
598    
599     forget_tag $tag;
600     }
601    
602 dpavlin 34 sub secure_tag {
603     my ($tag) = @_;
604    
605     my $path = "$secure_path/$tag";
606     my $data = substr(read_file( $path ),0,2);
607    
608 dpavlin 67 secure_tag_with( $tag, $data );
609 dpavlin 34
610     my $to = $path;
611     $to .= '.' . time();
612    
613     rename $path, $to;
614     print ">> $to\n";
615     }
616    
617 dpavlin 19 exit;
618    
619 dpavlin 1 for ( 1 .. 3 ) {
620    
621     # ++-->type 00-0a
622     # 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
623     # 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
624     # 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
625    
626     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 $_" );
627     warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
628    
629     }
630     warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
631    
632     cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
633    
634     cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
635     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
636     cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
637     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
638    
639     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',
640     'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
641    
642     undef $port;
643     print "Port closed\n";
644    
645     sub writechunk
646     {
647     my $str=shift;
648     my $count = $port->write($str);
649 dpavlin 38 my $len = length($str);
650     die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
651 dpavlin 19 print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
652 dpavlin 1 }
653    
654     sub as_hex {
655     my @out;
656     foreach my $str ( @_ ) {
657 dpavlin 78 my $hex = uc unpack( 'H*', $str );
658 dpavlin 2 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
659 dpavlin 8 $hex =~ s/\s+$//;
660 dpavlin 1 push @out, $hex;
661     }
662 dpavlin 8 return join(' | ', @out);
663 dpavlin 1 }
664    
665     sub read_bytes {
666     my ( $len, $desc ) = @_;
667     my $data = '';
668     while ( length( $data ) < $len ) {
669     my ( $c, $b ) = $port->read(1);
670 dpavlin 28 die "no bytes on port: $!" unless defined $b;
671 dpavlin 82 warn "## got $c bytes: ", as_hex($b), "\n";
672 dpavlin 83 last if $c == 0;
673 dpavlin 1 $data .= $b;
674     }
675     $desc ||= '?';
676 dpavlin 4 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
677 dpavlin 1 return $data;
678     }
679    
680 dpavlin 5 our $assert;
681 dpavlin 2
682 dpavlin 5 # my $rest = skip_assert( 3 );
683     sub skip_assert {
684     assert( 0, shift );
685     }
686    
687 dpavlin 2 sub assert {
688     my ( $from, $to ) = @_;
689    
690 dpavlin 5 $from ||= 0;
691 dpavlin 4 $to = length( $assert->{expect} ) if ! defined $to;
692    
693 dpavlin 2 my $p = substr( $assert->{payload}, $from, $to );
694     my $e = substr( $assert->{expect}, $from, $to );
695 dpavlin 3 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
696 dpavlin 5
697     # return the rest
698     return substr( $assert->{payload}, $to );
699 dpavlin 2 }
700    
701 dpavlin 15 use Digest::CRC;
702    
703     sub crcccitt {
704     my $bytes = shift;
705     my $crc = Digest::CRC->new(
706     # midified CCITT to xor with 0xffff instead of 0x0000
707     width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
708     ) or die $!;
709     $crc->add( $bytes );
710     pack('n', $crc->digest);
711     }
712    
713 dpavlin 8 # my $checksum = checksum( $bytes );
714     # my $checksum = checksum( $bytes, $original_checksum );
715     sub checksum {
716     my ( $bytes, $checksum ) = @_;
717    
718 dpavlin 16 my $len = ord(substr($bytes,2,1));
719 dpavlin 17 my $len_real = length($bytes) - 1;
720 dpavlin 16
721 dpavlin 17 if ( $len_real != $len ) {
722     print "length wrong: $len_real != $len\n";
723 dpavlin 38 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
724 dpavlin 17 }
725    
726 dpavlin 38 my $xor = crcccitt( substr($bytes,1) ); # skip D6
727     warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
728    
729 dpavlin 8 if ( defined $checksum && $xor ne $checksum ) {
730 dpavlin 65 warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
731 dpavlin 16 return $bytes . $xor;
732 dpavlin 8 }
733 dpavlin 16 return $bytes . $checksum;
734 dpavlin 8 }
735    
736 dpavlin 20 our $dispatch;
737    
738 dpavlin 1 sub readchunk {
739 dpavlin 43 # sleep 1; # FIXME remove
740 dpavlin 2
741 dpavlin 1 # read header of packet
742     my $header = read_bytes( 2, 'header' );
743 dpavlin 2 my $length = read_bytes( 1, 'length' );
744     my $len = ord($length);
745 dpavlin 1 my $data = read_bytes( $len, 'data' );
746    
747 dpavlin 2 my $payload = substr( $data, 0, -2 );
748     my $payload_len = length($data);
749     warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
750 dpavlin 8
751 dpavlin 2 my $checksum = substr( $data, -2, 2 );
752 dpavlin 20 checksum( $header . $length . $payload , $checksum );
753 dpavlin 1
754 dpavlin 22 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
755 dpavlin 2
756     $assert->{len} = $len;
757     $assert->{payload} = $payload;
758    
759 dpavlin 20 my $full = $header . $length . $data; # full
760     # find longest match for incomming data
761     my ($to) = grep {
762     my $match = substr($payload,0,length($_));
763     m/^\Q$match\E/
764     } sort { length($a) <=> length($b) } keys %$dispatch;
765     warn "?? payload dispatch to ",dump( $payload, $dispatch, $to ) if $debug;
766 dpavlin 2
767 dpavlin 42 if ( defined $to ) {
768     my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
769 dpavlin 20 warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
770     $dispatch->{ $to }->( $rest );
771     } else {
772 dpavlin 64 die "NO DISPATCH for ",as_hex( $full ),"\n";
773 dpavlin 20 }
774    
775 dpavlin 2 return $data;
776 dpavlin 1 }
777    
778 dpavlin 2 sub str2bytes {
779     my $str = shift || confess "no str?";
780 dpavlin 5 my $b = $str;
781 dpavlin 17 $b =~ s/\s+//g;
782     $b =~ s/(..)/\\x$1/g;
783     $b = "\"$b\"";
784 dpavlin 5 my $bytes = eval $b;
785 dpavlin 2 die $@ if $@;
786 dpavlin 5 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
787 dpavlin 2 return $bytes;
788     }
789    
790     sub cmd {
791 dpavlin 20 my $cmd = shift || confess "no cmd?";
792     my $cmd_desc = shift || confess "no description?";
793     my @expect = @_;
794    
795 dpavlin 2 my $bytes = str2bytes( $cmd );
796    
797 dpavlin 16 # fix checksum if needed
798     $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
799    
800 dpavlin 22 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
801 dpavlin 2 $assert->{send} = $cmd;
802     writechunk( $bytes );
803    
804 dpavlin 20 while ( @expect ) {
805     my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
806     my $coderef = shift @expect || confess "no coderef?";
807     confess "not coderef" unless ref $coderef eq 'CODE';
808    
809     next if defined $dispatch->{ $pattern };
810    
811     $dispatch->{ substr($pattern,3) } = $coderef;
812     warn "++ dispatch ", as_hex($pattern) ,dump( $dispatch ) if $debug;
813 dpavlin 2 }
814 dpavlin 20
815     readchunk;
816 dpavlin 2 }
817    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26