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

Diff of /cpr-m02.pl

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

3m-810.pl revision 10 by dpavlin, Sun Sep 28 22:15:29 2008 UTC cpr-m02.pl revision 83 by dpavlin, Mon Jul 12 10:59:59 2010 UTC
# Line 6  use warnings; Line 6  use warnings;
6    
7  use Data::Dump qw/dump/;  use Data::Dump qw/dump/;
8  use Carp qw/confess/;  use Carp qw/confess/;
9    use Getopt::Long;
10    use File::Slurp;
11    use JSON;
12    use POSIX qw(strftime);
13    
14  my $debug = 0;  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 = {  my $response = {
176          'd500090400110a0500027250'                              => 'version?',          'd500090400110a0500027250'                              => 'version?',
# Line 21  my $response = { Line 184  my $response = {
184          'd60017fe00000502e00401003123aa26e00401003123aa283124' => 'tag #1 + tag red-stripe',          '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  =head1 NAME
201    
202  3m-810 - support for 3M 810 RFID reader  3m-810 - support for 3M 810 RFID reader
203    
204  =head1 SYNOPSIS  =head1 SYNOPSIS
205    
206  3m-810.pl [DEVICE [BAUD [DATA [PARITY [STOP [FLOW]]]]]]  3m-810.pl --device /dev/ttyUSB0
207    
208  =head1 DESCRIPTION  =head1 DESCRIPTION
209    
# Line 39  L<Device::SerialPort(3)> Line 215  L<Device::SerialPort(3)>
215    
216  L<perl(1)>  L<perl(1)>
217    
218    L<http://stackoverflow.com/questions/149617/how-could-i-guess-a-checksum-algorithm>
219    
220  =head1 AUTHOR  =head1 AUTHOR
221    
222  Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>  Dobrica Pavlinusic <dpavlin@rot13.org> L<http://www.rot13.org/~dpavlin/>
# Line 50  it under the same terms ans Perl itself. Line 228  it under the same terms ans Perl itself.
228    
229  =cut  =cut
230    
231  # your serial port.  my $item_type = {
232  my ($device,$baudrate,$databits,$parity,$stopbits,$handshake)=@ARGV;          1 => 'Book',
233  $device    ||= "/dev/ttyUSB0";          6 => 'CD/CD ROM',
234  $baudrate  ||= "19200";          2 => 'Magazine',
235  $databits  ||= "8";          13 => 'Book with Audio Tape',
236  $parity    ||= "none";          9 => 'Book with CD/CD ROM',
237  $stopbits  ||= "1";          0 => 'Other',
238  $handshake ||= "none";  
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 "new($device): $!\n";  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);  $handshake=$port->handshake($handshake);
251  $baudrate=$port->baudrate($baudrate);  $baudrate=$port->baudrate($baudrate);
252  $databits=$port->databits($databits);  $databits=$port->databits($databits);
253  $parity=$port->parity($parity);  $parity=$port->parity($parity);
254  $stopbits=$port->stopbits($stopbits);  $stopbits=$port->stopbits($stopbits);
255    
256  print "## using $device $baudrate $databits $parity $stopbits\n";  warn "## using $device $baudrate $databits $parity $stopbits debug: $debug verbose: $verbose\n";
257    
258  # Just in case: reset our timing and buffers  # Just in case: reset our timing and buffers
259  $port->lookclear();  $port->lookclear();
# Line 77  $port->read_char_time(5); Line 264  $port->read_char_time(5);
264  #$port->stty_inpck(1);  #$port->stty_inpck(1);
265  #$port->stty_istrip(1);  #$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, $description ) = 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 ), "[$description]\n";
298            $port->write( $send );
299            my $r_len = $port->read(1);
300            warn "<< response len: ", as_hex($r_len), "\n";
301            $r_len = ord($r_len) - 1;
302            my $data = $port->read( $r_len );
303            warn "<< ", as_hex( $data );
304    
305            warn "## ",dump( $port->read(1) );
306    }
307    
308    #cpr( 'FF  52 00', 'detect boud rate' );
309    
310    #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    exit;
323  # initial hand-shake with device  # initial hand-shake with device
324    
325  cmd( 'D5 00  05   04 00 11                 8C66', 'hw version?',  cmd( 'D5 00  05   04 00 11                 8C66', 'hw version',
326       'D5 00  09   04 00 11   0A 05 00 02   7250', 'hw 10.5.0.2', sub {       'D5 00  09   04 00 11   0A 05 00 02   7250', sub {
327          print "hardware version ", join('.', unpack('CCCC', skip_assert(3) )), "\n";          my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
328            print "hardware version $hw_ver\n";
329  });  });
330    
331  cmd( 'D6 00  0C   13  04  01 00  02 00  03 00  04 00   AAF2','stats?',  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','FIXME: unimplemented', sub { assert() }  );       'D6 00  0C   13  00  02 01 01 03 02 02 03  00     E778', sub { assert() }  );
333    
334  # start scanning for tags  sub scan_for_tags {
335    
336            my @tags;
337    
338  cmd( 'D6 00  05   FE     00  05         FA40', "XXX scan $_",          cmd( 'D6 00  05   FE     00  05         FA40', "scan for tags",
339       'D6 00  07   FE  00 00  05     00  C97B', 'no tag', sub {                   'D6 00  0F   FE  00 00  05 ', sub { # 01 E00401003123AA26  941A         # seen, serial length: 8
 dispatch(  
          'D6 00  0F   FE  00 00  05 ',# 01 E00401003123AA26  941A        # seen, serial length: 8  
                 sub {  
340                          my $rest = shift || die "no rest?";                          my $rest = shift || die "no rest?";
341                          my $nr = ord( substr( $rest, 0, 1 ) );                          my $nr = ord( substr( $rest, 0, 1 ) );
                         my $tags = substr( $rest, 1 );  
342    
343                          my $tl = length( $tags );                          if ( ! $nr ) {
344                          die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;                                  _log "no tags in range\n";
345                                    update_visible_tags();
346                                    $tags_data = {};
347                            } else {
348    
349                                    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    
353                                    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                                    _log "$nr tags in range: ", join(',', @tags ) , "\n";
356    
357                          my @tags;                                  update_visible_tags( @tags );
358                          push @tags, substr($tags, $_ * 8, 8) foreach ( 0 .. $nr - 1 );                          }
                         warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;  
                         print "seen $nr tags: ", join(',', map { unpack('H16', $_) } @tags ) , "\n";  
359                  }                  }
360  ) }          );
361    
362            diag "tags: ",dump( @tags );
363            return $tags_data;
364    
365    }
366    
367    # start scanning for tags
368    
369    if ( $http_server ) {
370            http_server;
371    } else {
372            while (1) {
373                    scan_for_tags;
374                    sleep 1;
375            }
376    }
377    
378  ) foreach ( 1 .. 100 );  die "over and out";
379    
380  cmd( 'D6 00  0D  02      E00401003123AA26   00   03     1CC4', 'read offset: 0 blocks: 3' );  sub update_visible_tags {
381            my @tags = @_;
382    
383  #     D6 00  1F  02 00   E00401003123AA26   03   00 00   04 11 00 01   01 00   30 30 30 30   02 00   30 30 30 30    E5F4          my $last_visible_tags = $visible_tags;
384  warn "D6 00  1F  02 00   E00401003123AA26   03   00 00   04 11 00 01   01 00   31 32 33 34   02 00   35 36 37 38    531F\n";          $visible_tags = {};
385    
386            foreach my $tag ( @tags ) {
387                    $visible_tags->{$tag}++;
388                    if ( ! defined $last_visible_tags->{$tag} ) {
389                            if ( defined $tags_data->{$tag} ) {
390                                    warn "$tag in range\n";
391                            } else {
392                                    read_tag( $tag );
393                            }
394                    } else {
395                            warn "## using cached data for $tag" if $debug;
396                    }
397                    delete $last_visible_tags->{$tag}; # leave just missing tags
398    
399  cmd( 'D6 00  0D  02      E00401003123AA26   03   04     3970', 'read offset: 3 blocks: 4' );                  if ( -e "$program_path/$tag" ) {
400                                    write_tag( $tag );
401                    }
402                    if ( -e "$secure_path/$tag" ) {
403                                    secure_tag( $tag );
404                    }
405            }
406    
407  #     D6 00  25  02 00   E00401003123AA26   04                         03 00   30 30 00 00   04 00   00 00 00 00            foreach my $tag ( keys %$last_visible_tags ) {
408  #                                                                      05 00   00 00 00 00   06 00   00 00 00 00    B9BA                  my $data = delete $tags_data->{$tag};
409  warn "D6 00  25  02 00   E00401003123AA26   04                         03 00   39 30 31 32   04 00   33 34 35 36                  warn "$tag removed ", dump($data), $/;
410                                                                         05 00   00 00 00 00   06 00   00 00 00 00    524B\n";          }
411  warn "D6 00  0F  FE  00 00  05 01   E00401003123AA26  941A ##### ready?\n";  
412            warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
413    }
414    
415    my $tag_data_block;
416    
417    sub read_tag_data {
418            my ($start_block,$rest) = @_;
419            die "no rest?" unless $rest;
420    
421            my $last_block = 0;
422    
423            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                    warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
433                    my $data  = substr( $block, 2 );
434                    die "data payload should be 4 bytes" if length($data) != 4;
435                    warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
436                    $tag_data_block->{$tag}->[ $ord ] = $data;
437                    $last_block = $ord;
438            }
439            $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
440    
441            my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
442            print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
443    
444            return $last_block + 1;
445    }
446    
447    my $saved_in_log;
448    
449    sub decode_tag {
450            my $tag = shift;
451    
452            my $data = $tags_data->{$tag};
453            if ( ! $data ) {
454                    warn "no data for $tag\n";
455                    return;
456            }
457    
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            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            return $hash;
481    }
482    
483    sub forget_tag {
484            my $tag = shift;
485            delete $tags_data->{$tag};
486            delete $visible_tags->{$tag};
487    }
488    
489    sub read_tag {
490            my ( $tag ) = @_;
491    
492            confess "no tag?" unless $tag;
493    
494            print "read_tag $tag\n";
495    
496            my $start_block = 0;
497    
498            while ( $start_block < $max_rfid_block ) {
499    
500                    cmd(
501                             sprintf( "D6 00  0D  02      $tag   %02x   %02x     BEEF", $start_block, $read_blocks ),
502                                    "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                            "D6 00  0F  FE  00 00  05 01   $tag    BEEF", sub {
508                                    print "FIXME: tag $tag ready? (expected block read instead)\n";
509                            },
510                            "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                    );
517    
518            }
519    
520            my $security;
521    
522            cmd(
523                    "D6 00 0B 0A $tag BEEF", "check security $tag",
524                    "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                            $tags_security->{$tag} = $security;
531                            warn "# SECURITY $tag = $security\n";
532                    },
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            );
539    
540            print "TAG $tag ", dump(decode_tag( $tag ));
541    }
542    
543    sub write_tag {
544            my ($tag,$data) = @_;
545    
546            my $path = "$program_path/$tag";
547            $data = read_file( $path ) if -e $path;
548    
549            die "no data" unless $data;
550    
551            my $hex_data;
552    
553            if ( $data =~ s{^hex\s+}{} ) {
554                    $hex_data = $data;
555                    $hex_data =~ s{\s+}{}g;
556            } else {
557    
558                    $data .= "\0" x ( 4 - ( length($data) % 4 ) );
559    
560                    my $max_len = $max_rfid_block * 4;
561    
562                    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            # pad to block size
572            $hex_data .= '00' x ( 4 - $len % 4 );
573            my $blocks = sprintf('%02x', length($hex_data) / 4);
574    
575            print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
576    
577            cmd(
578                    "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            ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
581    
582            my $to = $path;
583            $to .= '.' . time();
584    
585            rename $path, $to;
586            print ">> $to\n";
587    
588            forget_tag $tag;
589    }
590    
591    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    sub secure_tag {
603            my ($tag) = @_;
604    
605            my $path = "$secure_path/$tag";
606            my $data = substr(read_file( $path ),0,2);
607    
608            secure_tag_with( $tag, $data );
609    
610            my $to = $path;
611            $to .= '.' . time();
612    
613            rename $path, $to;
614            print ">> $to\n";
615    }
616    
617    exit;
618    
619  for ( 1 .. 3 ) {  for ( 1 .. 3 ) {
620    
# Line 153  sub writechunk Line 646  sub writechunk
646  {  {
647          my $str=shift;          my $str=shift;
648          my $count = $port->write($str);          my $count = $port->write($str);
649          print "#> ", as_hex( $str ), "\t[$count]\n";          my $len = length($str);
650            die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
651            print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
652  }  }
653    
654  sub as_hex {  sub as_hex {
655          my @out;          my @out;
656          foreach my $str ( @_ ) {          foreach my $str ( @_ ) {
657                  my $hex = unpack( 'H*', $str );                  my $hex = uc unpack( 'H*', $str );
658                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;
659                  $hex =~ s/\s+$//;                  $hex =~ s/\s+$//;
660                  push @out, $hex;                  push @out, $hex;
# Line 172  sub read_bytes { Line 667  sub read_bytes {
667          my $data = '';          my $data = '';
668          while ( length( $data ) < $len ) {          while ( length( $data ) < $len ) {
669                  my ( $c, $b ) = $port->read(1);                  my ( $c, $b ) = $port->read(1);
670                  #warn "## got $c bytes: ", as_hex($b), "\n";                  die "no bytes on port: $!" unless defined $b;
671                    warn "## got $c bytes: ", as_hex($b), "\n";
672                    last if $c == 0;
673                  $data .= $b;                  $data .= $b;
674          }          }
675          $desc ||= '?';          $desc ||= '?';
# Line 201  sub assert { Line 698  sub assert {
698          return substr( $assert->{payload}, $to );          return substr( $assert->{payload}, $to );
699  }  }
700    
701  our $dispatch;  use Digest::CRC;
702  sub dispatch {  
703          my ( $pattern, $coderef ) = @_;  sub crcccitt {
704          my $patt = substr( str2bytes($pattern), 3 ); # just payload          my $bytes = shift;
705          my $l = length($patt);          my $crc = Digest::CRC->new(
706          my $p = substr( $assert->{payload}, 0, $l );                  # midified CCITT to xor with 0xffff instead of 0x0000
707          warn "## dispatch pattern $pattern [$l] ",dump( $patt, $p ) if $debug;                  width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
708            ) or die $!;
709          if ( $assert->{payload} eq $assert->{expect} ) {          $crc->add( $bytes );
710                  warn "## no dispatch, payload expected" if $debug;          pack('n', $crc->digest);
         } elsif ( $p eq $patt ) {  
                 # if matched call with rest of payload  
                 $coderef->( substr( $assert->{payload}, $l ) );  
         } else {  
                 warn "## dispatch ignored" if $debug;  
         }  
711  }  }
712    
713  # my $checksum = checksum( $bytes );  # my $checksum = checksum( $bytes );
# Line 224  sub dispatch { Line 715  sub dispatch {
715  sub checksum {  sub checksum {
716          my ( $bytes, $checksum ) = @_;          my ( $bytes, $checksum ) = @_;
717    
718          my $xor = $checksum; # FIXME          my $len = ord(substr($bytes,2,1));
719            my $len_real = length($bytes) - 1;
720    
721            if ( $len_real != $len ) {
722                    print "length wrong: $len_real != $len\n";
723                    $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
724            }
725    
726            my $xor = crcccitt( substr($bytes,1) ); # skip D6
727            warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
728    
729          if ( defined $checksum && $xor ne $checksum ) {          if ( defined $checksum && $xor ne $checksum ) {
730                  print "checksum doesn't match: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n";                  warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
731                    return $bytes . $xor;
732          }          }
733            return $bytes . $checksum;
734  }  }
735    
736  sub readchunk {  our $dispatch;
         my ( $parser ) = @_;  
737    
738          sleep 1;        # FIXME remove  sub readchunk {
739    #       sleep 1;        # FIXME remove
740    
741          # read header of packet          # read header of packet
742          my $header = read_bytes( 2, 'header' );          my $header = read_bytes( 2, 'header' );
# Line 247  sub readchunk { Line 749  sub readchunk {
749          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
750    
751          my $checksum = substr( $data, -2, 2 );          my $checksum = substr( $data, -2, 2 );
752          checksum( $header . $length . $payload, $checksum );          checksum( $header . $length . $payload , $checksum );
753    
754          print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n";          print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
755    
756          $assert->{len}      = $len;          $assert->{len}      = $len;
757          $assert->{payload}  = $payload;          $assert->{payload}  = $payload;
758    
759          $parser->( $len, $payload ) if $parser && ref($parser) eq 'CODE';          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    
767            if ( defined $to ) {
768                    my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
769                    warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
770                    $dispatch->{ $to }->( $rest );
771            } else {
772                    die "NO DISPATCH for ",as_hex( $full ),"\n";
773            }
774    
775          return $data;          return $data;
776  }  }
# Line 262  sub readchunk { Line 778  sub readchunk {
778  sub str2bytes {  sub str2bytes {
779          my $str = shift || confess "no str?";          my $str = shift || confess "no str?";
780          my $b = $str;          my $b = $str;
781          $b =~ s/\s+(\S\S)(\S\S)+\s*/ $1 $2/;    # fix checksum          $b =~ s/\s+//g;
782          $b =~ s/\s+$//;          $b =~ s/(..)/\\x$1/g;
783          $b =~ s/\s+/\\x/g;          $b = "\"$b\"";
         $b = '"\x' . $b . '"';  
784          my $bytes = eval $b;          my $bytes = eval $b;
785          die $@ if $@;          die $@ if $@;
786          warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;          warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
# Line 273  sub str2bytes { Line 788  sub str2bytes {
788  }  }
789    
790  sub cmd {  sub cmd {
791          my ( $cmd, $cmd_desc, $expect, $expect_desc, $coderef ) = @_;          my $cmd = shift || confess "no cmd?";
792            my $cmd_desc = shift || confess "no description?";
793            my @expect = @_;
794    
795          my $bytes = str2bytes( $cmd );          my $bytes = str2bytes( $cmd );
796    
797          warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n";          # fix checksum if needed
798            $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
799    
800            warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
801          $assert->{send} = $cmd;          $assert->{send} = $cmd;
802          writechunk( $bytes );          writechunk( $bytes );
803    
804          if ( $expect ) {          while ( @expect ) {
805                  warn "?? $expect", $expect_desc ? "\t## $expect_desc" : '', "\n";                  my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
806                  $assert->{expect} = substr(str2bytes($expect), 3, -2); # just expected payload                  my $coderef = shift @expect || confess "no coderef?";
807                  readchunk( $coderef );                  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          }          }
814    
815            readchunk;
816  }  }
817    

Legend:
Removed from v.10  
changed lines
  Added in v.83

  ViewVC Help
Powered by ViewVC 1.1.26