File Coverage

lib/Biblio/RFID/Reader/CPRM02.pm
Criterion Covered Total %
statement 19 137 13.8
branch 0 28 0.0
condition 0 5 0.0
subroutine 7 22 31.8
pod 0 10 0.0
total 26 202 12.8


line stmt bran cond sub pod time code
1             package Biblio::RFID::Reader::CPRM02;
2              
3             =head1 NAME
4              
5             Biblio::RFID::Reader::CPRM02 - support for CPR-M02 RFID reader
6              
7             =head1 DESCRIPTION
8              
9             This module implements serial protocol over usb/serial adapter with CPR-M02
10             reader as described in document C
11              
12             =cut
13              
14 2     2   42901 use warnings;
  2         5  
  2         81  
15 2     2   12 use strict;
  2         4  
  2         75  
16              
17 2     2   12 use base 'Biblio::RFID::Reader::Serial';
  2         3  
  2         675  
18 2     2   522 use Biblio::RFID;
  2         12  
  2         267  
19              
20 2     2   1207 use Time::HiRes;
  2         2162  
  2         13  
21 2     2   212 use Data::Dump qw(dump);
  2         5  
  2         3868  
22              
23             my $debug = 1;
24              
25             sub serial_settings {{
26 1     1 0 10 device => "/dev/ttyUSB0",
27             baudrate => "38400",
28             databits => "8",
29             parity => "even",
30             stopbits => "1",
31             handshake => "none",
32             }}
33              
34             sub cpr_m02_checksum {
35 0     0 0   my $data = shift;
36              
37 0           my $preset = 0xffff;
38 0           my $polynom = 0x8408;
39              
40 0           my $crc = $preset;
41 0           foreach my $i ( 0 .. length($data) - 1 ) {
42 0           $crc ^= ord(substr($data,$i,1));
43 0           for my $j ( 0 .. 7 ) {
44 0 0         if ( $crc & 0x0001 ) {
45 0           $crc = ( $crc >> 1 ) ^ $polynom;
46             } else {
47 0           $crc = $crc >> 1;
48             }
49             }
50             # warn sprintf('%d %04x', $i, $crc & 0xffff);
51             }
52              
53 0           return pack('v', $crc);
54             }
55              
56             sub wait_device {
57 0     0 0   Time::HiRes::sleep 0.010;
58             }
59              
60             our $port;
61              
62             sub cpr {
63 0     0 0   my ( $hex, $description, $coderef ) = @_;
64 0           my $bytes = hex2bytes($hex);
65 0           my $len = pack( 'c', length( $bytes ) + 3 );
66 0           my $send = $len . $bytes;
67 0           my $checksum = cpr_m02_checksum($send);
68 0           $send .= $checksum;
69              
70 0           warn "##>> ", as_hex( $send ), "\t\t[$description]\n";
71 0           $port->write( $send );
72              
73 0           wait_device;
74              
75 0           my $r_len = $port->read(1);
76              
77 0           my $count = 100;
78 0           while ( ! $r_len ) {
79 0 0         if ( $count-- == 0 ) {
80 0           warn "no response from device";
81 0           return;
82             }
83 0           wait_device;
84 0           $r_len = $port->read(1);
85             }
86              
87 0           wait_device;
88              
89 0           my $data_len = ord($r_len) - 1;
90 0           my $data = $port->read( $data_len );
91 0           warn "##<< ", as_hex( $r_len . $data ),"\n";
92              
93 0           wait_device;
94              
95 0 0         $coderef->( $data ) if $coderef;
96              
97             }
98              
99             # FF = COM-ADDR any
100              
101             sub init {
102 0     0 0   my $self = shift;
103              
104 0           $port = $self->port;
105              
106 0           cpr( 'FF 52 00', 'Boud Rate Detection' );
107              
108 0           cpr( 'FF 65', 'Get Software Version' );
109              
110 0           cpr( 'FF 66 00', 'Get Reader Info - General hard and firware' );
111              
112 0           cpr( 'FF 69', 'RF Reset' );
113              
114 0           return 1;
115             }
116              
117              
118             sub inventory {
119              
120 0     0 0   my @tags;
121              
122             cpr( 'FF B0 01 00', 'ISO - Inventory', sub {
123 0     0     my $data = shift;
124 0 0         if (length($data) < 5 + 2 ) {
125 0           warn "# no tags in range\n";
126 0           return;
127             }
128              
129 0           my $data_sets = ord(substr($data,3,1));
130 0           $data = substr($data,4);
131 0           foreach ( 1 .. $data_sets ) {
132 0           my $tr_type = substr($data,0,1);
133 0 0         die "FIXME only TR-TYPE=3 ISO 15693 supported" unless $tr_type eq "\x03";
134 0           my $dsfid = substr($data,1,1);
135 0           my $uid = substr($data,2,8);
136 0           $data = substr($data,10);
137 0           warn "# TAG $_ ",as_hex( $tr_type, $dsfid, $uid ),$/;
138 0           push @tags, hex_tag $uid;
139            
140             }
141 0           });
142              
143 0           warn "# tags ",dump(@tags),$/;
144 0           return @tags;
145             }
146              
147              
148             sub _get_system_info {
149 0     0     my $tag = shift;
150              
151 0           my $info;
152              
153             cpr( "FF B0 2B 01 $tag", "Get System Information $tag", sub {
154 0     0     my $data = shift;
155              
156 0           warn "# data ",as_hex($data);
157              
158 0 0         return if length($data) < 17;
159              
160 0           $info = {
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),
167             };
168              
169 0           });
170              
171 0           warn "# _get_system_info $tag ",dump( $info );
172              
173 0           return $info;
174             }
175              
176              
177             sub read_blocks {
178 0     0 0   my $tag = shift;
179 0 0         $tag = shift if ref $tag;
180              
181 0           my $info = _get_system_info $tag;
182              
183 0 0         return unless $info->{SIZE};
184              
185 0           my $max_block = ord($info->{SIZE});
186              
187 0           my $tag_blocks;
188              
189 0           my $block = 0;
190 0           while ( $block < $max_block ) {
191             cpr( sprintf("FF B0 23 01 $tag %02x 04", $block), "Read Multiple Blocks $block", sub {
192 0     0     my $data = shift;
193              
194 0           my $DB_N = ord substr($data,5-2,1);
195 0           my $DB_SIZE = ord substr($data,6-2,1);
196              
197 0           $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 0           foreach my $n ( 1 .. $DB_N ) {
200 0           my $sec = ord(substr($data,0,1));
201 0           my $db = substr($data,1,$DB_SIZE);
202 0 0         warn "## block $n ",dump( $sec, $db ) if $debug;
203 0           $tag_blocks->{$tag}->[$block+$n-1] = reverse split(//,$db);
204 0           $data = substr($data, $DB_SIZE + 1);
205             }
206 0           });
207 0           $block += 4;
208             }
209              
210 0           warn "# tag_blocks ",dump($tag_blocks),$/;
211 0           return $tag_blocks;
212             }
213              
214              
215             sub write_blocks {
216 0     0 0   my $tag = shift;
217 0 0         $tag = shift if ref $tag;
218              
219 0           my $data = shift;
220 0 0         $data = join('', @$data) if ref $data eq 'ARRAY';
221              
222 0           my $DB_ADR = 0; # start at first block
223 0           my $DB_SIZE = 4; # bytes in one block FIXME this should be read from transponder and not hard-coded
224 0 0         if ( my $padding = length($data) % $DB_SIZE ) {
225 0           warn "WARNING: data block not padded to $DB_SIZE bytes";
226 0           $data .= "\x00" x $padding;
227             }
228 0           my $DB_N = length($data) / $DB_SIZE;
229              
230 0           my $send_data;
231 0           foreach my $block ( 0 .. $DB_N ) {
232 0           $send_data .= reverse split(//, substr( $data, $block * $DB_SIZE, $DB_SIZE ) );
233             }
234              
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 {
236 0     0     my $data = shift;
237 0           warn dump( $data );
238 0           });
239              
240             }
241              
242             sub read_afi {
243 0     0 0   my $tag = shift;
244 0 0         $tag = shift if ref $tag;
245              
246 0           my $info = _get_system_info $tag;
247 0   0       return $info->{AFI} || warn "no AFI for $tag in ",dump($info);
248              
249             }
250              
251             sub write_afi {
252 0     0 0   my $tag = shift;
253 0 0         $tag = shift if ref $tag;
254              
255 0   0       my $afi = shift || die "no afi?";
256 0           $afi = as_hex $afi;
257              
258             cpr( "FF B0 27 01 $tag $afi", "Write AFI $tag $afi", sub {
259 0     0     my $data = shift;
260 0           warn "## write_afi $tag got ",as_hex($data);
261 0           });
262              
263             }
264              
265             1