/[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 88 - (hide annotations)
Fri Jul 16 13:33:10 2010 UTC (13 years, 10 months ago) by dpavlin
File MIME type: text/plain
File size: 21558 byte(s)
support no tags in range
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 dpavlin 87 sub cpr_psst_wait {
291     # Protocol Start Synchronization Time (PSST): 5ms < data timeout 12 ms
292     Time::HiRes::sleep 0.005;
293     }
294    
295 dpavlin 82 sub cpr {
296 dpavlin 86 my ( $hex, $description, $coderef ) = @_;
297 dpavlin 82 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 dpavlin 85 warn ">> ", as_hex( $send ), "\t\t[$description]\n";
304 dpavlin 83 $port->write( $send );
305 dpavlin 84
306 dpavlin 87 cpr_psst_wait;
307    
308 dpavlin 83 my $r_len = $port->read(1);
309 dpavlin 84
310     while ( ! $r_len ) {
311 dpavlin 87 warn "# wait for response length 5ms\n";
312     cpr_psst_wait;
313 dpavlin 84 $r_len = $port->read(1);
314     }
315    
316 dpavlin 87 my $data_len = ord($r_len) - 1;
317     my $data = $port->read( $data_len );
318     warn "<< ", as_hex( $r_len . $data ),"\n";
319 dpavlin 83
320 dpavlin 87 cpr_psst_wait;
321    
322 dpavlin 86 $coderef->( $data ) if $coderef;
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 87
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 dpavlin 86 my $inventory;
347 dpavlin 83
348 dpavlin 86 while(1) {
349    
350     cpr( 'FF B0 01 00', 'ISO - Inventory', sub {
351     my $data = shift;
352 dpavlin 88 if (length($data) < 5 + 2 ) {
353     warn "# no tags in range\n";
354     return;
355     }
356 dpavlin 86 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 dpavlin 87 die "FIXME only TR-TYPE=3 ISO 15693 supported" unless $tr_type eq "\x03";
361 dpavlin 86 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 dpavlin 87
367     cpr_read( $uid );
368 dpavlin 86 }
369     warn "inventory: ",dump($inventory);
370     });
371    
372     }
373    
374 dpavlin 83 #cpr( '', '?' );
375    
376 dpavlin 82 exit;
377 dpavlin 4 # initial hand-shake with device
378    
379 dpavlin 20 cmd( 'D5 00 05 04 00 11 8C66', 'hw version',
380     'D5 00 09 04 00 11 0A 05 00 02 7250', sub {
381 dpavlin 23 my $hw_ver = join('.', unpack('CCCC', skip_assert(3) ));
382     print "hardware version $hw_ver\n";
383 dpavlin 2 });
384 dpavlin 1
385 dpavlin 20 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', sub { assert() } );
387 dpavlin 1
388 dpavlin 43 sub scan_for_tags {
389 dpavlin 1
390 dpavlin 43 my @tags;
391 dpavlin 20
392 dpavlin 48 cmd( 'D6 00 05 FE 00 05 FA40', "scan for tags",
393 dpavlin 43 'D6 00 0F FE 00 00 05 ', sub { # 01 E00401003123AA26 941A # seen, serial length: 8
394     my $rest = shift || die "no rest?";
395     my $nr = ord( substr( $rest, 0, 1 ) );
396 dpavlin 20
397 dpavlin 43 if ( ! $nr ) {
398 dpavlin 48 _log "no tags in range\n";
399 dpavlin 43 update_visible_tags();
400     $tags_data = {};
401     } else {
402 dpavlin 1
403 dpavlin 43 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 dpavlin 16
407 dpavlin 43 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 dpavlin 48 _log "$nr tags in range: ", join(',', @tags ) , "\n";
410 dpavlin 25
411 dpavlin 43 update_visible_tags( @tags );
412     }
413 dpavlin 5 }
414 dpavlin 43 );
415 dpavlin 5
416 dpavlin 48 diag "tags: ",dump( @tags );
417 dpavlin 43 return $tags_data;
418 dpavlin 22
419 dpavlin 43 }
420 dpavlin 22
421 dpavlin 43 # start scanning for tags
422    
423     if ( $http_server ) {
424     http_server;
425     } else {
426 dpavlin 58 while (1) {
427     scan_for_tags;
428     sleep 1;
429     }
430 dpavlin 43 }
431    
432     die "over and out";
433    
434 dpavlin 22 sub update_visible_tags {
435     my @tags = @_;
436    
437     my $last_visible_tags = $visible_tags;
438     $visible_tags = {};
439    
440     foreach my $tag ( @tags ) {
441 dpavlin 51 $visible_tags->{$tag}++;
442 dpavlin 22 if ( ! defined $last_visible_tags->{$tag} ) {
443 dpavlin 25 if ( defined $tags_data->{$tag} ) {
444 dpavlin 64 warn "$tag in range\n";
445 dpavlin 25 } else {
446     read_tag( $tag );
447     }
448 dpavlin 22 } else {
449     warn "## using cached data for $tag" if $debug;
450     }
451     delete $last_visible_tags->{$tag}; # leave just missing tags
452 dpavlin 29
453     if ( -e "$program_path/$tag" ) {
454     write_tag( $tag );
455     }
456 dpavlin 34 if ( -e "$secure_path/$tag" ) {
457     secure_tag( $tag );
458     }
459 dpavlin 22 }
460    
461     foreach my $tag ( keys %$last_visible_tags ) {
462 dpavlin 23 my $data = delete $tags_data->{$tag};
463 dpavlin 64 warn "$tag removed ", dump($data), $/;
464 dpavlin 22 }
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 dpavlin 28 my $tag_data_block;
470 dpavlin 22
471 dpavlin 28 sub read_tag_data {
472     my ($start_block,$rest) = @_;
473     die "no rest?" unless $rest;
474 dpavlin 41
475     my $last_block = 0;
476    
477 dpavlin 28 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 dpavlin 41 warn "got block $ord, expected block $expected_ord from ",dump( $block ) if $ord != $expected_ord;
487 dpavlin 28 my $data = substr( $block, 2 );
488     die "data payload should be 4 bytes" if length($data) != 4;
489 dpavlin 40 warn sprintf "## tag %9s %02d: %s |%-4s|\n", $tag, $ord, as_hex( $data ), $data;
490 dpavlin 28 $tag_data_block->{$tag}->[ $ord ] = $data;
491 dpavlin 41 $last_block = $ord;
492 dpavlin 28 }
493     $tags_data->{ $tag } = join('', @{ $tag_data_block->{$tag} });
494 dpavlin 31
495     my $item_type_nr = ord(substr( $tags_data->{$tag}, 3, 1 ));
496 dpavlin 42 print "DATA $tag ",dump( $tags_data ), " item type: ", ( $item_type->{ $item_type_nr } || "UNKWOWN '$item_type_nr'" ), "\n";
497 dpavlin 41
498 dpavlin 42 return $last_block + 1;
499 dpavlin 28 }
500    
501 dpavlin 59 my $saved_in_log;
502    
503 dpavlin 43 sub decode_tag {
504     my $tag = shift;
505    
506 dpavlin 78 my $data = $tags_data->{$tag};
507     if ( ! $data ) {
508     warn "no data for $tag\n";
509     return;
510     }
511 dpavlin 43
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 dpavlin 59 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 dpavlin 43 return $hash;
535     }
536    
537 dpavlin 67 sub forget_tag {
538     my $tag = shift;
539     delete $tags_data->{$tag};
540     delete $visible_tags->{$tag};
541     }
542    
543 dpavlin 16 sub read_tag {
544     my ( $tag ) = @_;
545 dpavlin 1
546 dpavlin 22 confess "no tag?" unless $tag;
547    
548 dpavlin 16 print "read_tag $tag\n";
549 dpavlin 1
550 dpavlin 41 my $start_block = 0;
551 dpavlin 28
552 dpavlin 41 while ( $start_block < $max_rfid_block ) {
553 dpavlin 1
554 dpavlin 41 cmd(
555 dpavlin 65 sprintf( "D6 00 0D 02 $tag %02x %02x BEEF", $start_block, $read_blocks ),
556 dpavlin 41 "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 dpavlin 65 "D6 00 0F FE 00 00 05 01 $tag BEEF", sub {
562 dpavlin 41 print "FIXME: tag $tag ready? (expected block read instead)\n";
563     },
564 dpavlin 78 "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 dpavlin 41 );
571    
572     }
573    
574 dpavlin 33 my $security;
575    
576     cmd(
577 dpavlin 65 "D6 00 0B 0A $tag BEEF", "check security $tag",
578 dpavlin 33 "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 dpavlin 54 $tags_security->{$tag} = $security;
585 dpavlin 33 warn "# SECURITY $tag = $security\n";
586 dpavlin 78 },
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 dpavlin 33 );
593    
594 dpavlin 43 print "TAG $tag ", dump(decode_tag( $tag ));
595 dpavlin 16 }
596    
597 dpavlin 29 sub write_tag {
598 dpavlin 59 my ($tag,$data) = @_;
599 dpavlin 29
600     my $path = "$program_path/$tag";
601 dpavlin 59 $data = read_file( $path ) if -e $path;
602 dpavlin 29
603 dpavlin 59 die "no data" unless $data;
604    
605 dpavlin 38 my $hex_data;
606 dpavlin 29
607 dpavlin 38 if ( $data =~ s{^hex\s+}{} ) {
608     $hex_data = $data;
609     $hex_data =~ s{\s+}{}g;
610     } else {
611 dpavlin 29
612 dpavlin 38 $data .= "\0" x ( 4 - ( length($data) % 4 ) );
613 dpavlin 30
614 dpavlin 41 my $max_len = $max_rfid_block * 4;
615 dpavlin 30
616 dpavlin 38 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 dpavlin 40 # pad to block size
626     $hex_data .= '00' x ( 4 - $len % 4 );
627     my $blocks = sprintf('%02x', length($hex_data) / 4);
628 dpavlin 38
629     print "write_tag $tag = ",dump( $data ), " [$len/$blocks] == $hex_data\n";
630    
631 dpavlin 29 cmd(
632 dpavlin 65 "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 dpavlin 40 ); # foreach ( 1 .. 3 ); # XXX 3m software does this three times!
635 dpavlin 29
636     my $to = $path;
637     $to .= '.' . time();
638    
639     rename $path, $to;
640     print ">> $to\n";
641    
642 dpavlin 67 forget_tag $tag;
643 dpavlin 29 }
644    
645 dpavlin 67 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 dpavlin 34 sub secure_tag {
657     my ($tag) = @_;
658    
659     my $path = "$secure_path/$tag";
660     my $data = substr(read_file( $path ),0,2);
661    
662 dpavlin 67 secure_tag_with( $tag, $data );
663 dpavlin 34
664     my $to = $path;
665     $to .= '.' . time();
666    
667     rename $path, $to;
668     print ">> $to\n";
669     }
670    
671 dpavlin 19 exit;
672    
673 dpavlin 1 for ( 1 .. 3 ) {
674    
675     # ++-->type 00-0a
676     # 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
677     # 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
678     # 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
679    
680     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 $_" );
681     warn "D6 00 0D 04 00 E00401003123AA26 07 CFF1 -- ack 7 block?\n";
682    
683     }
684     warn " D6 00 0F FE 00 00 05 01 E00401003123AA26 941A\n";
685    
686     cmd( 'D6 00 05 FE 00 05 FA 40', "port-write scan $_" ) foreach ( 1 .. 2 );
687    
688     cmd('D6 00 0C 09 E00401003123AA26 D7 3AF0', 'checkin?',
689     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
690     cmd('D6 00 0C 09 E00401003123AA26 DA EB5D', 'checkout?',
691     'D6 00 0C 09 00 E00401003123AA26 6A44 -- no?' );
692    
693     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',
694     'D6 00 0D 04 00 E00401003123AA26 06 DFD0 -- ack 6 blocks' ) foreach ( 1 .. 3 );
695    
696     undef $port;
697     print "Port closed\n";
698    
699     sub writechunk
700     {
701     my $str=shift;
702     my $count = $port->write($str);
703 dpavlin 38 my $len = length($str);
704     die "wrong write length $count != $len in ",as_hex( $str ) if $count != $len;
705 dpavlin 19 print "#> ", as_hex( $str ), "\t[$count]\n" if $debug;
706 dpavlin 1 }
707    
708     sub as_hex {
709     my @out;
710     foreach my $str ( @_ ) {
711 dpavlin 78 my $hex = uc unpack( 'H*', $str );
712 dpavlin 2 $hex =~ s/(..)/$1 /g if length( $str ) > 2;
713 dpavlin 8 $hex =~ s/\s+$//;
714 dpavlin 1 push @out, $hex;
715     }
716 dpavlin 8 return join(' | ', @out);
717 dpavlin 1 }
718    
719     sub read_bytes {
720     my ( $len, $desc ) = @_;
721     my $data = '';
722     while ( length( $data ) < $len ) {
723     my ( $c, $b ) = $port->read(1);
724 dpavlin 28 die "no bytes on port: $!" unless defined $b;
725 dpavlin 82 warn "## got $c bytes: ", as_hex($b), "\n";
726 dpavlin 83 last if $c == 0;
727 dpavlin 1 $data .= $b;
728     }
729     $desc ||= '?';
730 dpavlin 4 warn "#< ", as_hex($data), "\t$desc\n" if $debug;
731 dpavlin 1 return $data;
732     }
733    
734 dpavlin 5 our $assert;
735 dpavlin 2
736 dpavlin 5 # my $rest = skip_assert( 3 );
737     sub skip_assert {
738     assert( 0, shift );
739     }
740    
741 dpavlin 2 sub assert {
742     my ( $from, $to ) = @_;
743    
744 dpavlin 5 $from ||= 0;
745 dpavlin 4 $to = length( $assert->{expect} ) if ! defined $to;
746    
747 dpavlin 2 my $p = substr( $assert->{payload}, $from, $to );
748     my $e = substr( $assert->{expect}, $from, $to );
749 dpavlin 3 warn "EXPECTED ",as_hex($e), " GOT ", as_hex($p), " [$from-$to] in ",dump( $assert ), "\n" if $e ne $p;
750 dpavlin 5
751     # return the rest
752     return substr( $assert->{payload}, $to );
753 dpavlin 2 }
754    
755 dpavlin 15 use Digest::CRC;
756    
757     sub crcccitt {
758     my $bytes = shift;
759     my $crc = Digest::CRC->new(
760     # midified CCITT to xor with 0xffff instead of 0x0000
761     width => 16, init => 0xffff, xorout => 0xffff, refout => 0, poly => 0x1021, refin => 0,
762     ) or die $!;
763     $crc->add( $bytes );
764     pack('n', $crc->digest);
765     }
766    
767 dpavlin 8 # my $checksum = checksum( $bytes );
768     # my $checksum = checksum( $bytes, $original_checksum );
769     sub checksum {
770     my ( $bytes, $checksum ) = @_;
771    
772 dpavlin 16 my $len = ord(substr($bytes,2,1));
773 dpavlin 17 my $len_real = length($bytes) - 1;
774 dpavlin 16
775 dpavlin 17 if ( $len_real != $len ) {
776     print "length wrong: $len_real != $len\n";
777 dpavlin 38 $bytes = substr($bytes,0,2) . chr($len_real) . substr($bytes,3);
778 dpavlin 17 }
779    
780 dpavlin 38 my $xor = crcccitt( substr($bytes,1) ); # skip D6
781     warn "## checksum ",dump( $bytes, $xor, $checksum ) if $debug;
782    
783 dpavlin 8 if ( defined $checksum && $xor ne $checksum ) {
784 dpavlin 65 warn "checksum error: ", as_hex($xor), " != ", as_hex($checksum), " data: ", as_hex($bytes), "\n" if $checksum ne "\xBE\xEF";
785 dpavlin 16 return $bytes . $xor;
786 dpavlin 8 }
787 dpavlin 16 return $bytes . $checksum;
788 dpavlin 8 }
789    
790 dpavlin 20 our $dispatch;
791    
792 dpavlin 1 sub readchunk {
793 dpavlin 43 # sleep 1; # FIXME remove
794 dpavlin 2
795 dpavlin 1 # read header of packet
796     my $header = read_bytes( 2, 'header' );
797 dpavlin 2 my $length = read_bytes( 1, 'length' );
798     my $len = ord($length);
799 dpavlin 1 my $data = read_bytes( $len, 'data' );
800    
801 dpavlin 2 my $payload = substr( $data, 0, -2 );
802     my $payload_len = length($data);
803     warn "## payload too short $payload_len != $len\n" if $payload_len != $len;
804 dpavlin 8
805 dpavlin 2 my $checksum = substr( $data, -2, 2 );
806 dpavlin 20 checksum( $header . $length . $payload , $checksum );
807 dpavlin 1
808 dpavlin 22 print "<< ",as_hex( $header ), " [$len] ", as_hex( $payload ), " | sum: ",as_hex($checksum),"\n" if $verbose;
809 dpavlin 2
810     $assert->{len} = $len;
811     $assert->{payload} = $payload;
812    
813 dpavlin 20 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 dpavlin 2
821 dpavlin 42 if ( defined $to ) {
822     my $rest = substr( $payload, length($to) ) if length($to) < length($payload);
823 dpavlin 20 warn "## DISPATCH payload to with rest", dump( $payload, $to, $rest ) if $debug;
824     $dispatch->{ $to }->( $rest );
825     } else {
826 dpavlin 64 die "NO DISPATCH for ",as_hex( $full ),"\n";
827 dpavlin 20 }
828    
829 dpavlin 2 return $data;
830 dpavlin 1 }
831    
832 dpavlin 2 sub str2bytes {
833     my $str = shift || confess "no str?";
834 dpavlin 5 my $b = $str;
835 dpavlin 17 $b =~ s/\s+//g;
836     $b =~ s/(..)/\\x$1/g;
837     $b = "\"$b\"";
838 dpavlin 5 my $bytes = eval $b;
839 dpavlin 2 die $@ if $@;
840 dpavlin 5 warn "## str2bytes( $str ) => $b => ",as_hex($bytes) if $debug;
841 dpavlin 2 return $bytes;
842     }
843    
844     sub cmd {
845 dpavlin 20 my $cmd = shift || confess "no cmd?";
846     my $cmd_desc = shift || confess "no description?";
847     my @expect = @_;
848    
849 dpavlin 2 my $bytes = str2bytes( $cmd );
850    
851 dpavlin 16 # fix checksum if needed
852     $bytes = checksum( substr( $bytes, 0, -2 ), substr( $bytes, -2, 2 ) );
853    
854 dpavlin 22 warn ">> ", as_hex( $bytes ), "\t## $cmd_desc\n" if $verbose;
855 dpavlin 2 $assert->{send} = $cmd;
856     writechunk( $bytes );
857    
858 dpavlin 20 while ( @expect ) {
859     my $pattern = str2bytes( shift @expect ) || confess "no pattern?";
860     my $coderef = shift @expect || confess "no coderef?";
861     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 dpavlin 2 }
868 dpavlin 20
869     readchunk;
870 dpavlin 2 }
871    

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26