added timeout to 3s for librfid tools
[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         local $SIG{ALRM} = sub { die "timeout\n" };
52         alarm $timeout;
53
54         warn "# _grep_tool $bin $param\n";
55         open(my $s, '-|', "$bin $param 2>/dev/null") || die $!;
56
57         my $sid;
58         my $iso;
59
60         while(<$s>) {
61                 chomp;
62                 warn "## $_\n";
63
64                 if ( m/Layer 2 success.+\(([^\)]+)\).*:\s+(.+)/ ) {
65                         ( $sid, $iso ) = ( $2, $1 );
66                         $sid =~ s/\s*'\s*//g;
67                         my @sid = split(/\s+/, $sid);
68                         @sid = reverse @sid if $iso =~ m/15693/;
69                         $sid = uc join('', @sid);
70                         warn "## sid=[$sid] iso=[$iso]\n";
71                 }
72                 $coderef->( $sid, $iso );
73         }
74
75         alarm(0);
76         close($s);
77
78         }; # eval
79
80         if ( $? >> 8 || $@ ) {
81                 my $lsusb = `lsusb -d 076b:`;
82                 if ( $lsusb =~ m/\S+\s+(\d+)\s+\S+\s+(\d+)/ ) {
83                         my $cmd = "usbreset /dev/bus/usb/$1/$2";
84                         warn "# $cmd\n";
85                         system $cmd;
86                 } else {
87                         warn "can't reset device $lsusb";
88                 }
89         }
90
91 }
92
93 my $sid_iso;
94
95 sub inventory {
96
97         my @tags; 
98         $sid_iso = {};
99         _grep_tool 'librfid-tool', '--scan' => sub {
100                 my ( $sid, $iso ) = @_;
101                 if ( $sid && ! exists $sid_iso->{$sid} ) {
102                         push @tags, $sid;
103                         $sid_iso->{$sid} = $iso;
104                 }
105         };
106         warn "# invetory ",dump(@tags);
107         return @tags;
108 }
109
110 sub tag_type {
111         my ( $self, $tag ) = @_;
112         return $sid_iso->{$tag} =~ m/15693/ ? 'RFID501' : 'SmartX';
113 }
114
115 our $mifare_keys;
116 sub read_mifare_keys {
117         my $key_path = $0;
118         $key_path =~ s{/[^/]+$}{/};
119         $key_path .= "mifare_keys.pl";
120         warn "# $key_path";
121         if ( -e $key_path ) {
122                 require $key_path;
123                 warn "# mifare keys for sectors ", join(' ', keys %$mifare_keys), " loaded\n";
124         }
125 }
126
127 sub read_blocks {
128         my ( $self, $sid ) = @_;
129
130         my $iso = $sid_iso->{$sid};
131         my $blocks;
132
133         if ( $iso =~ m/15693/ ) {
134                 _grep_tool 'librfid-tool', '--read -1' => sub {
135                         $sid ||= shift;
136                         $blocks->{$sid}->[$1] = hex2bytes($2)
137                         if m/block\[\s*(\d+):.+data.+:\s*(.+)/;
138
139                 };
140         } else {
141                 read_mifare_keys unless $mifare_keys;
142
143 =for unmodified mifate-tool
144                 foreach my $sector ( keys %$mifare_keys ) {
145                         my $key = lc $mifare_keys->{$sector};
146                         _grep_tool 'mifare-tool', "-k $key -r $sector" => sub {
147                                 $blocks->{$sid}->[$sector] = hex2bytes($1)
148                                 if m/data=\s*(.+)/;
149                         };
150                 }
151 =cut
152                 _grep_tool 'mifare-tool', "-k " . $mifare_keys->{0} . " -s " . $mifare_keys->{4} => sub {
153                         $blocks->{$sid}->[$1] = hex2bytes($2)
154                         if m/page=(\d+).*data=\s*(.+)/;
155                 };
156         }
157         warn "# read_blocks ",dump($blocks);
158         return $blocks;
159 }
160
161 sub write_blocks {}
162 sub read_afi { -1 }
163 sub write_afi {}
164
165 1