1 /* $Id: emulexmt02.c,v 1.4 2007/08/25 22:54:59 fredette Exp $ */
2 
3 /* scsi/emulexmt02.c - Emulex MT-02 SCSI tape emulation: */
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: emulexmt02.c,v 1.4 2007/08/25 22:54:59 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/scsi/scsi-tape.h>
41 
42 /* macros: */
43 
44 /* the Emulex MT-02 is a SCSI<->QIC translator, and being QIC,
45    it only supports a 512 byte block size: */
46 #define TME_EMULEXMT02_BLOCK_SIZE	(512)
47 
48 /* types: */
49 
50 /* the Emulex MT-02 returns extended sense plus some vendor-specific
51    additional bytes: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_request_sense)52 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_request_sense)
53 {
54   int lun;
55   struct tme_scsi_device_sense *sense;
56   tme_uint8_t transfer_length;
57   tme_uint8_t error;
58 
59   /* get the addressed LUN: */
60   lun = scsi_device->tme_scsi_device_addressed_lun;
61 
62   /* get the sense: */
63   sense = &scsi_device->tme_scsi_device_sense[lun];
64 
65   /* see how much space for sense bytes the initiator
66      has allocated.  zero means four: */
67   transfer_length
68     = scsi_device->tme_scsi_device_cdb[4];
69   if (transfer_length == 0) {
70     transfer_length = 4;
71   }
72 
73   /* the sun2 PROM insists that the fifth byte of the sense
74      have its least significant bit set: */
75   sense->tme_scsi_device_sense_data[4] |= 0x01;
76 
77   /* the Emulex MT-02 returns eight additional sense bytes: */
78   sense->tme_scsi_device_sense_data[7]
79     = 0x08;
80 
81   /* the Emulex error code.  get this by dispatching on the
82      generic sense key: */
83   switch (sense->tme_scsi_device_sense_data[2] & 0xf) {
84   case 0x0: /* NO SENSE */
85     /* EOM: */
86     if (sense->tme_scsi_device_sense_data[2] & 0x40) {
87       error = 0x34; /* END OF MEDIA */
88     }
89     /* ILI: */
90     else if (sense->tme_scsi_device_sense_data[2] & 0x20) {
91       error = 0x19; /* BAD BLOCK */
92     }
93     else if (sense->tme_scsi_device_sense_data[2] & 0x80) {
94       error = 0x1c; /* FILE MARK */
95     }
96     else {
97       error = 0x00; /* NO SENSE */
98     }
99     break;
100   case 0x1: error = 0x18; break; /* RECOVERED ERROR */
101   case 0x2: error = 0x04; break; /* NOT READY */
102   case 0x3: error = 0x11; break; /* MEDIA ERROR */
103   case 0x4: error = 0x0b; break; /* HARDWARE ERROR */
104   case 0x5: error = 0x20; break; /* INVALID COMMAND */
105   case 0x6: error = 0x30; break; /* UNIT ATTENTION */
106   case 0x7: error = 0x17; break; /* DATA PROTECT */
107   case 0x8: error = 0x19; break; /* BAD BLOCK */
108   case 0xd: error = 0x14; break; /* BLOCK NOT FOUND */
109   default: abort();
110   }
111   sense->tme_scsi_device_sense_data[8] = error;
112 
113   /* the Emulex retry count: */
114   sense->tme_scsi_device_sense_data[9] = 0x00;
115   sense->tme_scsi_device_sense_data[10] = 0x10;
116 
117   /* call the normal REQUEST SENSE handler: */
118   tme_scsi_device_cdb_request_sense(scsi_device,
119 				    control_old,
120 				    control_new);
121 }
122 
123 /* the Emulex MT-02 returns an all-bits-zero INQUIRY response: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_inquiry)124 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_inquiry)
125 {
126   int lun;
127 
128   /* get the addressed LUN: */
129   lun = scsi_device->tme_scsi_device_addressed_lun;
130 
131   /* return the INQUIRY data: */
132   memset(scsi_device->tme_scsi_device_data,
133 	 0,
134 	 sizeof(scsi_device->tme_scsi_device_data));
135 
136   /* The SunOS/sun3 4.1.1 tape bootblock will refuse to boot off of a
137      SCSI device unless the INQUIRY response indicates a tape (so
138      apparently it's impossible to install SunOS 4.1.1 on a real Sun3
139      from an Emulex tape).
140 
141      Because we're lazy and don't want to implement tme-scsi-1 tape
142      yet, we just indicate that we're a tape.
143 
144      However, this has some ramifications for NetBSD/sun2 1.6*, which
145      was rigged to specially match an all-bits-zero INQUIRY for an
146      Emulex tape.  To work around this, when running NetBSD/sun2 or
147      NetBSD/sun3 in the emulator, specify vendor EMULEX product "MT-02
148      QIC" in the tmesh configuration file, which will cause a regular,
149      full INQUIRY response to be sent, but with the specific vendor
150      and product so the right quirk entry gets matched: */
151   scsi_device->tme_scsi_device_data[0] = TME_SCSI_TYPE_TAPE;
152 
153   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
154     = 5;
155   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
156     = scsi_device->tme_scsi_device_data;
157   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
158     = NULL;
159 
160   /* return the data and the GOOD status: */
161   tme_scsi_device_target_do_dsmf(scsi_device,
162 				 TME_SCSI_STATUS_GOOD,
163 				 TME_SCSI_MSG_CMD_COMPLETE);
164 }
165 
166 /* the Emulex MT-02 behaves as if the "Fixed" bit is always set in
167    its READ and WRITE CDBs: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_read0)168 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_read0)
169 {
170   scsi_device->tme_scsi_device_cdb[1] |= 0x01;
171   tme_scsi_tape_cdb_xfer0(scsi_device, TRUE);
172 }
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_write0)173 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_write0)
174 {
175   scsi_device->tme_scsi_device_cdb[1] |= 0x01;
176   tme_scsi_tape_cdb_xfer0(scsi_device, FALSE);
177 }
178 
179 /* the Emulex MT-02 MODE SENSE command: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_mode_sense)180 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_mode_sense)
181 {
182   tme_uint8_t *data;
183   tme_uint32_t blocks, block_size;
184   int lun;
185 
186   /* get the addressed LUN: */
187   lun = scsi_device->tme_scsi_device_addressed_lun;
188 
189   data = &scsi_device->tme_scsi_device_data[0];
190 
191   /* the sense data length.  we will fill this in later: */
192   data++;
193 
194   /* byte 1 is the medium type: */
195   *(data++) = 0x00; /* default (only one medium type supported) */
196 
197   /* byte 2 is the WP (Write Protect), Buffered Mode, and Speed: */
198   *(data++) = 0x80; /* write protected, unbuffered, default speed */
199 
200   /* byte 3 is the Block Descriptor Length.  we will fill this in
201      later: */
202   data++;
203 
204   /* the first Block Descriptor: */
205 
206   /* the Block Descriptor density code: */
207   *(data++) = 0x05; /* QIC-24 */
208 
209   /* the Number of Blocks.  we assume a 60MB tape: */
210   block_size = TME_EMULEXMT02_BLOCK_SIZE;
211   blocks = (60 * 1024 * 1024) / block_size;
212   *(data++) = (blocks >> 16) & 0xff;
213   *(data++) = (blocks >>  8) & 0xff;
214   *(data++) = (blocks >>  0) & 0xff;
215 
216   /* a reserved byte: */
217   data++;
218 
219   /* the Block Length: */
220   *(data++) = (block_size >> 16) & 0xff;
221   *(data++) = (block_size >>  8) & 0xff;
222   *(data++) = (block_size >>  0) & 0xff;
223 
224   /* fill in the Block Descriptor Length: */
225   scsi_device->tme_scsi_device_data[3]
226     = (data - &scsi_device->tme_scsi_device_data[4]);
227 
228   /* there are no vendor-unique bytes: */
229 
230   /* fill in the sense data length: */
231   scsi_device->tme_scsi_device_data[0]
232     = (data - &scsi_device->tme_scsi_device_data[1]);
233 
234   /* set the DMA pointer and length: */
235   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
236     = TME_MIN((data
237 	       - &scsi_device->tme_scsi_device_data[0]),
238 	      scsi_device->tme_scsi_device_cdb[4]);
239   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
240     = &scsi_device->tme_scsi_device_data[0];
241   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
242     = NULL;
243 
244   /* finish the command: */
245   tme_scsi_device_target_do_dsmf(scsi_device,
246 				 TME_SCSI_STATUS_GOOD,
247 				 TME_SCSI_MSG_CMD_COMPLETE);
248 }
249 
250 /* the Emulex MT-02 MODE SELECT command: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_mode_select)251 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_mode_select)
252 {
253 
254   /* read in the parameter list: */
255   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
256     = scsi_device->tme_scsi_device_cdb[4];
257   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
258     = TME_MIN(scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
259 	      sizeof(scsi_device->tme_scsi_device_data));
260   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
261     = &scsi_device->tme_scsi_device_data[0];
262   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
263     = NULL;
264 
265   /* XXX for now we discard the parameter list after reading it in: */
266   tme_scsi_device_target_do_dsmf(scsi_device,
267 				 TME_SCSI_STATUS_GOOD,
268 				 TME_SCSI_MSG_CMD_COMPLETE);
269 }
270 
271 /* this vendor-unique command (0xd) puts us into qic02 mode: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_qic02)272 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_qic02)
273 {
274   tme_scsi_device_target_do_smf(scsi_device,
275 				TME_SCSI_STATUS_GOOD,
276 				TME_SCSI_MSG_CMD_COMPLETE);
277 }
278 
279 /* this implements the tape READ BLOCK LIMITS command: */
_TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_block_limits)280 static _TME_SCSI_DEVICE_CDB_DECL(_tme_emulexmt02_cdb_block_limits)
281 {
282   tme_uint8_t *data;
283   int lun;
284 
285   /* get the addressed LUN: */
286   lun = scsi_device->tme_scsi_device_addressed_lun;
287 
288   data = &scsi_device->tme_scsi_device_data[0];
289 
290   /* a reserved byte: */
291   data++;
292 
293   /* the Maximum Block Length: */
294   *(data++) = (TME_EMULEXMT02_BLOCK_SIZE >> 16) & 0xff;
295   *(data++) = (TME_EMULEXMT02_BLOCK_SIZE >>  8) & 0xff;
296   *(data++) = (TME_EMULEXMT02_BLOCK_SIZE >>  0) & 0xff;
297 
298   /* the Minimum Block Length: */
299   *(data++) = (TME_EMULEXMT02_BLOCK_SIZE >>  8) & 0xff;
300   *(data++) = (TME_EMULEXMT02_BLOCK_SIZE >>  0) & 0xff;
301 
302   /* set the DMA pointer and length: */
303   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
304     = (data
305        - &scsi_device->tme_scsi_device_data[0]);
306   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
307     = &scsi_device->tme_scsi_device_data[0];
308   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
309     = NULL;
310 
311   /* finish the command: */
312   tme_scsi_device_target_do_dsmf(scsi_device,
313 				 TME_SCSI_STATUS_GOOD,
314 				 TME_SCSI_MSG_CMD_COMPLETE);
315 }
316 
317 /* this enforces the block size at tape connection time: */
318 static void
_tme_emulexmt02_connected(struct tme_scsi_tape * scsi_tape,int lun)319 _tme_emulexmt02_connected(struct tme_scsi_tape *scsi_tape,
320 			  int lun)
321 {
322   struct tme_scsi_tape_connection *conn_scsi_tape;
323   struct tme_tape_connection *conn_tape;
324   unsigned long sizes[3];
325   int rc;
326 
327   /* get the tape connection: */
328   conn_scsi_tape
329     = scsi_tape->tme_scsi_tape_connections[lun];
330   conn_tape
331     = ((struct tme_tape_connection *)
332        conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
333 
334   /* set the block size: */
335   sizes[0] = TME_EMULEXMT02_BLOCK_SIZE;
336   sizes[1] = TME_EMULEXMT02_BLOCK_SIZE;
337   sizes[2] = TME_EMULEXMT02_BLOCK_SIZE;
338   rc
339     = ((*conn_tape->tme_tape_connection_control)
340        (conn_tape,
341 	TME_TAPE_CONTROL_BLOCK_SIZE_SET,
342 	sizes));
343   assert (rc == TME_OK);
344 }
345 
346 /* this initializes Emulex MT-02 SCSI tape emulation: */
347 int
tme_scsi_tape_emulexmt02_init(struct tme_scsi_tape * scsi_tape)348 tme_scsi_tape_emulexmt02_init(struct tme_scsi_tape *scsi_tape)
349 {
350   struct tme_scsi_device *scsi_device;
351 
352   scsi_device = &scsi_tape->tme_scsi_tape_device;
353 
354   /* Emulex MT-02 boards don't really support the INQUIRY command: */
355   /* XXX FIXME - this is a hack.  if the user has specified EMULEX
356      for the vendor name, don't override the INQUIRY handling: */
357   if (strcmp(scsi_device->tme_scsi_device_vendor, "EMULEX")) {
358     TME_SCSI_DEVICE_DO_CDB(scsi_device,
359 			   TME_SCSI_CDB_INQUIRY,
360 			   _tme_emulexmt02_cdb_inquiry);
361   }
362 
363   /* our type-specific connection function: */
364   scsi_tape->tme_scsi_tape_connected
365     = _tme_emulexmt02_connected;
366 
367   /* our block sizes: */
368   scsi_tape->tme_scsi_tape_block_size_min = TME_EMULEXMT02_BLOCK_SIZE;
369   scsi_tape->tme_scsi_tape_block_size_max = TME_EMULEXMT02_BLOCK_SIZE;
370   scsi_tape->tme_scsi_tape_block_size_current = TME_EMULEXMT02_BLOCK_SIZE;
371 
372   /* our type-specific CDB functions: */
373   TME_SCSI_DEVICE_DO_CDB(scsi_device,
374 			 0x0d,
375 			 _tme_emulexmt02_cdb_qic02);
376   TME_SCSI_DEVICE_DO_CDB(scsi_device,
377 			 TME_SCSI_CDB_TAPE_READ0,
378 			 _tme_emulexmt02_cdb_read0);
379   TME_SCSI_DEVICE_DO_CDB(scsi_device,
380 			 TME_SCSI_CDB_TAPE_WRITE0,
381 			 _tme_emulexmt02_cdb_write0);
382   TME_SCSI_DEVICE_DO_CDB(scsi_device,
383 			 TME_SCSI_CDB_TAPE_MODE_SENSE,
384 			 _tme_emulexmt02_cdb_mode_sense);
385   TME_SCSI_DEVICE_DO_CDB(scsi_device,
386 			 TME_SCSI_CDB_TAPE_MODE_SELECT,
387 			 _tme_emulexmt02_cdb_mode_select);
388   TME_SCSI_DEVICE_DO_CDB(scsi_device,
389 			 TME_SCSI_CDB_TAPE_BLOCK_LIMITS,
390 			 _tme_emulexmt02_cdb_block_limits);
391   TME_SCSI_DEVICE_DO_CDB(scsi_device,
392 			 TME_SCSI_CDB_REQUEST_SENSE,
393 			 _tme_emulexmt02_cdb_request_sense);
394   TME_SCSI_DEVICE_DO_CDB(scsi_device,
395 			 TME_SCSI_CDB_TAPE_RESERVE,
396 			 tme_scsi_device_cdb_illegal);
397 
398   return (TME_OK);
399 }
400