8f6a96da6e9ab56b8e393049f0b282198b6f2a73
[Biblio-RFID.git] / lib / Biblio / RFID / Reader / librfid.pm
1 package Biblio::RFID::Reader::librfid;
2
3 use warnings;
4 use strict;
5
6 use base 'Biblio::RFID::Reader::API';
7 use Biblio::RFID;
8
9 use Data::Dump qw(dump);
10
11 =head1 NAME
12
13 Biblio::RFID::Reader::librfid - execute librfid-tool
14
15 =head1 DESCRIPTION
16
17 This is wrapper around C<librfid-tool> from
18
19 L<http://openmrtd.org/projects/librfid/>
20
21 Due to limitation of L<librfid-tool> only
22 L<Biblio::RFID::Reader::API/inventory> and
23 L<Biblio::RFID::Reader::API/read_blocks> is supported.
24
25 This version uses modidified C<librfid-tool> which supports
26 new C<-s> flag which reads sectors 0-3 with C<-k> key and
27 sectors 4-7 with key from C<-s> option.
28
29 However, this code might provide template for integration
30 with any command-line utilities for different RFID readers.
31
32 Currently tested with only with Omnikey CardMan 5321 which
33 has problems. After a while it stops responding to commands
34 by C<librfid-tool> so I provided small C program to reset it:
35
36 C<examples/usbreset.c>
37
38 =cut
39
40 sub serial_settings {} # don't open serial
41
42 sub init { 1 }
43
44 sub _grep_tool {
45         my ( $bin, $param, $coderef, $path ) = @_;
46
47         my $timeout = 3; # s
48
49         eval {
50
51         warn "# _grep_tool $bin $param\n";
52 #       $param .= ' 2>/dev/null';
53         my $pipe_pid = open(my $s, '-|', "$bin $param") || die $!;
54
55         local $SIG{ALRM} = sub {
56                 warn "TIMEOUT!";
57                 kill TERM => $pipe_pid;
58                 die "timeout\n"
59         };
60         alarm $timeout;
61
62         my $sid;
63         my $iso;
64
65         while(<$s>) {
66                 chomp;
67                 warn "## $_\n";
68
69                 if ( m/Layer 2 success.+\(([^\)]+)\).*:\s+(.+)/ ) {
70                         ( $sid, $iso ) = ( $2, $1 );
71                         $sid =~ s/\s*'\s*//g;
72                         my @sid = split(/\s+/, $sid);
73                         @sid = reverse @sid if $iso =~ m/15693/;
74                         $sid = uc join('', @sid);
75                         warn "## sid=[$sid] iso=[$iso]\n";
76                 }
77                 $coderef->( $sid, $iso );
78         }
79
80         alarm(0);
81         close($s);
82
83         }; # eval
84
85         if ( $? >> 8 || $@ ) {
86                 my $lsusb = `lsusb -d 076b:`;
87                 if ( $lsusb =~ m/\S+\s+(\d+)\s+\S+\s+(\d+)/ ) {
88                         my $cmd = "usbreset /dev/bus/usb/$1/$2";
89                         warn "# $cmd\n";
90                         system $cmd;
91                 } else {
92                         warn "can't reset device $lsusb";
93                 }
94         }
95
96 }
97
98 my $sid_iso;
99
100 sub inventory {
101
102         my @tags; 
103         $sid_iso = {};
104         _grep_tool 'librfid-tool', '--scan' => sub {
105                 my ( $sid, $iso ) = @_;
106                 if ( $sid && ! exists $sid_iso->{$sid} ) {
107                         push @tags, $sid;
108                         $sid_iso->{$sid} = $iso;
109                 }
110         };
111         warn "# invetory ",dump(@tags);
112         return @tags;
113 }
114
115 sub tag_type {
116         my ( $self, $tag ) = @_;
117         return $sid_iso->{$tag} =~ m/15693/ ? 'RFID501' : 'SmartX';
118 }
119
120 our $mifare_keys;
121 sub read_mifare_keys {
122         my $key_path = $0;
123         $key_path =~ s{/[^/]+$}{/};
124         $key_path .= "mifare_keys.pl";
125         warn "# $key_path";
126         if ( -e $key_path ) {
127                 require $key_path;
128                 warn "# mifare keys for sectors ", join(' ', keys %$mifare_keys), " loaded\n";
129         }
130 }
131
132 sub read_blocks {
133         my ( $self, $sid ) = @_;
134
135         my $iso = $sid_iso->{$sid};
136         my $blocks;
137
138         if ( $iso =~ m/15693/ ) {
139                 _grep_tool 'librfid-tool', '--read -1' => sub {
140                         $sid ||= shift;
141                         $blocks->{$sid}->[$1] = hex2bytes($2)
142                         if m/block\[\s*(\d+):.+data.+:\s*(.+)/;
143
144                 };
145         } else {
146                 read_mifare_keys unless $mifare_keys;
147
148 =for unmodified mifate-tool
149                 foreach my $sector ( keys %$mifare_keys ) {
150                         my $key = lc $mifare_keys->{$sector};
151                         _grep_tool 'mifare-tool', "-k $key -r $sector" => sub {
152                                 $blocks->{$sid}->[$sector] = hex2bytes($1)
153                                 if m/data=\s*(.+)/;
154                         };
155                 }
156 =cut
157                 _grep_tool 'mifare-tool', "-k " . $mifare_keys->{0} . " -s " . $mifare_keys->{4} => sub {
158                         $blocks->{$sid}->[$1] = hex2bytes($2)
159                         if m/page=(\d+).*data=\s*(.+)/;
160                 };
161         }
162         warn "# read_blocks ",dump($blocks);
163         return $blocks;
164 }
165
166 sub write_blocks {}
167 sub read_afi { -1 }
168 sub write_afi {}
169
170 1