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

Legend:
Removed from v.2  
changed lines
  Added in v.87

  ViewVC Help
Powered by ViewVC 1.1.26