--- 3m-810.pl 2010/02/11 13:23:26 65 +++ cpr-m02.pl 2010/07/16 16:34:13 91 @@ -10,6 +10,7 @@ use File::Slurp; use JSON; use POSIX qw(strftime); +use Time::HiRes; use IO::Socket::INET; @@ -31,7 +32,7 @@ Reuse => 1 ); - die "can't setup server" unless $server; + die "can't setup server: $!" unless $server; print "Server $0 ready at $server_url\n"; @@ -84,14 +85,14 @@ $d->{security} = $tags_security->{$_}; push @{ $json->{tags} }, $d; } keys %$tags; - print $client "HTTP/1.0 200 OK\r\nContent-Type: application/x-javascript\r\n\r\n", + print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n", $param->{callback}, "(", to_json($json), ")\r\n"; } elsif ( $method =~ m{/program} ) { my $status = 501; # Not implementd foreach my $p ( keys %$param ) { - next unless $p =~ m/^tag_(\S+)/; + next unless $p =~ m/^(E[0-9A-F]{15})$/; my $tag = $1; my $content = "\x04\x11\x00\x01" . $param->{$p}; $content = "\x00" if $param->{$p} eq 'blank'; @@ -99,15 +100,39 @@ warn "PROGRAM $tag $content\n"; write_tag( $tag, $content ); + secure_tag_with( $tag, $param->{$p} =~ /^130/ ? 'DA' : 'D7' ); } print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n"; + } elsif ( $method =~ m{/secure(.js)} ) { + + my $json = $1; + + my $status = 501; # Not implementd + + foreach my $p ( keys %$param ) { + next unless $p =~ m/^(E[0-9A-F]{15})$/; + my $tag = $1; + my $data = $param->{$p}; + $status = 302; + + warn "SECURE $tag $data\n"; + secure_tag_with( $tag, $data ); + } + + if ( $json ) { + print $client "HTTP/1.0 200 OK\r\nContent-Type: application/json\r\n\r\n", + $param->{callback}, "({ ok: 1 })\r\n"; + } else { + print $client "HTTP/1.0 $status $method\r\nLocation: $server_url\r\n\r\n"; + } + } else { - print $client "HTTP/1.0 404 Unkown method\r\n"; + print $client "HTTP/1.0 404 Unkown method\r\n\r\n"; } } else { - print $client "HTTP/1.0 500 No method\r\n"; + print $client "HTTP/1.0 500 No method\r\n\r\n"; } close $client; } @@ -131,9 +156,9 @@ sub diag { _message('diag',@_) }; my $device = "/dev/ttyUSB0"; -my $baudrate = "19200"; +my $baudrate = "38400"; my $databits = "8"; -my $parity = "none"; +my $parity = "even"; my $stopbits = "1"; my $handshake = "none"; @@ -144,7 +169,8 @@ my $http_server = 1; # 3M defaults: 8,4 -my $max_rfid_block = 16; +# cards 16, stickers: 8 +my $max_rfid_block = 8; my $read_blocks = 8; my $response = { @@ -239,6 +265,157 @@ #$port->stty_inpck(1); #$port->stty_istrip(1); +sub cpr_m02_checksum { + my $data = shift; + + my $preset = 0xffff; + my $polynom = 0x8408; + + my $crc = $preset; + foreach my $i ( 0 .. length($data) - 1 ) { + $crc ^= ord(substr($data,$i,1)); + for my $j ( 0 .. 7 ) { + if ( $crc & 0x0001 ) { + $crc = ( $crc >> 1 ) ^ $polynom; + } else { + $crc = $crc >> 1; + } + } +# warn sprintf('%d %04x', $i, $crc & 0xffff); + } + + return pack('v', $crc); +} + +sub cpr_psst_wait { + # Protocol Start Synchronization Time (PSST): 5ms < data timeout 12 ms + Time::HiRes::sleep 0.005; +} + +sub cpr { + my ( $hex, $description, $coderef ) = @_; + my $bytes = str2bytes($hex); + my $len = pack( 'c', length( $bytes ) + 3 ); + my $send = $len . $bytes; + my $checksum = cpr_m02_checksum($send); + $send .= $checksum; + + warn ">> ", as_hex( $send ), "\t\t[$description]\n"; + $port->write( $send ); + + cpr_psst_wait; + + my $r_len = $port->read(1); + + while ( ! $r_len ) { + warn "# wait for response length 5ms\n"; + cpr_psst_wait; + $r_len = $port->read(1); + } + + my $data_len = ord($r_len) - 1; + my $data = $port->read( $data_len ); + warn "<< ", as_hex( $r_len . $data ),"\n"; + + cpr_psst_wait; + + $coderef->( $data ) if $coderef; + +} + +# FF = COM-ADDR any + +cpr( 'FF 52 00', 'Boud Rate Detection' ); + +cpr( 'FF 65', 'Get Software Version' ); + +cpr( 'FF 66 00', 'Get Reader Info - General hard and firware' ); + +cpr( 'FF 69', 'RF Reset' ); + + +sub cpr_read { + my $uid = shift; + my $hex_uid = as_hex($uid); + + my $max_block; + + cpr( "FF B0 2B 01 $hex_uid", "Get System Information $hex_uid", sub { + my $data = shift; + + warn "# data ",as_hex($data); + + my $DSFID = substr($data,5-2,1); + my $UID = substr($data,6-2,8); + my $AFI = substr($data,14-2,1); + my $MEM = substr($data,15-2,1); + my $SIZE = substr($data,16-2,1); + my $IC_REF = substr($data,17-2,1); + + warn "# split ",as_hex( $DSFID, $UID, $AFI, $MEM, $SIZE, $IC_REF ); + + $max_block = ord($SIZE); + }); + + my $transponder_data; + + my $block = 0; + while ( $block < $max_block ) { + cpr( sprintf("FF B0 23 01 $hex_uid %02x 04", $block), "Read Multiple Blocks $block", sub { + my $data = shift; + + my $DB_N = ord substr($data,5-2,1); + my $DB_SIZE = ord substr($data,6-2,1); + + $data = substr($data,7-2,-2); + warn "# DB N: $DB_N SIZE: $DB_SIZE ", as_hex( $data ); + foreach ( 1 .. $DB_N ) { + my $sec = substr($data,0,1); + my $db = substr($data,1,$DB_SIZE); + warn "block $_ ",dump( $sec, $db ); + $transponder_data .= reverse split(//,$db); + $data = substr($data, $DB_SIZE + 1); + } + }); + $block += 4; + } + + warn "DATA $hex_uid ", dump($transponder_data); + exit; +} + + +my $inventory; + +while(1) { + +cpr( 'FF B0 01 00', 'ISO - Inventory', sub { + my $data = shift; + if (length($data) < 5 + 2 ) { + warn "# no tags in range\n"; + return; + } + my $data_sets = ord(substr($data,3,1)); + $data = substr($data,4); + foreach ( 1 .. $data_sets ) { + my $tr_type = substr($data,0,1); + die "FIXME only TR-TYPE=3 ISO 15693 supported" unless $tr_type eq "\x03"; + my $dsfid = substr($data,1,1); + my $uid = substr($data,2,8); + $inventory->{$uid}++; + $data = substr($data,10); + warn "# TAG $_ ",as_hex( $tr_type, $dsfid, $uid ),$/; + + cpr_read( $uid ); + } + warn "inventory: ",dump($inventory); +}); + +} + +#cpr( '', '?' ); + +exit; # initial hand-shake with device cmd( 'D5 00 05 04 00 11 8C66', 'hw version', @@ -368,7 +545,11 @@ sub decode_tag { my $tag = shift; - my $data = $tags_data->{$tag} || die "no data for $tag"; + my $data = $tags_data->{$tag}; + if ( ! $data ) { + warn "no data for $tag\n"; + return; + } my ( $u1, $set_item, $u2, $type, $content, $br_lib, $custom ) = unpack('C4Z16Nl>',$data); my $hash = { @@ -395,6 +576,12 @@ return $hash; } +sub forget_tag { + my $tag = shift; + delete $tags_data->{$tag}; + delete $visible_tags->{$tag}; +} + sub read_tag { my ( $tag ) = @_; @@ -416,6 +603,12 @@ "D6 00 0F FE 00 00 05 01 $tag BEEF", sub { print "FIXME: tag $tag ready? (expected block read instead)\n"; }, + "D6 00 0D 02 06 $tag", sub { + my $rest = shift; + print "ERROR reading $tag ", as_hex($rest), $/; + forget_tag $tag; + $start_block = $max_rfid_block; # XXX break out of while + }, ); } @@ -432,7 +625,12 @@ $security = as_hex( $security ); $tags_security->{$tag} = $security; warn "# SECURITY $tag = $security\n"; - } + }, + "D6 00 0C 0A 06", sub { + my $rest = shift; + warn "ERROR reading security from $rest\n"; + forget_tag $tag; + }, ); print "TAG $tag ", dump(decode_tag( $tag )); @@ -483,9 +681,18 @@ rename $path, $to; print ">> $to\n"; - # force re-read of tag - delete $tags_data->{$tag}; - delete $visible_tags->{$tag}; + forget_tag $tag; +} + +sub secure_tag_with { + my ( $tag, $data ) = @_; + + cmd( + "d6 00 0c 09 $tag $data BEEF", "secure $tag -> $data", + "d6 00 0c 09 00 $tag BEEF", sub { assert() }, + ); + + forget_tag $tag; } sub secure_tag { @@ -494,10 +701,7 @@ my $path = "$secure_path/$tag"; my $data = substr(read_file( $path ),0,2); - cmd( - "d6 00 0c 09 $tag $data BEEF", "secure $tag -> $data", - "d6 00 0c 09 00 $tag BEEF", sub { assert() }, - ); + secure_tag_with( $tag, $data ); my $to = $path; $to .= '.' . time(); @@ -546,7 +750,7 @@ sub as_hex { my @out; foreach my $str ( @_ ) { - my $hex = unpack( 'H*', $str ); + my $hex = uc unpack( 'H*', $str ); $hex =~ s/(..)/$1 /g if length( $str ) > 2; $hex =~ s/\s+$//; push @out, $hex; @@ -560,7 +764,8 @@ while ( length( $data ) < $len ) { my ( $c, $b ) = $port->read(1); die "no bytes on port: $!" unless defined $b; - #warn "## got $c bytes: ", as_hex($b), "\n"; + warn "## got $c bytes: ", as_hex($b), "\n"; + last if $c == 0; $data .= $b; } $desc ||= '?';