/[RFID]/3m-810.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 /3m-810.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 68 - (hide annotations)
Thu Feb 11 15:10:39 2010 UTC (14 years, 2 months ago) by dpavlin
File MIME type: text/plain
File size: 18738 byte(s)
automatic secure/unsecure tag based on content after programming

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

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.26