scsi/scsi/commands/
inquiry.rs

1use byteorder::{ByteOrder, BE};
2use error::{ErrorCause, ScsiError};
3use scsi::commands::{Command, CommandBlockWrapper, Direction};
4use traits::{BufferPullable, BufferPushable};
5
6/// A command to get information about an SCSI device.
7#[derive(Clone, Copy, Eq, PartialEq, Debug)]
8pub struct InquiryCommand {
9    /// The size of the response that should be returned.
10    ///
11    /// Many devices only support an `allocation_length` of 36; other values
12    /// should be used with care.
13    pub allocation_length: u8,
14}
15
16impl Default for InquiryCommand {
17    fn default() -> Self {
18        InquiryCommand::new(36)
19    }
20}
21
22impl InquiryCommand {
23    /// Constructs a new `InquiryCommand` with the given value for
24    /// `allocation_length`.
25    pub fn new(allocation_length: u8) -> InquiryCommand {
26        InquiryCommand { allocation_length }
27    }
28}
29
30impl BufferPushable for InquiryCommand {
31    fn push_to_buffer<B: AsMut<[u8]>>(&self, mut buffer: B) -> Result<usize, ScsiError> {
32        let mut buffer = buffer.as_mut();
33        let cur_idx = self.wrapper().push_to_buffer(&mut buffer)?;
34        buffer[cur_idx] = InquiryCommand::opcode();
35        buffer[cur_idx + 1] = 0;
36        buffer[cur_idx + 2] = 0;
37        buffer[cur_idx + 3] = 0;
38        buffer[cur_idx + 4] = self.allocation_length;
39        Ok(cur_idx + 5)
40    }
41}
42
43impl BufferPullable for InquiryCommand {
44    fn pull_from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Self, ScsiError> {
45        let buffer = buffer.as_ref();
46        let header = CommandBlockWrapper::pull_from_buffer(buffer)?;
47        let opcode = buffer[15];
48        if opcode != InquiryCommand::opcode() {
49            return Err(ScsiError::from_cause(ErrorCause::ParseError));
50        }
51        let allocation_length_with_padding = BE::read_u32(&buffer[16..]);
52
53        let allocation_length = allocation_length_with_padding as u8;
54
55        if !header.data_transfer_length == allocation_length_with_padding
56            || header.direction != Direction::IN
57            || header.cb_length != InquiryCommand::length()
58        {
59            Err(ScsiError::from_cause(ErrorCause::ParseError))
60        } else {
61            Ok(InquiryCommand::new(allocation_length))
62        }
63    }
64}
65
66impl Command for InquiryCommand {
67    fn wrapper(&self) -> CommandBlockWrapper {
68        CommandBlockWrapper::new(
69            u32::from(self.allocation_length),
70            Direction::IN,
71            0,
72            InquiryCommand::length(),
73        )
74    }
75
76    fn opcode() -> u8 {
77        0x12
78    }
79
80    fn length() -> u8 {
81        0x6
82    }
83}
84
85/// The data sent in response to an `InquiryCommand`.
86///
87/// Currently does not include all data that the device responds with; finishing
88/// this is a TODO item.
89#[derive(Clone, Copy, Eq, PartialEq, Debug, Default)]
90pub struct InquiryResponse {
91    /// 3 bit flag set to determine the SCSI device's current accessibility. For
92    /// most common devices, this will be 0 to indicate that the device is accessible
93    /// for running commands.
94    ///
95    /// The flag bits are as follows:
96    ///
97    /// * If the least significant bit, `0x20`, is set, then the "SCSI task router"
98    /// in use is currently unable to access the logical unit specified via the LUN
99    /// field in the CBW.
100    ///
101    /// * If the next bit, `0x40`, is set, then specified LUN cannot be accessed
102    /// by the current "SCSI task router". Note that this flag being set implies
103    /// that the previous bit must also be set, and that the returned value of
104    /// `device_type` is `0x1F` to indicate a value of `Unknown`.
105    ///
106    /// * If the most significant bit, `0x80`, is set, then this flat set must be
107    /// interpretted through the device vendor's documentation instead of the information
108    /// given here; the previous 2 bullets no longer apply.
109    pub device_qualifier: u8,
110
111    /// The type of SCSI device this is. Usually 0 to indicate a
112    /// randomly accessable block storage device.
113    ///
114    /// For valid values, see the [SCSI Peripheral Device Type](https://en.wikipedia.org/wiki/SCSI_Peripheral_Device_Type)
115    /// list.
116    pub device_type: u8,
117
118    /// A flag set whose most significant bit corresponds to whether or not the
119    /// device is removable.
120    ///
121    /// Currently the next bit corresponds to whether or not the logical unit
122    /// is part of a "logical unit conglomerate", while the rest are reserved for
123    /// future use.
124    pub removable_flags: u8,
125
126    /// Indicates what version of the SCSI command set this device conforms to;
127    /// at time of writing, a value of 7 corresponds to adherence to the latest
128    /// version of the command specifications, SPC-5.
129    ///
130    /// A value of 0 indicates that the device does not claim to match *any*
131    /// SCSI specification version; proceed with caution in that case.
132    pub spc_version: u8,
133
134    /// What format the response will be in.
135    ///
136    /// Currently, the only valid value is 2.
137    pub response_format: u8,
138}
139
140impl BufferPullable for InquiryResponse {
141    fn pull_from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<InquiryResponse, ScsiError> {
142        let buffer = buffer.as_ref();
143        let bt = buffer[0];
144        let device_qualifier = bt & 0xe0;
145        let device_type = bt & 0x1f;
146        let removable_flags = buffer[1];
147        let spc_version = buffer[2];
148        let response_format = buffer[3];
149        Ok(InquiryResponse {
150            device_qualifier,
151            device_type,
152            removable_flags,
153            spc_version,
154            response_format,
155        })
156    }
157}
158
159impl BufferPushable for InquiryResponse {
160    fn push_to_buffer<B: AsMut<[u8]>>(&self, mut buffer: B) -> Result<usize, ScsiError> {
161        let buffer = buffer.as_mut();
162        let bt = self.device_qualifier | self.device_type;
163        buffer[0] = bt;
164        buffer[1] = self.removable_flags;
165        buffer[2] = self.spc_version;
166        buffer[3] = self.response_format;
167        Ok(4)
168    }
169}
170#[cfg(test)]
171mod tests {
172    use super::{InquiryCommand, InquiryResponse};
173    use crate::{BufferPullable, BufferPushable};
174
175    #[test]
176    pub fn test_inquirycommand() {
177        let expected: [u8; 31] = [
178            0x55, 0x53, 0x42, 0x43, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x80, 0x00,
179            0x06, 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180            0x00, 0x00, 0x00,
181        ];
182        let mut buff = [0; 32];
183        let inquiry_command = InquiryCommand::new(0x5);
184        let pushed = inquiry_command.push_to_buffer(&mut buff).unwrap();
185        assert_eq!(pushed, 20);
186        assert_eq!(&buff[0..pushed], &expected[0..pushed]);
187
188        let pulled = InquiryCommand::pull_from_buffer(&mut buff).unwrap();
189        assert_eq!(pulled, inquiry_command);
190    }
191    #[test]
192    pub fn test_inquiryresponse() {
193        let expected: [u8; 31] = [
194            0xab, 0x56, 0x78, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x80, 0x00,
195            0x06, 0x12, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196            0x00, 0x00, 0x00,
197        ];
198        let mut buff = [0; 32];
199        let inquiry_command = InquiryResponse {
200            device_qualifier: 0xa0,
201            device_type: 0x0b,
202            removable_flags: 0x56,
203            spc_version: 0x78,
204            response_format: 0x9a,
205        };
206        let pushed = inquiry_command.push_to_buffer(&mut buff).unwrap();
207        assert_eq!(pushed, 4);
208        assert_eq!(&buff[0..pushed], &expected[0..pushed]);
209
210        let pulled = InquiryResponse::pull_from_buffer(&mut buff).unwrap();
211        assert_eq!(pulled, inquiry_command);
212    }
213}