1 /* $Id: scsi-cdb.c,v 1.5 2007/08/25 22:52:18 fredette Exp $ */
2
3 /* scsi/scsi-cdb.c - implementation of generic SCSI device CDB support: */
4
5 /*
6 * Copyright (c) 2003 Matt Fredette
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Matt Fredette.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
27 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 #include <tme/common.h>
37 _TME_RCSID("$Id: scsi-cdb.c,v 1.5 2007/08/25 22:52:18 fredette Exp $");
38
39 /* includes: */
40 #include <tme/scsi/scsi-cdb.h>
41 #include <tme/scsi/scsi-msg.h>
42
43 /* this handles any illegal request: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_illegal)44 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_illegal)
45 {
46 struct tme_scsi_device_sense *sense;
47 int lun;
48
49 /* get the addressed LUN's sense: */
50 lun = scsi_device->tme_scsi_device_addressed_lun;
51 sense = &scsi_device->tme_scsi_device_sense[lun];
52
53 /* if this target does not support extended sense: */
54 if (scsi_device->tme_scsi_device_sense_no_extended) {
55
56 /* the error class and error code: */
57 sense->tme_scsi_device_sense_data[0]
58 = 0x20;
59 sense->tme_scsi_device_sense_data[1]
60 = 0;
61 sense->tme_scsi_device_sense_data[2]
62 = 0;
63 sense->tme_scsi_device_sense_data[3]
64 = 0;
65 sense->tme_scsi_device_sense_valid = 4;
66 }
67
68 /* otherwise, return an extended sense: */
69 else {
70
71 /* the error class and error code: */
72 sense->tme_scsi_device_sense_data[0]
73 = 0x70;
74
75 /* the ILLEGAL REQUEST sense key: */
76 sense->tme_scsi_device_sense_data[2]
77 = 0x05;
78
79 /* the additional sense length: */
80 sense->tme_scsi_device_sense_data[7]
81 = 0x00;
82
83 sense->tme_scsi_device_sense_valid
84 = TRUE;
85 }
86
87 /* return the CHECK CONDITION status: */
88 tme_scsi_device_target_do_smf(scsi_device,
89 TME_SCSI_STATUS_CHECK_CONDITION,
90 TME_SCSI_MSG_CMD_COMPLETE);
91 }
92
93 /* this handles the TEST UNIT READY command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_tur)94 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_tur)
95 {
96 /* finish the command: */
97 tme_scsi_device_target_do_smf(scsi_device,
98 TME_SCSI_STATUS_GOOD,
99 TME_SCSI_MSG_CMD_COMPLETE);
100 }
101
102 /* this handles the REQUEST SENSE command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_request_sense)103 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_device_cdb_request_sense)
104 {
105 int lun;
106 struct tme_scsi_device_sense *sense;
107 unsigned long transfer_length;
108
109 /* get the addressed LUN's sense: */
110 lun = scsi_device->tme_scsi_device_addressed_lun;
111 sense = &scsi_device->tme_scsi_device_sense[lun];
112
113 /* if the sense is not valid: */
114 if (!sense->tme_scsi_device_sense_valid) {
115
116 /* if this device doesn't do extended sense: */
117 if (scsi_device->tme_scsi_device_sense_no_extended) {
118
119 /* conjure up a no-error nonextended sense: */
120 sense->tme_scsi_device_sense_data[0]
121 = 0;
122 sense->tme_scsi_device_sense_data[1]
123 = 0;
124 sense->tme_scsi_device_sense_data[2]
125 = 0;
126 sense->tme_scsi_device_sense_data[3]
127 = 0;
128 sense->tme_scsi_device_sense_valid = 4;
129 }
130
131 /* otherwise, this device does do extended sense: */
132 else {
133
134 /* the error class and error code: */
135 sense->tme_scsi_device_sense_data[0]
136 = 0x70;
137
138 /* the NO SENSE sense key: */
139 sense->tme_scsi_device_sense_data[2]
140 = 0x00;
141
142 /* the additional sense length: */
143 sense->tme_scsi_device_sense_data[7]
144 = 0x00;
145 }
146 }
147
148 /* see how much space for sense bytes the initiator
149 has allocated. zero means four: */
150 transfer_length
151 = scsi_device->tme_scsi_device_cdb[4];
152 if (transfer_length == 0) {
153 transfer_length = 4;
154 }
155
156 /* bound this with the number of sense bytes we have available. an
157 extended sense has a length of 8 plus the additional sense
158 length. any other sense must specify how long it is in the
159 tme_scsi_device_sense_valid field (a standard nonextended sense
160 always has a length of four): */
161 transfer_length
162 = TME_MIN(transfer_length,
163 (((sense->tme_scsi_device_sense_data[0]
164 & 0x70)
165 == 0x70)
166 ? ((unsigned int) 8
167 + sense->tme_scsi_device_sense_data[7])
168 : sense->tme_scsi_device_sense_valid));
169
170 /* set up to transfer the sense: */
171 scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
172 = NULL;
173 scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
174 = &sense->tme_scsi_device_sense_data[0];
175 scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
176 = transfer_length;
177
178 /* the sense is no longer valid: */
179 sense->tme_scsi_device_sense_valid = FALSE;
180
181 /* finish the command: */
182 tme_scsi_device_target_do_dsmf(scsi_device,
183 TME_SCSI_STATUS_GOOD,
184 TME_SCSI_MSG_CMD_COMPLETE);
185 }
186
187 /* this processes the parameter list from a MODE SELECT command: */
188 void
tme_scsi_device_mode_select_data(struct tme_scsi_device * scsi_device,int (* do_mode_select_blocks)_TME_P ((struct tme_scsi_device *,const struct tme_scsi_device_mode_blocks *)),int (* do_mode_select_page)_TME_P ((struct tme_scsi_device *,const tme_uint8_t *)))189 tme_scsi_device_mode_select_data(struct tme_scsi_device *scsi_device,
190 int (*do_mode_select_blocks) _TME_P((struct tme_scsi_device *,
191 const struct tme_scsi_device_mode_blocks *)),
192 int (*do_mode_select_page) _TME_P((struct tme_scsi_device *,
193 const tme_uint8_t *)))
194 {
195 const tme_uint8_t *data;
196 const tme_uint8_t *data_end;
197 tme_uint32_t length;
198 tme_uint32_t block_descriptor_length;
199 struct tme_scsi_device_mode_blocks blocks_buffer;
200 int is_group0_cmd;
201
202 /* see if this is a Group 0 MODE SELECT: */
203 is_group0_cmd
204 = ((scsi_device->tme_scsi_device_cdb[0]
205 & TME_SCSI_CDB_GROUP_MASK)
206 == TME_SCSI_CDB_GROUP_0);
207
208 /* "A parameter list length of zero indicates that no data shall be
209 transferred. This condition shall not be considered as an error." */
210 if (scsi_device->tme_scsi_device_cdb[4] == 0) {
211 tme_scsi_device_target_do_smf(scsi_device,
212 TME_SCSI_STATUS_GOOD,
213 TME_SCSI_MSG_CMD_COMPLETE);
214 return;
215 }
216
217 /* get a pointer to the first byte of data, and a pointer past the
218 last byte of data: */
219 data = &scsi_device->tme_scsi_device_data[0];
220 length = scsi_device->tme_scsi_device_cdb[4];
221 data_end = (data
222 + TME_MIN(sizeof(scsi_device->tme_scsi_device_data),
223 length));
224
225 /* "A parameter list length that results in the truncation of any
226 descriptor, header or page of parameters shall cause the target
227 to terminate the command with CHECK CONDITION status. The sense
228 key shall be set to ILLEGAL REQUEST, and the additional sense
229 code shall be set to PARAMETER LIST LENGTH ERROR. " */
230
231 /* process the mode data: */
232 do {
233
234 /* skip a reserved byte (in a MODE SENSE command, this byte would
235 be the Mode Data Length): */
236 data++;
237
238 /* skip the Medium Type and the Device Specific Parameter: */
239 if ((data_end - data) < 2) break;
240 data += 2;
241
242 /* if this is not the Group 0 command, skip two reserved bytes: */
243 if (!is_group0_cmd) {
244 if ((data_end - data) < 2) break;
245 data += 2;
246 }
247
248 /* get the Block Descriptor Length: */
249 if (data == data_end) break;
250 block_descriptor_length = *(data++);
251 if (!is_group0_cmd) {
252 if (data == data_end) break;
253 block_descriptor_length = (block_descriptor_length << 8) + *(data++);
254 }
255
256 /* check the Block Descriptors: */
257 if (((unsigned long) (data_end - data)) < block_descriptor_length
258 || (block_descriptor_length % 8) != 0) {
259 break;
260 }
261 for (;
262 block_descriptor_length >= 8;
263 block_descriptor_length -= 8) {
264
265 /* get the density code: */
266 blocks_buffer.tme_scsi_device_mode_blocks_density_code = *(data++);
267
268 /* get the number of blocks: */
269 blocks_buffer.tme_scsi_device_mode_blocks_number
270 = ((((tme_uint32_t) data[0]) << 16)
271 | (((tme_uint32_t) data[1]) << 8)
272 | ((tme_uint32_t) data[2]));
273 data += 3;
274
275 /* skip the reserved byte: */
276 data++;
277
278 /* get the block length: */
279 blocks_buffer.tme_scsi_device_mode_blocks_length
280 = ((((tme_uint32_t) data[0]) << 16)
281 | (((tme_uint32_t) data[1]) << 8)
282 | ((tme_uint32_t) data[2]));
283 data += 3;
284
285 /* call back for this blocks descriptor: */
286 if ((*do_mode_select_blocks)(scsi_device, &blocks_buffer)) {
287 return;
288 }
289 }
290
291 /* check the Mode Pages: */
292 for (;
293 ((data_end - data) >= 2
294 && (data_end - data) >= (2 + data[1]));
295 data += (2 + data[1])) {
296
297 /* call back for this Mode Page: */
298 if ((*do_mode_select_page)(scsi_device, data)) {
299 return;
300 }
301 }
302
303 /* terminate the command with GOOD status: */
304 tme_scsi_device_target_do_smf(scsi_device,
305 TME_SCSI_STATUS_GOOD,
306 TME_SCSI_MSG_CMD_COMPLETE);
307 return;
308
309 } while (/* CONSTCOND */ 0);
310
311 /* terminate the command with CHECK CONDITION status: */
312 tme_scsi_device_check_condition(scsi_device,
313 TME_SCSI_SENSE_EXT_KEY_ILLEGAL_REQUEST,
314 TME_SCSI_SENSE_EXT_ASC_ASCQ_PARAMETER_LIST_LENGTH_ERROR);
315 }
316
317 /* this adds one of the inquiry strings to the data: */
318 static tme_uint8_t *
_tme_scsi_device_make_inquiry_string(tme_uint8_t * data,const char * string,unsigned int size)319 _tme_scsi_device_make_inquiry_string(tme_uint8_t *data,
320 const char *string,
321 unsigned int size)
322 {
323 tme_uint8_t c;
324
325 for (; size-- > 0; ) {
326 c = *(string++);
327 if (c == '\0') {
328 c = ' ';
329 string--;
330 }
331 *(data++) = c;
332 }
333 return (data);
334 }
335
336 /* this creates INQUIRY data: */
337 tme_uint8_t *
tme_scsi_device_make_inquiry_data(struct tme_scsi_device * scsi_device,const struct tme_scsi_device_inquiry * inquiry)338 tme_scsi_device_make_inquiry_data(struct tme_scsi_device *scsi_device,
339 const struct tme_scsi_device_inquiry *inquiry)
340 {
341 tme_uint8_t *data;
342
343 data = &scsi_device->tme_scsi_device_data[0];
344 scsi_device->tme_scsi_device_dma.tme_scsi_dma_out = data;
345 scsi_device->tme_scsi_device_dma.tme_scsi_dma_in = NULL;
346
347 /* byte 0 is the peripheral device type: */
348 *(data++)
349 = (inquiry->tme_scsi_device_inquiry_type
350 | inquiry->tme_scsi_device_inquiry_lun_state);
351
352 /* byte 1 is the device type qualifier: */
353 *(data++)
354 = (inquiry->tme_scsi_device_inquiry_type_qualifier
355 | (inquiry->tme_scsi_device_inquiry_lun_removable
356 ? 0x80
357 : 0x00));
358
359 /* byte 2 is the standards versions: */
360 *(data++)
361 = ((inquiry->tme_scsi_device_inquiry_std_iso << 6)
362 | (inquiry->tme_scsi_device_inquiry_std_ecma << 3)
363 | (inquiry->tme_scsi_device_inquiry_std_iso << 0));
364
365 /* byte 3 is the response format: */
366 *(data++)
367 = inquiry->tme_scsi_device_response_format;
368
369 /* byte 4 is the additional length. we will come back and
370 fill it later: */
371 data++;
372
373 /* bytes 5, 6, and 7 are flags, that we initialize to zero: */
374 *(data++) = 0x00;
375 *(data++) = 0x00;
376 *(data++) = 0x00;
377
378 /* the next eight bytes are for the vendor: */
379 data
380 = _tme_scsi_device_make_inquiry_string(data,
381 scsi_device->tme_scsi_device_vendor,
382 8);
383
384 /* the next 16 bytes are for the product: */
385 data
386 = _tme_scsi_device_make_inquiry_string(data,
387 scsi_device->tme_scsi_device_product,
388 16);
389
390 /* the next four bytes are for the revision: */
391 data
392 = _tme_scsi_device_make_inquiry_string(data,
393 scsi_device->tme_scsi_device_revision,
394 4);
395
396 /* fill in the additional length byte: */
397 scsi_device->tme_scsi_device_data[4]
398 = (data
399 - &scsi_device->tme_scsi_device_data[5]);
400
401 return (data);
402 }
403