/[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 6 by dpavlin, Sun Sep 28 18:19:37 2008 UTC cpr-m02.pl revision 88 by dpavlin, Fri Jul 16 13:33:10 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;  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?',
178          'd60007fe00000500c97b'                                  => 'no tag in range',          'd60007fe00000500c97b'                                  => 'no tag in range',
# Line 21  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 39  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<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 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    warn "## known item type: ",dump( $item_type ) if $debug;
248    
249  my $port=new Device::SerialPort($device) || die "new($device): $!\n";  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 77  $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    sub cpr_m02_checksum {
269            my $data = shift;
270    
271            my $preset = 0xffff;
272            my $polynom = 0x8408;
273    
274            my $crc = $preset;
275            foreach my $i ( 0 .. length($data) - 1 ) {
276                    $crc ^= ord(substr($data,$i,1));
277                    for my $j ( 0 .. 7 ) {
278                            if ( $crc & 0x0001 ) {
279                                    $crc = ( $crc >> 1 ) ^ $polynom;
280                            } else {
281                                    $crc = $crc >> 1;
282                            }
283                    }
284    #               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            if (length($data) < 5 + 2 ) {
353                    warn "# no tags in range\n";
354                    return;
355            }
356            my $data_sets = ord(substr($data,3,1));
357            $data = substr($data,4);
358            foreach ( 1 .. $data_sets ) {
359                    my $tr_type = substr($data,0,1);
360                    die "FIXME only TR-TYPE=3 ISO 15693 supported" unless $tr_type eq "\x03";
361                    my $dsfid   = substr($data,1,1);
362                    my $uid     = substr($data,2,8);
363                    $inventory->{$uid}++;
364                    $data = substr($data,10);
365                    warn "# TAG $_ ",as_hex( $tr_type, $dsfid, $uid ),$/;
366    
367                    cpr_read( $uid );
368            }
369            warn "inventory: ",dump($inventory);
370    });
371    
372    }
373    
374    #cpr( '', '?' );
375    
376    exit;
377  # initial hand-shake with device  # initial hand-shake with device
378    
379  cmd( 'D5 00  05   04 00 11                 8C66', 'hw version?',  cmd( 'D5 00  05   04 00 11                 8C66', 'hw version',
380       '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 {
381          print "hardware version ", join('.', unpack('CCCC', skip_assert(3) )), "\n";          my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
382            print "hardware version $hw_ver\n";
383  });  });
384    
385  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?',
386       '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() }  );
387    
388  # start scanning for tags  sub scan_for_tags {
389    
390            my @tags;
391    
392  cmd( 'D6 00  05   FE     00  05         FA40', "XXX scan $_",          cmd( 'D6 00  05   FE     00  05         FA40', "scan for tags",
393       '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 {  
394                          my $rest = shift || die "no rest?";                          my $rest = shift || die "no rest?";
395                          my $nr = ord( substr( $rest, 0, 1 ) );                          my $nr = ord( substr( $rest, 0, 1 ) );
                         my $tags = substr( $rest, 1 );  
396    
397                          my $tl = length( $tags );                          if ( ! $nr ) {
398                          die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;                                  _log "no tags in range\n";
399                                    update_visible_tags();
400                                    $tags_data = {};
401                            } else {
402    
403                                    my $tags = substr( $rest, 1 );
404                                    my $tl = length( $tags );
405                                    die "wrong length $tl for $nr tags: ",dump( $tags ) if $tl =! $nr * 8;
406    
407                                    push @tags, uc(unpack('H16', substr($tags, $_ * 8, 8))) foreach ( 0 .. $nr - 1 );
408                                    warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags ) if $debug;
409                                    _log "$nr tags in range: ", join(',', @tags ) , "\n";
410    
411                          my @tags;                                  update_visible_tags( @tags );
412                          push @tags, substr($tags, $_ * 8, 8) foreach ( 0 .. $nr - 1 );                          }
                         warn "## tags ",as_hex($tags), " [$tl] = ",dump( $tags );  
                         print "seen $nr tags: ", join(',', map { unpack('H16', $_) } @tags ) , "\n";  
413                  }                  }
414  ) }          );
415    
416            diag "tags: ",dump( @tags );
417            return $tags_data;
418    
419    }
420    
421    # start scanning for tags
422    
423    if ( $http_server ) {
424            http_server;
425    } else {
426            while (1) {
427                    scan_for_tags;
428                    sleep 1;
429            }
430    }
431    
432  ) foreach ( 1 .. 100 );  die "over and out";
433    
434  cmd( 'D6 00  0D  02      E00401003123AA26   00   03     1CC4', 'read offset: 0 blocks: 3' );  sub update_visible_tags {
435            my @tags = @_;
436    
437  #     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;
438  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 = {};
439    
440  cmd( 'D6 00  0D  02      E00401003123AA26   03   04     3970', 'read offset: 3 blocks: 4' );          foreach my $tag ( @tags ) {
441                    $visible_tags->{$tag}++;
442                    if ( ! defined $last_visible_tags->{$tag} ) {
443                            if ( defined $tags_data->{$tag} ) {
444                                    warn "$tag in range\n";
445                            } else {
446                                    read_tag( $tag );
447                            }
448                    } else {
449                            warn "## using cached data for $tag" if $debug;
450                    }
451                    delete $last_visible_tags->{$tag}; # leave just missing tags
452    
453                    if ( -e "$program_path/$tag" ) {
454                                    write_tag( $tag );
455                    }
456                    if ( -e "$secure_path/$tag" ) {
457                                    secure_tag( $tag );
458                    }
459            }
460    
461            foreach my $tag ( keys %$last_visible_tags ) {
462                    my $data = delete $tags_data->{$tag};
463                    warn "$tag removed ", dump($data), $/;
464            }
465    
466            warn "## update_visible_tags(",dump( @tags ),") = ",dump( $visible_tags )," removed: ",dump( $last_visible_tags ), " data: ",dump( $tags_data ) if $debug;
467    }
468    
469  #     D6 00  25  02 00   E00401003123AA26   04                         03 00   30 30 00 00   04 00   00 00 00 00    my $tag_data_block;
470  #                                                                      05 00   00 00 00 00   06 00   00 00 00 00    B9BA  
471  warn "D6 00  25  02 00   E00401003123AA26   04                         03 00   39 30 31 32   04 00   33 34 35 36  sub read_tag_data {
472                                                                         05 00   00 00 00 00   06 00   00 00 00 00    524B\n";          my ($start_block,$rest) = @_;
473  warn "D6 00  0F  FE  00 00  05 01   E00401003123AA26  941A ##### ready?\n";          die "no rest?" unless $rest;
474    
475            my $last_block = 0;
476    
477            warn "## DATA [$start_block] ", dump( $rest ) if $debug;
478            my $tag = uc(unpack('H16',substr( $rest, 0, 8 )));
479            my $blocks = ord(substr($rest,8,1));
480            $rest = substr($rest,9); # leave just data blocks
481            foreach my $nr ( 0 .. $blocks - 1 ) {
482                    my $block = substr( $rest, $nr * 6, 6 );
483                    warn "## block ",as_hex( $block ) if $debug;
484                    my $ord   = unpack('v',substr( $block, 0, 2 ));
485                    my $expected_ord = $nr + $start_block;
486                    warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
487                    my $data  = substr( $block, 2 );
488                    die "data payload should be 4 bytes" if length($data) != 4;
489                    warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
490                    $tag_data_block->{$tag}->[ $ord ] = $data;
491                    $last_block = $ord;
492            }
493            $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
494    
495            my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
496            print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
497    
498            return $last_block + 1;
499    }
500    
501    my $saved_in_log;
502    
503    sub decode_tag {
504            my $tag = shift;
505    
506            my $data = $tags_data->{$tag};
507            if ( ! $data ) {
508                    warn "no data for $tag\n";
509                    return;
510            }
511    
512            my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data);
513            my $hash = {
514                    u1 => $u1,
515                    u2 => $u2,
516                    set => ( $set_item & 0xf0 ) >> 4,
517                    total => ( $set_item & 0x0f ),
518    
519                    type => $type,
520                    content => $content,
521    
522                    branch => $br_lib >> 20,
523                    library => $br_lib & 0x000fffff,
524    
525                    custom => $custom,
526            };
527    
528            if ( ! $saved_in_log->{$tag}++ ) {
529                    open(my $log, '>>', 'rfid-log.txt');
530                    print $log strftime( "%Y-%m-%d %H:%M:%S", localtime ), ",$tag,$content\n";
531                    close($log);
532            }
533    
534            return $hash;
535    }
536    
537    sub forget_tag {
538            my $tag = shift;
539            delete $tags_data->{$tag};
540            delete $visible_tags->{$tag};
541    }
542    
543    sub read_tag {
544            my ( $tag ) = @_;
545    
546            confess "no tag?" unless $tag;
547    
548            print "read_tag $tag\n";
549    
550            my $start_block = 0;
551    
552            while ( $start_block < $max_rfid_block ) {
553    
554                    cmd(
555                             sprintf( "D6 00  0D  02      $tag   %02x   %02x     BEEF", $start_block, $read_blocks ),
556                                    "read $tag offset: $start_block blocks: $read_blocks",
557                            "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";
558                                    $start_block = read_tag_data( $start_block, @_ );
559                                    warn "# read tag upto $start_block\n";
560                            },
561                            "D6 00  0F  FE  00 00  05 01   $tag    BEEF", sub {
562                                    print "FIXME: tag $tag ready? (expected block read instead)\n";
563                            },
564                            "D6 00 0D 02 06 $tag", sub {
565                                    my $rest = shift;
566                                    print "ERROR reading $tag ", as_hex($rest), $/;
567                                    forget_tag $tag;
568                                    $start_block = $max_rfid_block; # XXX break out of while
569                            },
570                    );
571    
572            }
573    
574            my $security;
575    
576            cmd(
577                    "D6 00 0B 0A $tag BEEF", "check security $tag",
578                    "D6 00 0D 0A 00", sub {
579                            my $rest = shift;
580                            my $from_tag;
581                            ( $from_tag, $security ) = ( substr($rest,0,8), substr($rest,8,1) );
582                            die "security from other tag: ",as_hex( $from_tag ) if $from_tag ne str2bytes( $tag );
583                            $security = as_hex( $security );
584                            $tags_security->{$tag} = $security;
585                            warn "# SECURITY $tag = $security\n";
586                    },
587                    "D6 00 0C 0A 06", sub {
588                            my $rest = shift;
589                            warn "ERROR reading security from $rest\n";
590                            forget_tag $tag;
591                    },
592            );
593    
594            print "TAG $tag ", dump(decode_tag( $tag ));
595    }
596    
597    sub write_tag {
598            my ($tag,$data) = @_;
599    
600            my $path = "$program_path/$tag";
601            $data = read_file( $path ) if -e $path;
602    
603            die "no data" unless $data;
604    
605            my $hex_data;
606    
607            if ( $data =~ s{^hex\s+}{} ) {
608                    $hex_data = $data;
609                    $hex_data =~ s{\s+}{}g;
610            } else {
611    
612                    $data .= "\0" x ( 4 - ( length($data) % 4 ) );
613    
614                    my $max_len = $max_rfid_block * 4;
615    
616                    if ( length($data) > $max_len ) {
617                            $data = substr($data,0,$max_len);
618                            warn "strip content to $max_len bytes\n";
619                    }
620    
621                    $hex_data = unpack('H*', $data);
622            }
623    
624            my $len = length($hex_data) / 2;
625            # pad to block size
626            $hex_data .= '00' x ( 4 - $len % 4 );
627            my $blocks = sprintf('%02x', length($hex_data) / 4);
628    
629            print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
630    
631            cmd(
632                    "d6 00  ff  04  $tag  00 $blocks 00  $hex_data  BEEF", "write $tag",
633                    "d6 00  0d  04 00  $tag  $blocks  BEEF", sub { assert() },
634            ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
635    
636            my $to = $path;
637            $to .= '.' . time();
638    
639            rename $path, $to;
640            print ">> $to\n";
641    
642            forget_tag $tag;
643    }
644    
645    sub secure_tag_with {
646            my ( $tag, $data ) = @_;
647    
648            cmd(
649                    "d6 00  0c  09  $tag $data BEEF", "secure $tag -> $data",
650                    "d6 00  0c  09 00  $tag    BEEF", sub { assert() },
651            );
652    
653            forget_tag $tag;
654    }
655    
656    sub secure_tag {
657            my ($tag) = @_;
658    
659            my $path = "$secure_path/$tag";
660            my $data = substr(read_file( $path ),0,2);
661    
662            secure_tag_with( $tag, $data );
663    
664            my $to = $path;
665            $to .= '.' . time();
666    
667            rename $path, $to;
668            print ">> $to\n";
669    }
670    
671    exit;
672    
673  for ( 1 .. 3 ) {  for ( 1 .. 3 ) {
674    
# Line 153  sub writechunk Line 700  sub writechunk
700  {  {
701          my $str=shift;          my $str=shift;
702          my $count = $port->write($str);          my $count = $port->write($str);
703          print "#> ", as_hex( $str ), "\t[$count]\n";          my $len = length($str);
704            die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
705            print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
706  }  }
707    
708  sub as_hex {  sub as_hex {
709          my @out;          my @out;
710          foreach my $str ( @_ ) {          foreach my $str ( @_ ) {
711                  my $hex = unpack( 'H*', $str );                  my $hex = uc unpack( 'H*', $str );
712                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;                  $hex =~ s/(..)/$1 /g if length( $str ) > 2;
713                    $hex =~ s/\s+$//;
714                  push @out, $hex;                  push @out, $hex;
715          }          }
716          return join('  ', @out);          return join(' | ', @out);
717  }  }
718    
719  sub read_bytes {  sub read_bytes {
# Line 171  sub read_bytes { Line 721  sub read_bytes {
721          my $data = '';          my $data = '';
722          while ( length( $data ) < $len ) {          while ( length( $data ) < $len ) {
723                  my ( $c, $b ) = $port->read(1);                  my ( $c, $b ) = $port->read(1);
724                  #warn "## got $c bytes: ", as_hex($b), "\n";                  die "no bytes on port: $!" unless defined $b;
725                    warn "## got $c bytes: ", as_hex($b), "\n";
726                    last if $c == 0;
727                  $data .= $b;                  $data .= $b;
728          }          }
729          $desc ||= '?';          $desc ||= '?';
# Line 200  sub assert { Line 752  sub assert {
752          return substr( $assert->{payload}, $to );          return substr( $assert->{payload}, $to );
753  }  }
754    
755  our $dispatch;  use Digest::CRC;
756  sub dispatch {  
757          my ( $pattern, $coderef ) = @_;  sub crcccitt {
758          my $patt = substr( str2bytes($pattern), 3 ); # just payload          my $bytes = shift;
759          my $l = length($patt);          my $crc = Digest::CRC->new(
760          my $p = substr( $assert->{payload}, 0, $l );                  # midified CCITT to xor with 0xffff instead of 0x0000
761          warn "## dispatch pattern $pattern [$l] ",dump( $patt, $p ) if $debug;                  width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
762            ) or die $!;
763          if ( $assert->{payload} eq $assert->{expect} ) {          $crc->add( $bytes );
764                  warn "## no dispatch, payload expected" if $debug;          pack('n', $crc->digest);
765          } elsif ( $p eq $patt ) {  }
766                  # if matched call with rest of payload  
767                  $coderef->( substr( $assert->{payload}, $l ) );  # my $checksum = checksum( $bytes );
768          } else {  # my $checksum = checksum( $bytes, $original_checksum );
769                  warn "## dispatch ignored" if $debug;  sub checksum {
770            my ( $bytes, $checksum ) = @_;
771    
772            my $len = ord(substr($bytes,2,1));
773            my $len_real = length($bytes) - 1;
774    
775            if ( $len_real != $len ) {
776                    print "length wrong: $len_real != $len\n";
777                    $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
778          }          }
779    
780            my $xor = crcccitt( substr($bytes,1) ); # skip D6
781            warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
782    
783            if ( defined $checksum && $xor ne $checksum ) {
784                    warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
785                    return $bytes . $xor;
786            }
787            return $bytes . $checksum;
788  }  }
789    
790  sub readchunk {  our $dispatch;
         my ( $parser ) = @_;  
791    
792          sleep 1;        # FIXME remove  sub readchunk {
793    #       sleep 1;        # FIXME remove
794    
795          # read header of packet          # read header of packet
796          my $header = read_bytes( 2, 'header' );          my $header = read_bytes( 2, 'header' );
797          my $length = read_bytes( 1, 'length' );          my $length = read_bytes( 1, 'length' );
798          my $len = ord($length);          my $len = ord($length);
799          my $data = read_bytes( $len, 'data' );          my $data = read_bytes( $len, 'data' );
         my ( $cmd ) = unpack('C', $data );  
800    
801          my $payload  = substr( $data, 0, -2 );          my $payload  = substr( $data, 0, -2 );
802          my $payload_len = length($data);          my $payload_len = length($data);
803          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;          warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
804    
805          my $checksum = substr( $data, -2, 2 );          my $checksum = substr( $data, -2, 2 );
806          # FIXME check checksum          checksum( $header . $length . $payload , $checksum );
807    
808          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;
809    
810          $assert->{len}      = $len;          $assert->{len}      = $len;
811          $assert->{payload}  = $payload;          $assert->{payload}  = $payload;
         $assert->{checksum} = $checksum;  
812    
813          $parser->( $len, $payload, $checksum ) if $parser && ref($parser) eq 'CODE';          my $full = $header . $length . $data; # full
814            # find longest match for incomming data
815            my ($to) = grep {
816                    my $match = substr($payload,0,length($_));
817                    m/^\Q$match\E/
818            } sort { length($a) <=> length($b) } keys %$dispatch;
819            warn "?? payload dispatch to ",dump( $payload, $dispatch, $to ) if $debug;
820    
821            if ( defined $to ) {
822                    my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
823                    warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
824                    $dispatch->{ $to }->( $rest );
825            } else {
826                    die "NO DISPATCH for ",as_hex( $full ),"\n";
827            }
828    
829          return $data;          return $data;
830  }  }
# Line 250  sub readchunk { Line 832  sub readchunk {
832  sub str2bytes {  sub str2bytes {
833          my $str = shift || confess "no str?";          my $str = shift || confess "no str?";
834          my $b = $str;          my $b = $str;
835          $b =~ s/\s+(\S\S)(\S\S)+\s*/ $1 $2/;    # fix checksum          $b =~ s/\s+//g;
836          $b =~ s/\s+$//;          $b =~ s/(..)/\\x$1/g;
837          $b =~ s/\s+/\\x/g;          $b = "\"$b\"";
         $b = '"\x' . $b . '"';  
838          my $bytes = eval $b;          my $bytes = eval $b;
839          die $@ if $@;          die $@ if $@;
840          warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;          warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
# Line 261  sub str2bytes { Line 842  sub str2bytes {
842  }  }
843    
844  sub cmd {  sub cmd {
845          my ( $cmd, $cmd_desc, $expect, $expect_desc, $coderef ) = @_;          my $cmd = shift || confess "no cmd?";
846            my $cmd_desc = shift || confess "no description?";
847            my @expect = @_;
848    
849          my $bytes = str2bytes( $cmd );          my $bytes = str2bytes( $cmd );
850    
851          warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n";          # fix checksum if needed
852            $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
853    
854            warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
855          $assert->{send} = $cmd;          $assert->{send} = $cmd;
856          writechunk( $bytes );          writechunk( $bytes );
857    
858          if ( $expect ) {          while ( @expect ) {
859                  warn "?? $expect", $expect_desc ? "\t## $expect_desc" : '', "\n";                  my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
860                  $assert->{expect} = substr(str2bytes($expect), 3, -2); # just expected payload                  my $coderef = shift @expect || confess "no coderef?";
861                  readchunk( $coderef );                  confess "not coderef" unless ref $coderef eq 'CODE';
862    
863                    next if defined $dispatch->{ $pattern };
864    
865                    $dispatch->{ substr($pattern,3) } = $coderef;
866                    warn "++ dispatch ", as_hex($pattern) ,dump( $dispatch ) if $debug;
867          }          }
868    
869            readchunk;
870  }  }
871    

Legend:
Removed from v.6  
changed lines
  Added in v.88

  ViewVC Help
Powered by ViewVC 1.1.26