File Coverage

blib/lib/Device/Moose/SCSI.pm
Criterion Covered Total %
statement 15 74 20.2
branch 0 24 0.0
condition 0 6 0.0
subroutine 5 11 45.4
pod 5 5 100.0
total 25 120 20.8


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -c
2              
3             package Device::Moose::SCSI;
4             {
5             $Device::Moose::SCSI::AUTHORITY = "cpan:potatogim";
6             $Device::Moose::SCSI::VERSION = "0.13";
7             };
8              
9 1     1   15589 use Moose;
  1         445903  
  1         8  
10 1     1   7620 use namespace::clean -except => "meta";
  1         2  
  1         57  
11              
12 1     1   242 use Carp;
  1         8  
  1         122  
13 1     1   861 use IO::File;
  1         9110  
  1         123  
14 1     1   7 use Fcntl qw/:mode/;
  1         1  
  1         1082  
15              
16              
17             #-----------------------------------------------------------------------------
18             # Attributes
19             #-----------------------------------------------------------------------------
20             has "fh" =>
21             (
22             is => "ro",
23             isa => "FileHandle",
24             writer => "_set_fh",
25             clearer => "close",
26             );
27              
28             has "name" =>
29             (
30             is => "ro",
31             isa => "Str",
32             writer => "_set_name",
33             );
34              
35             has "devs" =>
36             (
37             is => "ro",
38             isa => "HashRef",
39             builder => "_build_devs",
40             );
41              
42              
43             #-----------------------------------------------------------------------------
44             # Private Methods
45             #-----------------------------------------------------------------------------
46             sub _build_devs
47             {
48 0     0     my $self = shift;
49 0           my %args = @_;
50              
51 0           my $dh = undef;
52              
53 0 0         if (!opendir($dh, "/dev"))
54             {
55 0           carp "Cannot read /dev: $!";
56 0           return undef;
57             }
58              
59 0           my %devs = ();
60              
61 0           foreach my $file (readdir($dh))
62             {
63 0           my @stat = lstat("/dev/$file");
64              
65             # next if stat() failed
66 0 0         next unless (scalar(@stat));
67              
68             # next if file isn't character special or block device
69 0 0 0       next unless (S_ISCHR($stat[2]) || S_ISBLK($stat[2]));
70              
71 0           my $major = int($stat[6] / 256);
72              
73             # major number of /dev/sg* is 21 and /dev/sd* is 8
74 0 0 0       next unless ($major == 21 || $major == 8);
75              
76 0           my $minor = $stat[6] % 256;
77              
78 0           @{$devs{$file}}{qw/name major minor/} = ($file, $major, $minor);
  0            
79             }
80              
81 0           return \%devs;
82             }
83              
84              
85             #-----------------------------------------------------------------------------
86             # Public Methods
87             #-----------------------------------------------------------------------------
88             sub enumerate
89             {
90 0     0 1   my $self = shift;
91 0           my %args = @_;
92              
93 0           return sort { $a cmp $b } keys(%{$self->devs});
  0            
  0            
94             }
95              
96             sub open
97             {
98 0     0 1   my $self = shift;
99 0           my %args = @_;
100              
101 0 0         $self->close() if (defined($self->fh));
102              
103 0 0         if (defined($args{device}))
104             {
105 0           my $fh = IO::File->new("+</dev/$args{device}");
106              
107 0 0         if (!defined($fh))
108             {
109 0           carp "Cannot open $args{device}: $!";
110 0           return -1;
111             }
112              
113 0           $self->_set_fh($fh);
114 0           $self->_set_name($args{device});
115             }
116              
117 0           return 0;
118             }
119              
120             sub execute
121             {
122 0     0 1   my $self = shift;
123 0           my %args = @_;
124              
125 0           my ($command, $wanted, $data) = @args{qw/command wanted data/};
126              
127 0 0         $data = "" unless(defined($data));
128              
129 0 0         my $header = pack ("i4 I x16"
130             , 36 + length($command) + length($data) # int pack_len
131             , 36 + $wanted # int reply_len
132             , 0 # int pack_id
133             , 0 # int result
134             , length($command) == 12 ? 1 : 0); # unsigned int twelve_byte:1
135              
136 0           my $iobuf = $header . $command . $data;
137              
138 0           my $ret = syswrite($self->fh, $iobuf, length($iobuf));
139              
140 0 0         if (!defined($ret))
141             {
142 0           carp "Cannot write to " . $self->name . ": $!";
143 0           return undef;
144             }
145              
146 0           $ret = sysread($self->fh, $iobuf, 36 + $wanted);
147              
148 0 0         if (!defined($ret))
149             {
150 0           carp "Cannot read from " . $self->name . ": $!";
151 0           return undef;
152             }
153              
154 0           my @data = unpack("i4 I C16", substr($iobuf, 0, 36));
155              
156 0           return (substr($iobuf, 36), \@data);
157             }
158              
159             sub inquiry
160             {
161 0     0 1   my $self = shift;
162              
163 0           my ($model, undef) = $self->execute(
164             command => pack("C x3 C x1", 0x12, 0x60)
165             , wanted => 96);
166              
167 0           my ($serial, undef) = $self->execute(
168             command => pack("C3 x1 C x1", 0x12, 0x01, 0x80, 0x60)
169             , wanted => 96);
170              
171 0           my %enq;
172              
173 0           @enq{qw/DEVICE VENDOR PRODUCT REVISION SERIAL/}
174             = (unpack("C x7 A8 A16 A4", $model), $serial);
175              
176 0           return \%enq;
177             }
178              
179              
180             #-----------------------------------------------------------------------------
181             # Life Cycle
182             #-----------------------------------------------------------------------------
183             sub BUILD
184             {
185 0     0 1   my $self = shift;
186 0           my $args = shift;
187              
188 0 0         if (defined($args->{device}))
189             {
190 0           $self->open(device => $args->{device});
191             }
192             }
193              
194             1;
195              
196             __END__
197              
198             =encoding utf8
199              
200             =head1 NAME
201              
202             Device::Moose::SCSI - Reimplementation of Device::SCSI with Moose.
203              
204             =head1 SYNOPSIS
205              
206             use Device::Moose::SCSI;
207              
208             my $device = Device::Moose::SCSI->new(device => "/dev/sg0");
209              
210             # INQUIRY
211             my $inquiry = $device->inquiry();
212              
213             # TESTUNITREADY
214             my ($result, $sense) = $device->execute (
215             command => pack("C x5", 0x00)
216             , wanted => 32
217             );
218              
219             =head1 DESCRIPTION
220              
221             C<Device::Moose::SCSI> reimplementation of Device::SCSI using Moose.
222              
223             See L<Device::SCSI> for detail information.
224              
225             Refer to L<http://www.tldp.org/HOWTO/archived/SCSI-Programming-HOWTO>
226             if you need to know how to SCSI programming with Linux.
227              
228             =head1 ATTRIBUTES
229              
230             =over
231              
232             =item B<fh>
233              
234             =item B<name>
235              
236             =back
237              
238             =head1 METHODS
239              
240             =over
241              
242             =item B<enumerate>
243              
244             =item B<open>
245              
246             =item B<close>
247              
248             =item B<execute>
249              
250             =item B<inquiry>
251              
252             =back
253              
254             =head2 Lifecycle methods
255              
256             =over
257              
258             =item B<BUILD>
259              
260             =back
261              
262             =head1 AUTHOR
263              
264             Ji-Hyeon Gim <potatogim@potatogim.net>
265              
266             =head1 CONTRIBUTORS
267              
268             =head1 COPYRIGHT AND LICENSE
269              
270             Copyright(c) 2015, by Ji-Hyeon Gim <potatogim@potatogim.net>
271              
272             This is free software; you can redistribute it and/or modify it
273             under the same terms as Perl 5 itself at:
274              
275             L<http://www.perlfoundation.org/artistic_license_2_0>
276              
277             You may obtain a copy of the full license at:
278              
279             L<http://www.perl.com/perl/misc/Artistic.html>
280              
281             =head1 SEE ALSO
282              
283             L<Device::SCSI>, L<Moose>
284              
285             =cut