1 /* $Id: scsi-tape.c,v 1.8 2007/09/06 23:19:06 fredette Exp $ */
2 
3 /* scsi/scsi-tape.c - implementation of 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: scsi-tape.c,v 1.8 2007/09/06 23:19:06 fredette Exp $");
38 
39 /* includes: */
40 #include <tme/scsi/scsi-tape.h>
41 #ifdef HAVE_STDARG_H
42 #include <stdarg.h>
43 #else  /* HAVE_STDARG_H */
44 #include <varargs.h>
45 #endif /* HAVE_STDARG_H */
46 
47 /* macros: */
48 
49 /* globals: */
50 
51 /* the list of tapes that we emulate: */
52 const struct {
53 
54   /* the type name: */
55   const char *_tme_scsi_tape_list_type;
56 
57   /* the initialization function: */
58   int (*_tme_scsi_tape_list_init) _TME_P((struct tme_scsi_tape *));
59 } _tme_scsi_tape_list[] = {
60 
61   /* the generic TME SCSI-1 tape: */
62   { "tme-scsi-1", tme_scsi_tape_tme_init },
63 
64   /* the Emulex MT02 emulation: */
65   { "emulex-mt02", tme_scsi_tape_emulexmt02_init },
66 };
67 
68 /* this is the LUN addresser for LUN-aware tape devices: */
69 int
tme_scsi_tape_address_lun_aware(struct tme_scsi_device * scsi_device)70 tme_scsi_tape_address_lun_aware(struct tme_scsi_device *scsi_device)
71 {
72   struct tme_scsi_tape *scsi_tape;
73   struct tme_scsi_tape_connection *conn_scsi_tape;
74   struct tme_scsi_device_sense *sense;
75   int lun;
76 
77   /* recover our data structure: */
78   scsi_tape = (struct tme_scsi_tape *) scsi_device;
79 
80   /* if an IDENTIFY message was sent, use that LUN: */
81   lun = scsi_device->tme_scsi_device_addressed_lun;
82 
83   /* otherwise, get the LUN from bits 5-7 of the second
84      CDB byte: */
85   if (lun < 0) {
86     lun = (scsi_device->tme_scsi_device_cdb[1] >> 5);
87     scsi_device->tme_scsi_device_addressed_lun = lun;
88   }
89 
90   /* get this LUN's tape connection: */
91   conn_scsi_tape = scsi_tape->tme_scsi_tape_connections[lun];
92 
93   /* if this LUN is not defined, and this isn't a REQUEST SENSE
94      command: */
95   if (!(scsi_device->tme_scsi_device_luns
96 	& TME_BIT(lun))
97       && (scsi_device->tme_scsi_device_cdb[0]
98 	  != TME_SCSI_CDB_REQUEST_SENSE)) {
99 
100     /* form the ILLEGAL REQUEST sense: */
101     sense = &scsi_device->tme_scsi_device_sense[lun];
102     sense->tme_scsi_device_sense_data[2] = 0x05;
103   }
104 
105   /* otherwise, this LUN is defined.  an INQUIRY or REQUEST SENSE
106      command is always allowed for a defined LUN: */
107   else if ((scsi_device->tme_scsi_device_cdb[0]
108 	    == TME_SCSI_CDB_INQUIRY)
109 	   || (scsi_device->tme_scsi_device_cdb[0]
110 	       == TME_SCSI_CDB_REQUEST_SENSE)) {
111     sense = NULL;
112   }
113 
114   /* otherwise, if the tape at this LUN has an attention condition: */
115   else if (conn_scsi_tape->tme_scsi_tape_connection_flags
116 	   & TME_SCSI_TAPE_FLAG_ATTENTION) {
117 
118     /* clear the attention condition: */
119     conn_scsi_tape->tme_scsi_tape_connection_flags
120       &= ~TME_SCSI_TAPE_FLAG_ATTENTION;
121 
122     /* form the UNIT ATTENTION sense: */
123     sense = &scsi_device->tme_scsi_device_sense[lun];
124     sense->tme_scsi_device_sense_data[2] = 0x06;
125   }
126 
127   /* otherwise, if the tape at this LUN is not loaded: */
128   else if (!(conn_scsi_tape->tme_scsi_tape_connection_flags
129 	     & TME_SCSI_TAPE_FLAG_LOADED)) {
130 
131     /* form the NOT READY sense: */
132     sense = &scsi_device->tme_scsi_device_sense[lun];
133     sense->tme_scsi_device_sense_data[2] = 0x02;
134   }
135 
136   /* otherwise, this command is okay: */
137   else {
138     sense = NULL;
139   }
140 
141   /* if addressing this LUN caused some sense: */
142   if (sense != NULL) {
143 
144     /* this target must support extended sense: */
145     assert (!scsi_device->tme_scsi_device_sense_no_extended);
146 
147     /* the error class and error code: */
148     sense->tme_scsi_device_sense_data[0]
149       = 0x70;
150 
151     /* the additional sense length: */
152     sense->tme_scsi_device_sense_data[7]
153       = 0x00;
154 
155     sense->tme_scsi_device_sense_valid
156       = TRUE;
157 
158     /* return the CHECK CONDITION status: */
159     tme_scsi_device_target_do_smf(scsi_device,
160 				  TME_SCSI_STATUS_CHECK_CONDITION,
161 				  TME_SCSI_MSG_CMD_COMPLETE);
162     return (EINVAL);
163   }
164 
165   return (TME_OK);
166 }
167 
168 /* this is the LUN addresser for LUN-unaware devices: */
169 int
tme_scsi_tape_address_lun_unaware(struct tme_scsi_device * scsi_device)170 tme_scsi_tape_address_lun_unaware(struct tme_scsi_device *scsi_device)
171 {
172 
173   /* we always force a LUN of zero: */
174   scsi_device->tme_scsi_device_addressed_lun = 0;
175 
176   return (tme_scsi_tape_address_lun_aware(scsi_device));
177 }
178 
179 /* this determines the status of a tape READ or WRITE command: */
180 tme_uint8_t
tme_scsi_tape_xfer_status(struct tme_scsi_tape * scsi_tape,int flags,unsigned long count_xfer_got)181 tme_scsi_tape_xfer_status(struct tme_scsi_tape *scsi_tape,
182 			  int flags,
183 			  unsigned long count_xfer_got)
184 {
185   int lun;
186   tme_uint8_t *cdb;
187   tme_uint8_t status;
188   unsigned long count_xfer_wanted;
189   struct tme_scsi_device_sense *sense;
190 
191   /* assume that this command completed successfully: */
192   status = TME_SCSI_STATUS_GOOD;
193 
194   /* if there are some tape check condition flags: */
195   if (flags & ~TME_TAPE_FLAG_FIXED) {
196 
197     /* there is a condition to check: */
198     status = TME_SCSI_STATUS_CHECK_CONDITION;
199 
200     /* get the addressed LUN: */
201     lun = scsi_tape->tme_scsi_tape_device.tme_scsi_device_addressed_lun;
202 
203     /* get the original transfer length: */
204     cdb = &scsi_tape->tme_scsi_tape_device.tme_scsi_device_cdb[0];
205     count_xfer_wanted = cdb[2];
206     count_xfer_wanted = (count_xfer_wanted << 8) | cdb[3];
207     count_xfer_wanted = (count_xfer_wanted << 8) | cdb[4];
208 
209     /* set the sense: */
210     sense
211       = &scsi_tape->tme_scsi_tape_device.tme_scsi_device_sense[lun];
212 
213     /* the Valid, Error Class, and Error Code values: */
214     sense->tme_scsi_device_sense_data[0]
215       = (0x80 | 0x70);
216 
217     /* the Filemark, EOM, ILI, and Sense Key (NO SENSE) values: */
218     sense->tme_scsi_device_sense_data[2]
219       = (((flags & TME_TAPE_FLAG_MARK)
220 	  ? 0x80
221 	  : 0x00)
222 	 | ((flags & TME_TAPE_FLAG_EOM)
223 	    ? 0x40
224 	    : 0x00)
225 	 | ((flags & TME_TAPE_FLAG_ILI)
226 	    ? 0x20
227 	    : 0x00)
228 	 | 0x00);
229 
230     /* set the Information Bytes (for a tape, the residue): */
231     count_xfer_wanted -= count_xfer_got;
232     sense->tme_scsi_device_sense_data[3]
233       = (count_xfer_wanted >> 24) & 0xff;
234     sense->tme_scsi_device_sense_data[4]
235       = (count_xfer_wanted >> 16) & 0xff;
236     sense->tme_scsi_device_sense_data[5]
237       = (count_xfer_wanted >>  8) & 0xff;
238     sense->tme_scsi_device_sense_data[6]
239       = (count_xfer_wanted >>  0) & 0xff;
240 
241     /* there are no additional bytes: */
242     sense->tme_scsi_device_sense_data[7]
243       = 0x00;
244 
245     /* this sense is valid: */
246     sense->tme_scsi_device_sense_valid
247       = TRUE;
248   }
249 
250   /* done: */
251   return (status);
252 }
253 
254 /* this finishes a WRITE command: */
_TME_SCSI_DEVICE_PHASE_DECL(tme_scsi_tape_target_do_write)255 _TME_SCSI_DEVICE_PHASE_DECL(tme_scsi_tape_target_do_write)
256 {
257   struct tme_scsi_tape *scsi_tape;
258   struct tme_scsi_tape_connection *conn_scsi_tape;
259   struct tme_tape_connection *conn_tape;
260   int lun;
261   unsigned long count;
262   int flags;
263   tme_uint8_t status;
264   int rc;
265 
266   /* recover our tape: */
267   scsi_tape = (struct tme_scsi_tape *) scsi_device;
268 
269   /* get the addressed LUN: */
270   lun = scsi_device->tme_scsi_device_addressed_lun;
271 
272   /* get the tape connection: */
273   conn_scsi_tape
274     = scsi_tape->tme_scsi_tape_connections[lun];
275   conn_tape
276     = ((struct tme_tape_connection *)
277        conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
278 
279   /* release the buffer: */
280   rc
281     = ((*conn_tape->tme_tape_connection_release)
282        (conn_tape,
283 	&flags,
284 	&count));
285   assert (rc == TME_OK);
286 
287   /* get the status: */
288   status
289     = ((*scsi_tape->tme_scsi_tape_xfer_status)
290        (scsi_tape,
291 	flags,
292 	count));;
293 
294   /* finish the command: */
295   tme_scsi_device_target_do_smf(scsi_device,
296 				status,
297 				TME_SCSI_MSG_CMD_COMPLETE);
298 }
299 
300 /* this implements the tape Group 0 READ and WRITE commands: */
301 void
tme_scsi_tape_cdb_xfer0(struct tme_scsi_device * scsi_device,int read)302 tme_scsi_tape_cdb_xfer0(struct tme_scsi_device *scsi_device,
303 			int read)
304 {
305   struct tme_scsi_tape *scsi_tape;
306   struct tme_scsi_tape_connection *conn_scsi_tape;
307   struct tme_tape_connection *conn_tape;
308   int lun;
309   tme_uint8_t *cdb;
310   unsigned long count_xfer;
311   unsigned int bytes_xfer;
312   int flags;
313   tme_uint8_t status;
314   int rc;
315 
316   /* recover our tape: */
317   scsi_tape = (struct tme_scsi_tape *) scsi_device;
318 
319   /* get the addressed LUN: */
320   lun = scsi_device->tme_scsi_device_addressed_lun;
321 
322   /* get the tape connection: */
323   conn_scsi_tape
324     = scsi_tape->tme_scsi_tape_connections[lun];
325   conn_tape
326     = ((struct tme_tape_connection *)
327        conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
328 
329   cdb = &scsi_device->tme_scsi_device_cdb[0];
330 
331   /* get the fixed bit: */
332   flags = (cdb[1] & 0x01) * TME_TAPE_FLAG_FIXED;
333 
334   /* get the transfer length: */
335   count_xfer = cdb[2];
336   count_xfer = (count_xfer << 8) | cdb[3];
337   count_xfer = (count_xfer << 8) | cdb[4];
338   bytes_xfer = count_xfer;
339   if (flags & TME_TAPE_FLAG_FIXED) {
340     bytes_xfer *= scsi_tape->tme_scsi_tape_block_size_current;
341   }
342 
343   /* if this is a read: */
344   if (read) {
345 
346     /* get the tape buffer: */
347     rc
348       = ((*conn_tape->tme_tape_connection_read)
349 	 (conn_tape,
350 	  &flags,
351 	  &count_xfer,
352 	  &scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
353 	  &scsi_device->tme_scsi_device_dma.tme_scsi_dma_out));
354     scsi_device->tme_scsi_device_dma.tme_scsi_dma_in = NULL;
355 
356     /* XXX FIXME - this is a big hack.  the tme_tape_connection_read
357        function always reads one or more whole tape blocks, but we
358        call it for every READ CDB, even when the initiator hasn't set
359        a block size with MODE SELECT (which allows the initiator to
360        issue reads without regard to block size - reads smaller than
361        the tape block size don't discard the remainder of a block and
362        advance to the next tape block, but instead just return
363        successive parts of the same tape block).
364 
365        this entire function needs to be rewritten, to handle this mode
366        by only issuing tme_tape_connection_read calls when the block
367        buffer has been exhausted, and also honor the SILI bit.  as it
368        is, this code definitely has no chance of working with a real
369        tape drive.
370 
371        but to fix the immediate problem, if the read has transferred
372        some bytes with an underrun, we just pad the read out with
373        zeroes.  this fixes NetBSD PR pkg/34536: */
374     if (scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid > 0
375 	&& scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid < bytes_xfer
376 	&& (flags
377 	    & ~(TME_TAPE_FLAG_ILI
378 		| TME_TAPE_FLAG_MARK)) == 0) {
379       /* XXX this breaks const: */
380       /* XXX writing into the tape buffer breaks the tape abstraction,
381 	 and only works because we know that posix-tape.c has
382 	 allocated this buffer to be at least as big as the read we
383 	 requested: */
384       memset(((tme_uint8_t *)
385 	      (scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
386 	       + scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid)),
387 	     0,
388 	     (bytes_xfer
389 	      - scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid));
390       scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid = bytes_xfer;
391       flags &= ~TME_TAPE_FLAG_ILI;
392     }
393 
394     /* get the status: */
395     status
396       = ((*scsi_tape->tme_scsi_tape_xfer_status)
397 	 (scsi_tape,
398 	  flags,
399 	  count_xfer));
400 
401     /* finish the command: */
402     tme_scsi_device_target_do_dsmf(scsi_device,
403 				   status,
404 				   TME_SCSI_MSG_CMD_COMPLETE);
405   }
406   else {
407 
408     /* get the tape buffer: */
409     rc
410       = ((*conn_tape->tme_tape_connection_write)
411 	 (conn_tape,
412 	  flags,
413 	  count_xfer,
414 	  &scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
415 	  &scsi_device->tme_scsi_device_dma.tme_scsi_dma_in));
416     scsi_device->tme_scsi_device_dma.tme_scsi_dma_out = NULL;
417 
418     /* enter the DATA OUT phase to transfer all of the data to be
419        written: */
420     /* XXX when in fixed-block mode, we should go a block at a time,
421        so we can report errors as soon as they happen: */
422     tme_scsi_device_target_phase(scsi_device,
423 				 TME_SCSI_SIGNAL_BSY
424 				 | TME_SCSI_PHASE_DATA_OUT);
425 
426     /* when the DATA OUT phase is done, we'll write the data end
427        enter the STATUS phase: */
428     scsi_device->tme_scsi_device_phase
429       = tme_scsi_tape_target_do_write;
430   }
431 
432   /* if we couldn't get the tape buffer: */
433   if (rc != TME_OK) {
434 
435     /* XXX we should return MEDIUM ERROR or HARDWARE ERROR sense here: */
436     abort();
437   }
438 }
439 
440 /* this implements the tape REWIND command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_rewind)441 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_rewind)
442 {
443   struct tme_scsi_tape *scsi_tape;
444   struct tme_scsi_tape_connection *conn_scsi_tape;
445   struct tme_tape_connection *conn_tape;
446   int lun;
447   int rc;
448 
449   /* recover our tape: */
450   scsi_tape = (struct tme_scsi_tape *) scsi_device;
451 
452   /* get the addressed LUN: */
453   lun = scsi_device->tme_scsi_device_addressed_lun;
454 
455   /* get the tape connection: */
456   conn_scsi_tape
457     = scsi_tape->tme_scsi_tape_connections[lun];
458   conn_tape
459     = ((struct tme_tape_connection *)
460        conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
461 
462   /* call out a REWIND control: */
463   rc =
464     ((*conn_tape->tme_tape_connection_control)
465      (conn_tape,
466       TME_TAPE_CONTROL_REWIND));
467   assert (rc == TME_OK);
468 
469   /* finish the command: */
470   tme_scsi_device_target_do_smf(scsi_device,
471 				TME_SCSI_STATUS_GOOD,
472 				TME_SCSI_MSG_CMD_COMPLETE);
473 }
474 
475 /* this implements the tape READ BLOCK LIMITS command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_block_limits)476 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_block_limits)
477 {
478   struct tme_scsi_tape *scsi_tape;
479   tme_uint8_t *data;
480   int lun;
481   tme_uint32_t block_size;
482 
483   /* recover our tape: */
484   scsi_tape = (struct tme_scsi_tape *) scsi_device;
485 
486   /* get the addressed LUN: */
487   lun = scsi_device->tme_scsi_device_addressed_lun;
488 
489   data = &scsi_device->tme_scsi_device_data[0];
490 
491   /* a reserved byte: */
492   data++;
493 
494   /* the Maximum Block Length: */
495   block_size = scsi_tape->tme_scsi_tape_block_size_max;
496   *(data++) = (block_size >> 16) & 0xff;
497   *(data++) = (block_size >>  8) & 0xff;
498   *(data++) = (block_size >>  0) & 0xff;
499 
500   /* the Minimum Block Length: */
501   block_size = scsi_tape->tme_scsi_tape_block_size_min;
502   assert (block_size > 0);
503   *(data++) = (block_size >>  8) & 0xff;
504   *(data++) = (block_size >>  0) & 0xff;
505 
506   /* set the DMA pointer and length: */
507   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
508     = (data
509        - &scsi_device->tme_scsi_device_data[0]);
510   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
511     = &scsi_device->tme_scsi_device_data[0];
512   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
513     = NULL;
514 
515   /* finish the command: */
516   tme_scsi_device_target_do_dsmf(scsi_device,
517 				 TME_SCSI_STATUS_GOOD,
518 				 TME_SCSI_MSG_CMD_COMPLETE);
519 }
520 
521 /* this implements the tape Group 0 READ command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_read0)522 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_read0)
523 {
524   tme_scsi_tape_cdb_xfer0(scsi_device, TRUE);
525 }
526 
527 /* this implements the tape Group 0 WRITE command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write0)528 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write0)
529 {
530   tme_scsi_tape_cdb_xfer0(scsi_device, FALSE);
531 }
532 
533 /* this implements the tape INQUIRY command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_inquiry)534 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_inquiry)
535 {
536   int lun;
537   struct tme_scsi_device_inquiry inquiry;
538   tme_uint8_t *data;
539 
540   /* get the active LUN: */
541   lun = scsi_device->tme_scsi_device_addressed_lun;
542 
543   /* this is a sequential-access device: */
544   inquiry.tme_scsi_device_inquiry_type = TME_SCSI_TYPE_TAPE;
545 
546   /* if this LUN is defined: */
547   inquiry.tme_scsi_device_inquiry_lun_state
548     = ((scsi_device->tme_scsi_device_luns
549 	& TME_BIT(lun))
550        ? TME_SCSI_LUN_PRESENT
551        : TME_SCSI_LUN_UNSUPPORTED);
552 
553   /* the device type qualifier: */
554   inquiry.tme_scsi_device_inquiry_type_qualifier = 0x00;
555 
556   /* nonzero iff the LUN is removable: */
557   inquiry.tme_scsi_device_inquiry_lun_removable = TRUE;
558 
559   /* the various standards versions: */
560   inquiry.tme_scsi_device_inquiry_std_ansi = 1;
561   inquiry.tme_scsi_device_inquiry_std_ecma = 1;
562   inquiry.tme_scsi_device_inquiry_std_iso = 1;
563 
564   /* the response format: */
565   inquiry.tme_scsi_device_response_format = TME_SCSI_FORMAT_CCS;
566 
567   /* make the inquiry data: */
568   data
569     = tme_scsi_device_make_inquiry_data(scsi_device,
570 					&inquiry);
571   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
572     = TME_MIN((data
573 	       - scsi_device->tme_scsi_device_dma.tme_scsi_dma_out),
574 	      scsi_device->tme_scsi_device_cdb[4]);
575 
576   /* finish the command: */
577   tme_scsi_device_target_do_dsmf(scsi_device,
578 				 TME_SCSI_STATUS_GOOD,
579 				 TME_SCSI_MSG_CMD_COMPLETE);
580 }
581 
582 /* this implements the tape WRITE MARKS command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write_marks)583 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_write_marks)
584 {
585   abort();
586 }
587 
588 /* this implements the tape SPACE command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_space)589 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_space)
590 {
591   struct tme_scsi_tape *scsi_tape;
592   struct tme_scsi_tape_connection *conn_scsi_tape;
593   struct tme_tape_connection *conn_tape;
594   int lun;
595   tme_uint8_t *cdb;
596   tme_int32_t count;
597   int rc;
598 
599   /* recover our tape: */
600   scsi_tape = (struct tme_scsi_tape *) scsi_device;
601 
602   /* get the addressed LUN: */
603   lun = scsi_device->tme_scsi_device_addressed_lun;
604 
605   /* get the tape connection: */
606   conn_scsi_tape
607     = scsi_tape->tme_scsi_tape_connections[lun];
608   conn_tape
609     = ((struct tme_tape_connection *)
610        conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
611 
612   cdb = &scsi_device->tme_scsi_device_cdb[0];
613 
614   /* get the signed count: */
615   count = ((tme_int8_t *) cdb)[2];
616   count = (count << 8) | cdb[3];
617   count = (count << 8) | cdb[4];
618 
619   /* dispatch on the SPACE code: */
620   switch (cdb[1] & 0x03) {
621 
622     /* blocks: */
623   case 0x00:
624     abort();
625 
626     /* filemarks: */
627   case 0x01:
628 
629     /* call out a MARK_SKIPF or MARK_SKIPR control: */
630     rc =
631       (count < 0
632        ? ((*conn_tape->tme_tape_connection_control)
633 	  (conn_tape,
634 	   TME_TAPE_CONTROL_MARK_SKIPR,
635 	   (unsigned int) (-count)))
636        : ((*conn_tape->tme_tape_connection_control)
637 	  (conn_tape,
638 	   TME_TAPE_CONTROL_MARK_SKIPF,
639 	   (unsigned int) count)));
640     assert (rc == TME_OK);
641     break;
642 
643     /* sequential filemarks: */
644   case 0x02:
645     abort();
646 
647     /* physical end-of-data: */
648   case 0x03:
649     abort();
650   }
651 
652   /* finish the command: */
653   tme_scsi_device_target_do_smf(scsi_device,
654 				TME_SCSI_STATUS_GOOD,
655 				TME_SCSI_MSG_CMD_COMPLETE);
656 }
657 
658 /* this processes the parameter list from a tape MODE SELECT command: */
_TME_SCSI_DEVICE_PHASE_DECL(_tme_scsi_tape_mode_select_data)659 _TME_SCSI_DEVICE_PHASE_DECL(_tme_scsi_tape_mode_select_data)
660 {
661   struct tme_scsi_tape *scsi_tape;
662   int lun;
663   struct tme_scsi_tape_connection *conn_scsi_tape;
664   struct tme_tape_connection *conn_tape;
665   const tme_uint8_t *data;
666   const tme_uint8_t *data_end;
667   tme_uint8_t status;
668   unsigned int block_descriptors;
669   tme_uint32_t blocks;
670   tme_uint32_t block_size;
671   tme_uint32_t length;
672   unsigned long sizes[3];
673   int rc;
674 
675   /* recover our tape: */
676   scsi_tape = (struct tme_scsi_tape *) scsi_device;
677 
678   /* get the addressed LUN: */
679   lun = scsi_device->tme_scsi_device_addressed_lun;
680 
681   /* get a pointer to the first byte of data, and a pointer past the
682      last byte of data: */
683   data = &scsi_device->tme_scsi_device_data[0];
684   length = scsi_device->tme_scsi_device_cdb[4];
685   data_end = (data
686 	      + TME_MIN(sizeof(scsi_device->tme_scsi_device_data),
687 			length));
688 
689   /* assume that this command will succeed: */
690   status = TME_SCSI_STATUS_GOOD;
691 
692   /* skip the two reserved bytes: */
693   data += (data < data_end);
694   data += (data < data_end);
695 
696   /* we ignore the buffered mode and speed byte: */
697   data += (data < data_end);
698 
699   /* get the count of bytes in block descriptors: */
700   block_descriptors
701     = (data < data_end
702        ? *(data++)
703        : 0);
704 
705   /* check the block descriptors: */
706   block_size = 0;
707   for (;
708        block_descriptors >= 8;
709        block_descriptors -= 8) {
710 
711     /* if this block descriptor is short: */
712     if ((data_end - data) < 8) {
713 
714       /* XXX FIXME - we need to assemble a sense and return a CHECK
715          CONDITION here: */
716       abort();
717     }
718 
719     /* we ignore the density code: */
720     data++;
721 
722     /* get the block count: */
723     blocks = *(data++);
724     blocks = (blocks << 8) + *(data++);
725     blocks = (blocks << 8) + *(data++);
726 
727     /* skip the reserved byte: */
728     data++;
729 
730     /* get the block length: */
731     block_size = *(data++);
732     block_size = (block_size << 8) + *(data++);
733     block_size = (block_size << 8) + *(data++);
734 
735     /* if this block descriptor doesn't describe the entire tape: */
736     if (blocks != 0) {
737 
738       /* XXX FIXME - we need to assemble a sense and return a CHECK
739          CONDITION here: */
740       abort();
741     }
742 
743     /* set the new current block size: */
744     scsi_tape->tme_scsi_tape_block_size_current = block_size;
745   }
746 
747   /* if the parameter list was good: */
748   if (status == TME_SCSI_STATUS_GOOD) {
749 
750     /* get the tape connection: */
751     conn_scsi_tape
752       = scsi_tape->tme_scsi_tape_connections[lun];
753     conn_tape
754       = ((struct tme_tape_connection *)
755 	 conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
756 
757     /* set the block size: */
758     if (block_size != 0) {
759       sizes[0] = block_size;
760       sizes[1] = block_size;
761       sizes[2] = block_size;
762     }
763     else {
764       sizes[0] = scsi_tape->tme_scsi_tape_block_size_min;
765       sizes[1] = scsi_tape->tme_scsi_tape_block_size_max;
766       sizes[2] = 0;
767     }
768     rc
769       = ((*conn_tape->tme_tape_connection_control)
770 	 (conn_tape,
771 	  TME_TAPE_CONTROL_BLOCK_SIZE_SET,
772 	  sizes));
773     assert (rc == TME_OK);
774   }
775 
776   tme_scsi_device_target_do_smf(scsi_device,
777 				status,
778 				TME_SCSI_MSG_CMD_COMPLETE);
779 }
780 
781 /* this implements the tape MODE SELECT command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_select)782 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_select)
783 {
784 
785   /* read in the parameter list: */
786   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
787     = scsi_device->tme_scsi_device_cdb[4];
788   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
789     = TME_MIN(scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid,
790 	      sizeof(scsi_device->tme_scsi_device_data));
791   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
792     = &scsi_device->tme_scsi_device_data[0];
793   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
794     = NULL;
795 
796   /* transfer the parameter list: */
797   tme_scsi_device_target_phase(scsi_device,
798 			       (TME_SCSI_SIGNAL_BSY
799 				| TME_SCSI_PHASE_DATA_OUT));
800   scsi_device->tme_scsi_device_phase
801     = _tme_scsi_tape_mode_select_data;
802 }
803 
804 /* this implements the tape MODE SENSE command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_sense)805 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_mode_sense)
806 {
807   struct tme_scsi_tape *scsi_tape;
808   tme_uint8_t *data;
809   tme_uint32_t blocks, block_size;
810   int lun;
811 
812   /* recover our tape: */
813   scsi_tape = (struct tme_scsi_tape *) scsi_device;
814 
815   /* get the addressed LUN: */
816   lun = scsi_device->tme_scsi_device_addressed_lun;
817 
818   /* get the current block size: */
819   block_size = scsi_tape->tme_scsi_tape_block_size_current;
820 
821   data = &scsi_device->tme_scsi_device_data[0];
822 
823   /* the sense data length.  we will fill this in later: */
824   data++;
825 
826   /* byte 1 is the medium type: */
827   *(data++) = 0x00; /* default (only one medium type supported) */
828 
829   /* byte 2 is the WP (Write Protect), Buffered Mode, and Speed: */
830   *(data++) = 0x80; /* write protected, unbuffered, default speed */
831 
832   /* byte 3 is the Block Descriptor Length.  we will fill this in
833      later: */
834   data++;
835 
836   /* the first Block Descriptor: */
837 
838   /* the Block Descriptor density code: */
839   *(data++) = 0x05; /* QIC-24 */
840 
841   /* the Number of Blocks: */
842   /* XXX FIXME - we assume a 60MB tape: */
843   blocks = (60 * 1024 * 1024) / block_size;
844   *(data++) = (blocks >> 16) & 0xff;
845   *(data++) = (blocks >>  8) & 0xff;
846   *(data++) = (blocks >>  0) & 0xff;
847 
848   /* a reserved byte: */
849   data++;
850 
851   /* the Block Length: */
852   *(data++) = (block_size >> 16) & 0xff;
853   *(data++) = (block_size >>  8) & 0xff;
854   *(data++) = (block_size >>  0) & 0xff;
855 
856   /* fill in the Block Descriptor Length: */
857   scsi_device->tme_scsi_device_data[3]
858     = (data - &scsi_device->tme_scsi_device_data[4]);
859 
860   /* there are no vendor-unique bytes or mode pages: */
861 
862   /* fill in the sense data length: */
863   scsi_device->tme_scsi_device_data[0]
864     = (data - &scsi_device->tme_scsi_device_data[1]);
865 
866   /* set the DMA pointer and length: */
867   scsi_device->tme_scsi_device_dma.tme_scsi_dma_resid
868     = TME_MIN((data
869 	       - &scsi_device->tme_scsi_device_data[0]),
870 	      scsi_device->tme_scsi_device_cdb[4]);
871   scsi_device->tme_scsi_device_dma.tme_scsi_dma_out
872     = &scsi_device->tme_scsi_device_data[0];
873   scsi_device->tme_scsi_device_dma.tme_scsi_dma_in
874     = NULL;
875 
876   /* finish the command: */
877   tme_scsi_device_target_do_dsmf(scsi_device,
878 				 TME_SCSI_STATUS_GOOD,
879 				 TME_SCSI_MSG_CMD_COMPLETE);
880 }
881 
882 /* this implements the tape LOAD/UNLOAD command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_load_unload)883 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_load_unload)
884 {
885   /* XXX TBD */
886 
887   /* finish the command: */
888   tme_scsi_device_target_do_smf(scsi_device,
889 				TME_SCSI_STATUS_GOOD,
890 				TME_SCSI_MSG_CMD_COMPLETE);
891 }
892 
893 /* this implements the tape PREVENT/ALLOW command: */
_TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_prevent_allow)894 _TME_SCSI_DEVICE_CDB_DECL(tme_scsi_tape_cdb_prevent_allow)
895 {
896   /* XXX TBD */
897 
898   /* finish the command: */
899   tme_scsi_device_target_do_smf(scsi_device,
900 				TME_SCSI_STATUS_GOOD,
901 				TME_SCSI_MSG_CMD_COMPLETE);
902 }
903 
904 /* the tape control handler: */
905 #ifdef HAVE_STDARG_H
_tme_scsi_tape_control(struct tme_tape_connection * conn_tape,unsigned int control,...)906 static int _tme_scsi_tape_control(struct tme_tape_connection *conn_tape,
907 				  unsigned int control,
908 				  ...)
909 #else  /* HAVE_STDARG_H */
910 static int _tme_scsi_tape_control(conn_tape, control, va_alist)
911      struct tme_tape_connection *conn_tape;
912      unsigned int control;
913      va_dcl
914 #endif /* HAVE_STDARG_H */
915 {
916   struct tme_scsi_tape *scsi_tape;
917   struct tme_scsi_tape_connection *conn_scsi_tape;
918   va_list control_args;
919 
920   /* recover our data structures: */
921   conn_scsi_tape = (struct tme_scsi_tape_connection *) conn_tape;
922   scsi_tape = (struct tme_scsi_tape *) conn_tape->tme_tape_connection.tme_connection_element->tme_element_private;
923 
924   /* lock the mutex: */
925   tme_mutex_lock(&scsi_tape->tme_scsi_tape_mutex);
926 
927   /* start the variable arguments: */
928 #ifdef HAVE_STDARG_H
929   va_start(control_args, control);
930 #else  /* HAVE_STDARG_H */
931   va_start(control_args);
932 #endif /* HAVE_STDARG_H */
933 
934   /* dispatch on the sequence type: */
935   switch (control) {
936 
937   case TME_TAPE_CONTROL_LOAD:
938     /* a tape has been loaded: */
939     conn_scsi_tape->tme_scsi_tape_connection_flags
940       = (conn_scsi_tape->tme_scsi_tape_connection_flags
941 	 | (TME_SCSI_TAPE_FLAG_LOADED
942 	    | TME_SCSI_TAPE_FLAG_ATTENTION));
943     break;
944 
945   case TME_TAPE_CONTROL_UNLOAD:
946     /* a tape has been unloaded: */
947     conn_scsi_tape->tme_scsi_tape_connection_flags
948       = ((conn_scsi_tape->tme_scsi_tape_connection_flags
949 	  & ~TME_SCSI_TAPE_FLAG_LOADED)
950 	 | TME_SCSI_TAPE_FLAG_ATTENTION);
951     break;
952 
953     abort();
954 
955   case TME_TAPE_CONTROL_DENSITY_GET:
956     abort();
957 
958   case TME_TAPE_CONTROL_DENSITY_SET:
959     abort();
960 
961   case TME_TAPE_CONTROL_BLOCK_SIZE_GET:
962     abort();
963 
964   case TME_TAPE_CONTROL_BLOCK_SIZE_SET:
965     abort();
966 
967   case TME_TAPE_CONTROL_REWIND:
968   case TME_TAPE_CONTROL_MARK_WRITE:
969   case TME_TAPE_CONTROL_MARK_SKIPF:
970   case TME_TAPE_CONTROL_MARK_SKIPR:
971   default:
972     abort();
973   }
974 
975   /* end the variable arguments: */
976   va_end(control_args);
977 
978   /* unlock the mutex: */
979   tme_mutex_unlock(&scsi_tape->tme_scsi_tape_mutex);
980 
981   return (TME_OK);
982 }
983 
984 /* this breaks a connection: */
985 static int
_tme_scsi_tape_connection_break(struct tme_connection * conn,unsigned int state)986 _tme_scsi_tape_connection_break(struct tme_connection *conn,
987 				unsigned int state)
988 {
989   abort();
990 }
991 
992 /* this makes a new tape connection: */
993 static int
_tme_scsi_tape_connection_make(struct tme_connection * conn,unsigned int state)994 _tme_scsi_tape_connection_make(struct tme_connection *conn,
995 			       unsigned int state)
996 {
997   struct tme_scsi_tape *scsi_tape;
998   struct tme_scsi_tape_connection *conn_scsi_tape;
999   struct tme_tape_connection *conn_tape;
1000   int lun;
1001   int loaded;
1002   int rc;
1003 
1004   /* both sides must be tape connections: */
1005   assert (conn->tme_connection_type == TME_CONNECTION_TAPE);
1006   assert (conn->tme_connection_other->tme_connection_type == TME_CONNECTION_TAPE);
1007 
1008   /* recover our data structures: */
1009   scsi_tape = conn->tme_connection_element->tme_element_private;
1010   conn_scsi_tape = (struct tme_scsi_tape_connection *) conn;
1011 
1012   /* we're always set up to answer calls across the connection,
1013      so we only have to do work when the connection has gone full,
1014      namely taking the other side of the connection: */
1015   if (state == TME_CONNECTION_FULL) {
1016 
1017     /* lock the mutex: */
1018     tme_mutex_lock(&scsi_tape->tme_scsi_tape_mutex);
1019 
1020     /* make this tape connection: */
1021     lun = conn_scsi_tape->tme_scsi_tape_connection_lun;
1022     assert (scsi_tape->tme_scsi_tape_connections[lun]
1023 	    == NULL);
1024     scsi_tape->tme_scsi_tape_connections[lun]
1025       = conn_scsi_tape;
1026     scsi_tape->tme_scsi_tape_device.tme_scsi_device_luns
1027       |= (1 << lun);
1028 
1029     /* call any type-specific connection function: */
1030     if (scsi_tape->tme_scsi_tape_connected != NULL) {
1031       (*scsi_tape->tme_scsi_tape_connected)(scsi_tape, lun);
1032     }
1033 
1034     /* call out a LOAD control to see if the tape is currently loaded: */
1035     conn_tape
1036       = ((struct tme_tape_connection *)
1037 	 conn_scsi_tape->tme_scsi_tape_connection.tme_tape_connection.tme_connection_other);
1038     rc =
1039       ((*conn_tape->tme_tape_connection_control)
1040        (conn_tape,
1041 	TME_TAPE_CONTROL_LOAD,
1042 	&loaded));
1043     assert (rc == TME_OK);
1044     conn_scsi_tape->tme_scsi_tape_connection_flags
1045       = (loaded
1046 	 ? (TME_SCSI_TAPE_FLAG_LOADED
1047 	    | TME_SCSI_TAPE_FLAG_ATTENTION)
1048 	 : 0);
1049 
1050     /* unlock the mutex: */
1051     tme_mutex_unlock(&scsi_tape->tme_scsi_tape_mutex);
1052   }
1053 
1054   return (TME_OK);
1055 }
1056 
1057 /* this returns the new connections possible: */
1058 static int
_tme_scsi_tape_connections_new(struct tme_element * element,const char * const * args,struct tme_connection ** _conns,char ** _output)1059 _tme_scsi_tape_connections_new(struct tme_element *element,
1060 			       const char * const *args,
1061 			       struct tme_connection **_conns,
1062 			       char **_output)
1063 {
1064   struct tme_scsi_tape *scsi_tape;
1065   struct tme_scsi_tape_connection *conn_scsi_tape;
1066   struct tme_tape_connection *conn_tape;
1067   struct tme_connection *conn;
1068   int lun;
1069   int arg_i;
1070   int usage;
1071   int rc;
1072 
1073   /* recover our device: */
1074   scsi_tape = (struct tme_scsi_tape *) element->tme_element_private;
1075 
1076   /* check our arguments: */
1077   lun = -1;
1078   arg_i = 1;
1079   usage = FALSE;
1080 
1081   /* loop reading our arguments: */
1082   for (;;) {
1083 
1084     /* the LUN to attach to: */
1085     if (TME_ARG_IS(args[arg_i + 0], "lun")
1086 	&& lun < 0
1087 	&& (lun = tme_scsi_lun_parse(args[arg_i + 1])) >= 0
1088 	&& lun < TME_SCSI_DEVICE_LUN_COUNT
1089 	&& scsi_tape->tme_scsi_tape_connections[lun] == NULL) {
1090       arg_i += 2;
1091     }
1092 
1093     /* if we've run out of arguments: */
1094     else if (args[arg_i + 0] == NULL) {
1095       break;
1096     }
1097 
1098     /* this is a bad argument: */
1099     else {
1100       tme_output_append_error(_output,
1101 			      "%s %s, ",
1102 			      args[arg_i],
1103 			      _("unexpected"));
1104       usage = TRUE;
1105       break;
1106     }
1107   }
1108 
1109   if (usage) {
1110     tme_output_append_error(_output,
1111 			    "%s %s [ lun %s ]",
1112 			    _("usage:"),
1113 			    args[0],
1114 			    _("LOGICAL-UNIT"));
1115     return (EINVAL);
1116   }
1117 
1118   /* return any SCSI device SCSI connection: */
1119   rc = tme_scsi_device_connections_new(element,
1120 				       args,
1121 				       _conns,
1122 				       _output);
1123   if (rc != TME_OK) {
1124     return (rc);
1125   }
1126 
1127   /* if we don't have a particular lun, see if there is a free lun.
1128      if there isn't a free lun, return now: */
1129   if (lun < 0) {
1130     for (lun = 0;
1131 	 lun < TME_SCSI_DEVICE_LUN_COUNT;
1132 	 lun++) {
1133       if (scsi_tape->tme_scsi_tape_connections[lun] == NULL) {
1134 	break;
1135       }
1136     }
1137     if (lun == TME_SCSI_DEVICE_LUN_COUNT) {
1138       return (TME_OK);
1139     }
1140   }
1141 
1142   /* create our side of a tape connection: */
1143   conn_scsi_tape = tme_new0(struct tme_scsi_tape_connection, 1);
1144   conn_tape = &conn_scsi_tape->tme_scsi_tape_connection;
1145   conn = &conn_tape->tme_tape_connection;
1146 
1147   /* fill in the generic connection: */
1148   conn->tme_connection_next = *_conns;
1149   conn->tme_connection_type = TME_CONNECTION_TAPE;
1150   conn->tme_connection_score = tme_tape_connection_score;
1151   conn->tme_connection_make = _tme_scsi_tape_connection_make;
1152   conn->tme_connection_break = _tme_scsi_tape_connection_break;
1153 
1154   /* fill in the tape connection: */
1155   conn_tape->tme_tape_connection_control = _tme_scsi_tape_control;
1156 
1157   /* fill in the internal tape connection: */
1158   conn_scsi_tape->tme_scsi_tape_connection_lun = lun;
1159 
1160   /* return the connection side possibility: */
1161   *_conns = conn;
1162   return (TME_OK);
1163 }
1164 
1165 /* the new SCSI tape function: */
TME_ELEMENT_SUB_NEW_DECL(tme_scsi,tape)1166 TME_ELEMENT_SUB_NEW_DECL(tme_scsi,tape) {
1167   int id;
1168   const char *tape_type;
1169   const char *vendor;
1170   const char *product;
1171   const char *revision;
1172   struct tme_scsi_tape *scsi_tape;
1173   struct tme_scsi_device *scsi_device;
1174   int arg_i;
1175   int usage;
1176   unsigned int tape_list_i;
1177   int (*tape_init) _TME_P((struct tme_scsi_tape *));
1178   int rc;
1179 
1180   /* check our arguments: */
1181   id = -1;
1182   tape_type = NULL;
1183   vendor = NULL;
1184   product = NULL;
1185   revision = NULL;
1186   arg_i = 1;
1187   usage = FALSE;
1188 
1189   /* loop reading our arguments: */
1190   for (;;) {
1191 
1192     /* the SCSI ID: */
1193     if (TME_ARG_IS(args[arg_i], "id")
1194 	&& id < 0
1195 	&& (id = tme_scsi_id_parse(args[arg_i + 1])) >= 0) {
1196       arg_i += 2;
1197     }
1198 
1199     /* the tape type: */
1200     else if (TME_ARG_IS(args[arg_i], "type")
1201 	     && tape_type == NULL
1202 	     && args[arg_i + 1] != NULL) {
1203       tape_type = args[arg_i + 1];
1204       arg_i += 2;
1205     }
1206 
1207     /* any inquiry vendor, product, or revision: */
1208     else if (TME_ARG_IS(args[arg_i], "vendor")
1209 	     && vendor == NULL
1210 	     && args[arg_i + 1] != NULL) {
1211       vendor = args[arg_i + 1];
1212       arg_i += 2;
1213     }
1214     else if (TME_ARG_IS(args[arg_i], "product")
1215 	     && product == NULL
1216 	     && args[arg_i + 1] != NULL) {
1217       product = args[arg_i + 1];
1218       arg_i += 2;
1219     }
1220     else if (TME_ARG_IS(args[arg_i], "revision")
1221 	     && revision == NULL
1222 	     && args[arg_i + 1] != NULL) {
1223       revision = args[arg_i + 1];
1224       arg_i += 2;
1225     }
1226 
1227     /* if we've run out of arguments: */
1228     else if (args[arg_i + 0] == NULL) {
1229 
1230       /* we must have been given an ID and a type: */
1231       if (id < 0
1232 	  || tape_type == NULL) {
1233 	usage = TRUE;
1234       }
1235       break;
1236     }
1237 
1238     /* this is a bad argument: */
1239     else {
1240       tme_output_append_error(_output,
1241 			      "%s %s",
1242 			      args[arg_i],
1243 			      _("unexpected"));
1244       usage = TRUE;
1245       break;
1246     }
1247   }
1248 
1249   if (usage) {
1250     tme_output_append_error(_output,
1251 			    "%s %s id %s type %s [ vendor %s ] [ product %s ] [ revision %s ]",
1252 			    _("usage:"),
1253 			    args[0],
1254 			    _("TYPE"),
1255 			    _("ID"),
1256 			    _("VENDOR"),
1257 			    _("PRODUCT"),
1258 			    _("REVISION"));
1259     return (EINVAL);
1260   }
1261 
1262   /* make sure that this tape type is known: */
1263   tape_init = NULL;
1264   for (tape_list_i = 0;
1265        tape_list_i < TME_ARRAY_ELS(_tme_scsi_tape_list);
1266        tape_list_i++) {
1267     if (!strcmp(_tme_scsi_tape_list[tape_list_i]._tme_scsi_tape_list_type,
1268 		tape_type)) {
1269       tape_init = _tme_scsi_tape_list[tape_list_i]._tme_scsi_tape_list_init;
1270       break;
1271     }
1272   }
1273   if (tape_init == NULL) {
1274     tme_output_append_error(_output, "%s", tape_type);
1275     return (ENOENT);
1276   }
1277 
1278   /* start the tape structure: */
1279   scsi_tape = tme_new0(struct tme_scsi_tape, 1);
1280   scsi_tape->tme_scsi_tape_element = element;
1281   scsi_tape->tme_scsi_tape_type = tme_strdup(tape_type);
1282 
1283   /* initialize the generic SCSI device structure: */
1284   scsi_device = &scsi_tape->tme_scsi_tape_device;
1285   rc = tme_scsi_device_new(scsi_device, id);
1286   assert (rc == TME_OK);
1287 
1288   scsi_device->tme_scsi_device_vendor
1289     = tme_strdup((vendor == NULL)
1290 		 ? "TME"
1291 		 : vendor);
1292   scsi_device->tme_scsi_device_product
1293     = tme_strdup((product == NULL)
1294 		 ? "TAPE"
1295 		 : product);
1296   scsi_device->tme_scsi_device_revision
1297     = tme_strdup((revision == NULL)
1298 		 ? "0000"
1299 		 : revision);
1300 
1301   /* set the commands for sequential-access devices: */
1302   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1303 			 TME_SCSI_CDB_INQUIRY,
1304 			 tme_scsi_tape_cdb_inquiry);
1305   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1306 			 TME_SCSI_CDB_TAPE_REWIND,
1307 			 tme_scsi_tape_cdb_rewind);
1308   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1309 			 TME_SCSI_CDB_TAPE_BLOCK_LIMITS,
1310 			 tme_scsi_tape_cdb_block_limits);
1311   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1312 			 TME_SCSI_CDB_TAPE_READ0,
1313 			 tme_scsi_tape_cdb_read0);
1314   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1315 			 TME_SCSI_CDB_TAPE_WRITE0,
1316 			 tme_scsi_tape_cdb_write0);
1317   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1318 			 TME_SCSI_CDB_TAPE_WRITE_MARKS,
1319 			 tme_scsi_tape_cdb_write_marks);
1320   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1321 			 TME_SCSI_CDB_TAPE_SPACE,
1322 			 tme_scsi_tape_cdb_space);
1323   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1324 			 TME_SCSI_CDB_TAPE_MODE_SELECT,
1325 			 tme_scsi_tape_cdb_mode_select);
1326   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1327 			 TME_SCSI_CDB_TAPE_MODE_SENSE,
1328 			 tme_scsi_tape_cdb_mode_sense);
1329   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1330 			 TME_SCSI_CDB_TAPE_LOAD_UNLOAD,
1331 			 tme_scsi_tape_cdb_load_unload);
1332   TME_SCSI_DEVICE_DO_CDB(scsi_device,
1333 			 TME_SCSI_CDB_TAPE_PREVENT_ALLOW,
1334 			 tme_scsi_tape_cdb_prevent_allow);
1335 
1336   /* there is no type-specific connected function: */
1337   scsi_tape->tme_scsi_tape_connected = NULL;
1338 
1339   /* use the default transfer status function: */
1340   scsi_tape->tme_scsi_tape_xfer_status
1341     = tme_scsi_tape_xfer_status;
1342 
1343   /* use the default tape LUN addresser: */
1344   scsi_device->tme_scsi_device_address_lun
1345     = tme_scsi_tape_address_lun_aware;
1346 
1347   /* call the type-specific initialization function: */
1348   rc = (*tape_init)(scsi_tape);
1349   assert (rc == TME_OK);
1350 
1351   /* fill the element: */
1352   element->tme_element_private = scsi_tape;
1353   element->tme_element_connections_new = _tme_scsi_tape_connections_new;
1354 
1355   return (TME_OK);
1356 }
1357