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