/[RFID]/cpr-m02.pl
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /cpr-m02.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 86 - (hide annotations)
Fri Jul 16 09:31:56 2010 UTC (13 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 21225 byte(s)
decode tags uid from inventory response

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26