1 package Biblio::RFID::Reader::CPRM02;
5 Biblio::RFID::Reader::CPRM02 - support for CPR-M02 RFID reader
9 This module implements serial protocol over usb/serial adapter with CPR-M02
10 reader as described in document C<H20800-16e-ID-B.pdf>
17 use base 'Biblio::RFID::Reader::Serial';
21 use Data::Dump qw(dump);
25 sub serial_settings {{
26 device => "/dev/ttyUSB0",
34 sub cpr_m02_checksum {
41 foreach my $i ( 0 .. length($data) - 1 ) {
42 $crc ^= ord(substr($data,$i,1));
43 for my $j ( 0 .. 7 ) {
44 if ( $crc & 0x0001 ) {
45 $crc = ( $crc >> 1 ) ^ $polynom;
50 # warn sprintf('%d %04x', $i, $crc & 0xffff);
53 return pack('v', $crc);
57 Time::HiRes::sleep 0.010;
63 my ( $hex, $description, $coderef ) = @_;
64 my $bytes = hex2bytes($hex);
65 my $len = pack( 'c', length( $bytes ) + 3 );
66 my $send = $len . $bytes;
67 my $checksum = cpr_m02_checksum($send);
70 warn "##>> ", as_hex( $send ), "\t\t[$description]\n";
71 $port->write( $send );
75 my $r_len = $port->read(1);
79 if ( $count-- == 0 ) {
80 warn "no response from device";
84 $r_len = $port->read(1);
89 my $data_len = ord($r_len) - 1;
90 my $data = $port->read( $data_len );
91 warn "##<< ", as_hex( $r_len . $data ),"\n";
95 $coderef->( $data ) if $coderef;
106 cpr( 'FF 52 00', 'Boud Rate Detection' );
108 cpr( 'FF 65', 'Get Software Version' );
110 cpr( 'FF 66 00', 'Get Reader Info - General hard and firware' );
112 cpr( 'FF 69', 'RF Reset' );
122 cpr( 'FF B0 01 00', 'ISO - Inventory', sub {
124 if (length($data) < 5 + 2 ) {
125 warn "# no tags in range\n";
129 my $data_sets = ord(substr($data,3,1));
130 $data = substr($data,4);
131 foreach ( 1 .. $data_sets ) {
132 my $tr_type = substr($data,0,1);
133 die "FIXME only TR-TYPE=3 ISO 15693 supported" unless $tr_type eq "\x03";
134 my $dsfid = substr($data,1,1);
135 my $uid = substr($data,2,8);
136 $data = substr($data,10);
137 warn "# TAG $_ ",as_hex( $tr_type, $dsfid, $uid ),$/;
138 push @tags, hex_tag $uid;
143 warn "# tags ",dump(@tags),$/;
148 sub _get_system_info {
153 cpr( "FF B0 2B 01 $tag", "Get System Information $tag", sub {
156 warn "# data ",as_hex($data);
158 return if length($data) < 17;
161 DSFID => substr($data,5-2,1),
162 UID => substr($data,6-2,8),
163 AFI => substr($data,14-2,1),
164 MEM => substr($data,15-2,1),
165 SIZE => substr($data,16-2,1),
166 IC_REF => substr($data,17-2,1),
171 warn "# _get_system_info $tag ",dump( $info );
179 $tag = shift if ref $tag;
181 my $info = _get_system_info $tag;
183 return unless $info->{SIZE};
185 my $max_block = ord($info->{SIZE});
190 while ( $block < $max_block ) {
191 cpr( sprintf("FF B0 23 01 $tag %02x 04", $block), "Read Multiple Blocks $block", sub {
194 my $DB_N = ord substr($data,5-2,1);
195 my $DB_SIZE = ord substr($data,6-2,1);
197 $data = substr($data,7-2,-2);
198 # warn "# DB N: $DB_N SIZE: $DB_SIZE ", as_hex( $data ), " transponder_data: [$transponder_data] ",length($transponder_data),"\n";
199 foreach my $n ( 1 .. $DB_N ) {
200 my $sec = ord(substr($data,0,1));
201 my $db = substr($data,1,$DB_SIZE);
202 warn "## block $n ",dump( $sec, $db ) if $debug;
203 $tag_blocks->{$tag}->[$block+$n-1] = reverse split(//,$db);
204 $data = substr($data, $DB_SIZE + 1);
210 warn "# tag_blocks ",dump($tag_blocks),$/;
217 $tag = shift if ref $tag;
220 $data = join('', @$data) if ref $data eq 'ARRAY';
222 my $DB_ADR = 0; # start at first block
223 my $DB_SIZE = 4; # bytes in one block FIXME this should be read from transponder and not hard-coded
224 if ( my $padding = length($data) % $DB_SIZE ) {
225 warn "WARNING: data block not padded to $DB_SIZE bytes";
226 $data .= "\x00" x $padding;
228 my $DB_N = length($data) / $DB_SIZE;
231 foreach my $block ( 0 .. $DB_N ) {
232 $send_data .= reverse split(//, substr( $data, $block * $DB_SIZE, $DB_SIZE ) );
235 cpr( sprintf("FF B0 24 01 $tag %02x %02x %02x %s", $DB_ADR, $DB_N, $DB_SIZE, as_hex($send_data)), "Write Multiple Blocks $tag", sub {
244 $tag = shift if ref $tag;
246 my $info = _get_system_info $tag;
247 return $info->{AFI} || warn "no AFI for $tag in ",dump($info);
253 $tag = shift if ref $tag;
255 my $afi = shift || die "no afi?";
258 cpr( "FF B0 27 01 $tag $afi", "Write AFI $tag $afi", sub {
260 warn "## write_afi $tag got ",as_hex($data);