File Coverage

blib/lib/Device/Moose/SCSI.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


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