home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / programm.ing / libscsi1.zoo / LibScsi-0.01 / scsi_io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-15  |  29.1 KB  |  1,023 lines

  1. /*
  2.  * scsi_io.c - Copyright Steve Woodford, August 1993
  3.  *
  4.  * Implement a nice clean I/O interface to SCSI devices.
  5.  * This code should be completely independent of the underlying
  6.  * SCSI bus architecture. (Although currently, only support
  7.  * for the ST's ACSI bus is provided in the low level stuff used
  8.  * by the functions here).
  9.  *
  10.  * Although untested, if my theory is good, this should work
  11.  * for CD-ROM devices ;-). Currently, it has been tested on
  12.  * hard disks & an Archive Viper tapedrive.
  13.  */
  14.  
  15. #include <stdlib.h>
  16. #include <errno.h>
  17. #include <sys/types.h>
  18. #include <sys/scsi.h>
  19. #include <sys/scsi_io.h>
  20. #include <memory.h>
  21. #include <strings.h>
  22. #include <cfile.h>
  23. #include "libscsi.h"
  24.  
  25. /*
  26.  * Ensure version string linked in.
  27.  */
  28. EXTERN  char    Scsi_Lib_Version[];
  29. PRIVATE char   *slv = &(Scsi_Lib_Version[0]);
  30.  
  31.  
  32. #define _SCM_READ           0x01    /* Enable READS on device           */
  33. #define _SCM_WRITE          0x02    /* Enable WRITES on device          */
  34. #define _SCM_APPEND         0x04    /* Append (Implies No Rewind Open)  */
  35. #define _SCM_REWIND_CLOSE   0x08    /* Rewind on close                  */
  36.  
  37. #define NFSLOTS             8
  38.  
  39. /*
  40.  * Each 'scsi_open()' call results in one of these being allocated...
  41.  */
  42. typedef struct _dd {
  43.     u_char          dd_id;          /* SCSI Id of target                */
  44.     u_char          dd_lun;         /* Lun of target                    */
  45.     u_char          dd_type;        /* Target Type                      */
  46.     u_char          dd_flags;       /* Target flags                     */
  47.     u_char          dd_mode;        /* Open Mode of device              */
  48.     u_char          dd_wfm;         /* Set when filemark to be written  */
  49.     u_char          dd_eof;         /* Set when filemark hit during read*/
  50.     u_char          dd_eot;         /* Set when end of tape reached     */
  51.     u_char          dd_blk_shift;   /* Convert DMA blocks to Device blks*/
  52.     u_char          dd_sense_key;   /* Most recent sense key            */
  53.     u_long          dd_sense_blk;   /* Block # of associated sense key  */
  54.     u_short         dd_blk_size;    /* # of bytes per block             */
  55.     u_long          dd_num_blocks;  /* # of blocks on device            */
  56.     char            dd_vendor[8];   /* Vendor ID                        */
  57.     char            dd_product[16]; /* Product ID                       */
  58.     char            dd_revision[4]; /* Revision level                   */
  59.     long            dd_seek_pos;    /* Current 'seek' position          */
  60.     struct _dd     *dd_prev;        /* Link to previous in device chain */
  61.     struct _dd     *dd_next;        /* Link to next in device chain     */
  62. } Dev_Desc;
  63.  
  64. PRIVATE Dev_Desc   **sc_open_list    = (Dev_Desc **)0,
  65.                     *Dd_List[]       = {0L,0L,0L,0L,0L,0L,0L,0L};
  66. PRIVATE u_char       is_sequential[] = {0,0,0,0,0,0,0,0},
  67.                      is_write_prot[] = {0,0,0,0,0,0,0,0},
  68.                      is_removable [] = {0,0,0,0,0,0,0,0},
  69.                      num_opens    [] = {0,0,0,0,0,0,0,0};
  70. PRIVATE u_short      free_slots      = 0,
  71.                      nslots          = 0;
  72.  
  73. PRIVATE u_short      got_cfile = 0;
  74. PRIVATE short        cfiled;
  75. PRIVATE short        get_cfile(void),
  76.                      read_config_file(char *);
  77.  
  78. PRIVATE int          sc_dev_error(u_char, short, Dev_Desc *);
  79. PRIVATE Dev_Desc    *get_dev_info(u_char);
  80. PRIVATE short        check_numeric(char *);
  81.  
  82. #define Strncpy      (void)strncpy
  83.  
  84.  
  85. /*
  86.  * Initiate a connection to a Scsi device...
  87.  */
  88.  
  89. PUBLIC  int
  90. scsi_open(u_short id, u_short lun, char *mode)
  91. {
  92.     Dev_Desc    *dd, *sd;
  93.     u_char       md, targ = id | (lun << 3);
  94.     int          fd;
  95.  
  96.     /*
  97.      * Check range of id and lun. Watch out for magic 'SCSI_TAPE_ID'
  98.      * which implies "system's default Scsi tape drive", normally
  99.      * picked up from the environment, or a SCSI.CNF file.
  100.      */
  101.     if ( ((id > MAX_SCSI_ID) && (id != SCSI_TAPE_ID)) ||
  102.                                             (lun > MAX_LUN_ID) ) {
  103.         errno = ENODEV;
  104.         return(-1);
  105.     }
  106.  
  107.     /*
  108.      * Quick check on the 'mode' parameter...
  109.      */
  110.     if ( (mode == (char *)0) || (*mode == '\0') ) {
  111.         errno = EINVAL;
  112.         return(-1);
  113.     }
  114.  
  115.     /*
  116.      * Now decode 'mode' to something a bit more managable internally...
  117.      */
  118.     for (md = 0; *mode; mode++) {
  119.         switch (*mode) {
  120.           case  'r':
  121.           case  'R':    md |= _SCM_READ;            /* Open for reading   */
  122.                         break;
  123.           case  'w':
  124.           case  'W':    md |= _SCM_WRITE;           /* Open for writing   */
  125.                         break;
  126.           case  'a':
  127.           case  'A':    md |= _SCM_APPEND;          /* Open for appending */
  128.                         break;
  129.           case  'c':
  130.           case  'C':    md |= _SCM_REWIND_CLOSE;    /* Rewind on Close    */
  131.                         break;
  132.  
  133.           default:      errno = EINVAL;
  134.                         return(-1);
  135.         }
  136.     }
  137.  
  138.     /*
  139.      * Read SCSI config. file
  140.      */
  141.     if ( !got_cfile && (get_cfile() < 0) )
  142.         return(-1);
  143.  
  144.     /*
  145.      * If caller wants the system's default tapedrive, go find it's ID...
  146.      */
  147.     if ( id == SCSI_TAPE_ID ) {
  148.         char    *tid;
  149.         if ( (tid = getenv("SCSI_TAPE_ID")) == (char *)0 )
  150.             tid = (char *)config_file(CFILE_SEARCH, cfiled, "SCSI_TAPE_ID");
  151.  
  152.         if ( tid == (char *)0 ) {
  153.             errno = ENODEV;
  154.             return(-1);
  155.         }
  156.  
  157.         if ( ! check_numeric(tid) ) {
  158.             errno = EGENERIC;
  159.             return(-1);
  160.         }
  161.         id = targ = (u_short)atoi(tid);
  162.     }
  163.  
  164.     /*
  165.      * Go find out about the device...
  166.      */
  167.     if ( (sd = get_dev_info(targ)) == (Dev_Desc *)0 ) {
  168.         errno = EIO;
  169.         return(-1);
  170.     }
  171.  
  172.     /*
  173.      * Only one scsi_open() on a sequential device allowed at any time.
  174.      */
  175.     if ( is_sequential[id] && num_opens[id] ) {
  176.         errno = ENMFIL;
  177.         return(-1);
  178.     }
  179.  
  180.     /*
  181.      * Prevent attempts to Write to Read-only media...
  182.      */
  183.     if ( (md & _SCM_WRITE) && is_write_prot[id] ) {
  184.         errno = EROFS;
  185.         return(-1);
  186.     }
  187.  
  188.     /*
  189.      * For sequential devices, if caller didn't specify the APPEND
  190.      * flag, rezero the unit. For tapes, this performs a rewind.
  191.      */
  192.     if ( is_sequential[id] && !(md & _SCM_APPEND) ) {
  193.         short   z = Scsi_Rezero(targ);
  194.         if ( z != CBYTE_OK ) {
  195.             errno = EIO;
  196.             (void) sc_dev_error(targ, z, (Dev_Desc *)0);
  197.             return(-1);
  198.         }
  199.     }
  200.  
  201.     /*
  202.      * Grow the linked list of device descriptor blocks, if required.
  203.      */
  204.     if ( free_slots == 0 ) {
  205.         if ( sc_open_list == (Dev_Desc **)0 ) {
  206.             sc_open_list = (Dev_Desc **)malloc(NFSLOTS * sizeof(Dev_Desc *));
  207.             if ( sc_open_list == (Dev_Desc **)0 )
  208.                 return(-1);
  209.             Bzero((char *)sc_open_list, NFSLOTS * sizeof(Dev_Desc *));
  210.         } else {
  211.             char *zz = realloc((char *)sc_open_list,
  212.                             (nslots + NFSLOTS) * sizeof(Dev_Desc *));
  213.             if ( zz == (char *)0 )
  214.                 return(-1);
  215.             Bzero(&(zz[nslots * sizeof(Dev_Desc *)]),
  216.                             NFSLOTS * sizeof(Dev_Desc *));
  217.             sc_open_list = (Dev_Desc **)zz;
  218.         }
  219.  
  220.         nslots     += NFSLOTS;
  221.         free_slots += NFSLOTS;
  222.     }
  223.  
  224.     /*
  225.      * Find a free slot in the device chain...
  226.      */
  227.     for (fd = 0; (fd < nslots) && (sc_open_list[fd] != (Dev_Desc *)0); fd++)
  228.         ;
  229.  
  230.     /*
  231.      * This should never actually happen!
  232.      */
  233.     if ( fd == nslots ) {
  234.         errno = EFAULT;
  235.         return(-1);
  236.     }
  237.  
  238.     /*
  239.      * Allocate space for the device descriptot block (DDB) and then
  240.      * link it into the chain for the required target.
  241.      */
  242.     if ( (dd = (Dev_Desc *)malloc(sizeof(Dev_Desc))) == (Dev_Desc *)0 )
  243.         return(-1);
  244.     else {
  245.         Dev_Desc    **dp = &(Dd_List[id]),
  246.                      *op = (Dev_Desc *)0;
  247.  
  248.         Bzero((char *)dd, sizeof(Dev_Desc));
  249.  
  250.         while ( *dp ) {
  251.             op = *dp;
  252.             dp = &(op->dd_next);
  253.         }
  254.  
  255.         (*dp) = dd;
  256.         dd->dd_prev = op;
  257.         dd->dd_next = (Dev_Desc *)0;
  258.     }
  259.  
  260.     /*
  261.      * Fix up details in the DDB
  262.      */
  263.     sc_open_list[fd]  = dd;
  264.     free_slots       -= 1;
  265.     num_opens[id]    += 1;
  266.  
  267.     dd->dd_id         = id;
  268.     dd->dd_lun        = lun;
  269.     dd->dd_mode       = md;
  270.     dd->dd_type       = sd->dd_type;
  271.     dd->dd_flags      = sd->dd_flags;
  272.     dd->dd_blk_shift  = (u_char)(sd->dd_blk_size >> 10);
  273.     dd->dd_num_blocks = sd->dd_num_blocks;
  274.     dd->dd_blk_size   = sd->dd_blk_size;
  275.     Strncpy(dd->dd_vendor, sd->dd_vendor, sizeof(sd->dd_vendor));
  276.     Strncpy(dd->dd_product, sd->dd_product, sizeof(sd->dd_product));
  277.     Strncpy(dd->dd_revision, sd->dd_revision, sizeof(sd->dd_revision));
  278.  
  279.     return(fd);
  280. }
  281.  
  282.  
  283. /*
  284.  * Close device associated to 'fd'.
  285.  */
  286. PUBLIC  int
  287. scsi_close(int fd)
  288. {
  289.     Dev_Desc     dd;
  290.     u_char       targ;
  291.  
  292.     /*
  293.      * Sanity check
  294.      */
  295.     if ( (fd < 0) || (fd > (nslots - free_slots)) ||
  296.                      (sc_open_list[fd] == (Dev_Desc *)0) ) {
  297.         errno = EBADF;
  298.         return(-1);
  299.     }
  300.  
  301.     /*
  302.      * Make a local copy of the DDB for the affected device
  303.      */
  304.     Bcopy((char *)(sc_open_list[fd]), (char *)&dd, sizeof(dd));
  305.  
  306.     /*
  307.      * Unlink from the chain
  308.      */
  309.     if ( dd.dd_prev )
  310.         dd.dd_prev->dd_next = dd.dd_next;
  311.     if ( dd.dd_next )
  312.         dd.dd_next->dd_prev = dd.dd_prev;
  313.  
  314.     /*
  315.      * Garbage collection...
  316.      */
  317.     (void) free( (char *)(sc_open_list[fd]) );
  318.  
  319.     sc_open_list[fd]     = (Dev_Desc *)0;
  320.     num_opens[dd.dd_id] -= 1;
  321.     free_slots          += 1;
  322.  
  323.     targ = dd.dd_id | (dd.dd_lun << 3);
  324.  
  325.     /*
  326.      * If a filemark needs to be written (for sequential devs.) then
  327.      * go ahead and do it. This could return an error....
  328.      */
  329.     if ( dd.dd_wfm ) {
  330.         short   z = Scsi_File_Marks(targ, 1);
  331.         if ( z != CBYTE_OK ) {
  332.             errno = EIO;
  333.             (void) sc_dev_error(targ, z, (Dev_Desc *)0);
  334.             return(-1);
  335.         }
  336.     }
  337.  
  338.     /*
  339.      * If device was open such that it should be rewound on close, do it...
  340.      */
  341.     if ( dd.dd_mode & _SCM_REWIND_CLOSE ) {
  342.         if ( is_sequential[dd.dd_id] ) {
  343.             short   z = Scsi_Rezero(targ);
  344.             if ( z != CBYTE_OK ) {
  345.                 errno = EIO;
  346.                 (void) sc_dev_error(targ, z, (Dev_Desc *)0);
  347.                 return(-1);
  348.             }
  349.         }
  350.         /*
  351.          * Assume, (probably incorrectly!) that the caller also wanted
  352.          * the media unloaded...
  353.          */
  354.         if ( is_removable[dd.dd_id] ) {
  355.             short z = Scsi_Load_Unload(targ, 0);
  356.             if ( z != CBYTE_OK ) {
  357.                 errno = EIO;
  358.                 (void) sc_dev_error(targ, z, (Dev_Desc *)0);
  359.                 return(-1);
  360.             }
  361.         }
  362.     }
  363.  
  364.     return(0);
  365. }
  366.  
  367.  
  368. /*
  369.  * The business end. Read 'len' bytes into buffer 'buf' from the
  370.  * device associated with 'fd'.
  371.  * Note that 'len' should be a multiple of the device block size; which
  372.  * for most hard disks is 512 bytes. For tapes, this may vary, and for
  373.  * CD-ROMS, this is probably 2048 bytes. Your best bet is to issue an
  374.  * ioctl(fd, SCIO_GET) to find out the device's block size.
  375.  * Argument 'len' will be rounded DOWN to be a multiple of the device
  376.  * block size.
  377.  * Also note that 'buf' *MUST* be on an even address boundary because
  378.  * that's what the ST's DMA chip expects. I could have provided a work-
  379.  * around, but the performance would have suffered too much. So I'd
  380.  * rather insist that you keep your buffers word alligned.
  381.  */
  382. PUBLIC  long
  383. scsi_read(int fd, void *buf, long len)
  384. {
  385.     Dev_Desc    *dd;
  386.     u_char       id,
  387.                  targ,
  388.                  bshift;
  389.     u_long       bstart;
  390.     long         total = len;
  391.  
  392.     /*
  393.      * Sanity check...
  394.      */
  395.     if ( (fd < 0) || (fd > (nslots - free_slots)) ||
  396.                      (sc_open_list[fd] == (Dev_Desc *)0) ) {
  397.         errno = EBADF;
  398.         return(-1);
  399.     }
  400.  
  401.     dd = sc_open_list[fd];
  402.  
  403.     /*
  404.      * Reject requests to read a device opened Write-only (pretty dum)
  405.      * or if we've already written something to the device (in the case
  406.      * of a sequential device).
  407.      */
  408.     if ( !(dd->dd_mode & _SCM_READ) || dd->dd_wfm ) {
  409.         errno = EBADRQ;
  410.         return(-1);
  411.     }
  412.     id = dd->dd_id;
  413.  
  414.     /*
  415.      * Reject requests to read a device if we've already read a file-mark.
  416.      * In this case, the caller will either have to close then re-open the
  417.      * device, or issue an ioctl() to reposition the media. (eg. SCIO_FSF)
  418.      */
  419.     if ( is_sequential[id] && (dd->dd_eof || dd->dd_eot) )
  420.         return(0);
  421.  
  422.     /*
  423.      * Setup some useful stuff...
  424.      */
  425.     targ   = id | (dd->dd_lun << 3);
  426.     bstart = dd->dd_seek_pos;
  427.     bshift = dd->dd_blk_shift;
  428.  
  429.     /*
  430.      * Round down the supplied length to be a device block size multiple...
  431.      */
  432.     len   &= ~((long)(dd->dd_blk_size) - 1L);
  433.  
  434.     /*
  435.      * Loop until all blocks have been written...
  436.      */
  437.     while ( len > 0 ) {
  438.         u_char  dma_sects,
  439.                 dev_sects;
  440.         short   z;
  441.         long    bytes;
  442.  
  443.         /*
  444.          * ST's DMA chip always works with 512 byte blocks. So we must
  445.          * compute that number of DMA blocks to transfer this time around...
  446.          * Note that the value '252' stems from wanting 252 * 512 to be
  447.          * a multiple of 2048 bytes, for CD-ROMS etc.
  448.          */
  449.         if ( len > (252L * 512L) )
  450.             dma_sects = 252;
  451.         else
  452.             dma_sects = (u_char)(len >> 9);
  453.         /*
  454.          * From the number of DMA blocks, work out how many physical
  455.          * device blocks this represents. (For devices with 512 byte
  456.          * blocks, 'bshift' is zero. For 2048 byte blocks, 'bshift'
  457.          * is 2, etc.)
  458.          */
  459.         dev_sects = dma_sects >> bshift;
  460.  
  461.         /*
  462.          * Sequential devices need a slightly different CDB...
  463.          */
  464.         if ( is_sequential[id] )
  465.             z = Scsi_RdWr_Seq(SCSI_READ, targ, buf, dev_sects, dma_sects);
  466.         else {
  467.             u_long  bb;
  468.             z = Scsi_RdWr_Rand(SCSI_READ, targ, buf, dev_sects,
  469.                                                      dma_sects, bstart);
  470.             /*
  471.              * Get actual # of device blocks transferred...
  472.              */
  473.             bb               = (long)_Scsi_Dma_Len >> bshift;
  474.             bstart          += bb;
  475.             dd->dd_seek_pos += bb;
  476.         }
  477.  
  478.         /*
  479.          * Compute # of bytes transferred. Note: _Scsi_Dma_Len comes
  480.          * from scsismd.s and is the number of DMA blocks _actually_
  481.          * transferred.
  482.          */
  483.         bytes = (long)_Scsi_Dma_Len << 9;
  484.         len  -= bytes;
  485.  
  486.         /*
  487.          * Check completion code...
  488.          */
  489.         if ( z != CBYTE_OK ) {
  490.             if ( sc_dev_error(targ, z, dd) < 0 ) {
  491.                 errno = EIO;
  492.                 return(-1);
  493.             }
  494.             break;
  495.         }
  496.  
  497.         /*
  498.          * Point to next buffer position...
  499.          */
  500.         buf = (void *)(&(((char *)buf)[bytes]));
  501.     }
  502.  
  503.     /*
  504.      * This is a bit of a bodge to prevent writes to a sequential
  505.      * device following a read. It is probably a bad idea in the case
  506.      * of variable record length tapedrives....
  507.      */
  508.     if ( is_sequential[id] && (dd->dd_mode & _SCM_WRITE) )
  509.         dd->dd_mode &= ~(_SCM_WRITE);
  510.  
  511.     return(total - len);
  512. }
  513.             
  514.  
  515. /*
  516.  * Opposite of previous routine. Same comments apply.
  517.  */
  518. PUBLIC  long
  519. scsi_write(int fd, void *buf, long len)
  520. {
  521.     Dev_Desc    *dd;
  522.     u_char       id,
  523.                  targ,
  524.                  bshift;
  525.     u_long       bstart;
  526.     long         total = len;
  527.  
  528.     if ( (fd < 0) || (fd > (nslots - free_slots)) ||
  529.                      (sc_open_list[fd] == (Dev_Desc *)0) ) {
  530.         errno = EBADF;
  531.         return(-1);
  532.     }
  533.  
  534.     dd = sc_open_list[fd];
  535.     if ( ! (dd->dd_mode & _SCM_WRITE) ) {
  536.         errno = EBADRQ;
  537.         return(-1);
  538.     }
  539.     id = dd->dd_id;
  540.     if ( is_sequential[id] && (dd->dd_eot || dd->dd_eof) )
  541.         return(0);
  542.  
  543.     targ   = id | (dd->dd_lun << 3);
  544.     bstart = dd->dd_seek_pos;
  545.     bshift = dd->dd_blk_shift;
  546.     len   &= ~((long)(dd->dd_blk_size) - 1L);
  547.  
  548.     while ( len > 0 ) {
  549.         u_char  dma_sects,
  550.                 dev_sects;
  551.         short   z;
  552.         long    bytes;
  553.  
  554.         if ( len > (252L * 512L) )
  555.             dma_sects = 252;
  556.         else
  557.             dma_sects = (u_char)(len >> 9);
  558.         dev_sects = dma_sects >> bshift;
  559.  
  560.         if ( is_sequential[id] )
  561.             z = Scsi_RdWr_Seq(SCSI_WRITE, targ, buf, dev_sects, dma_sects);
  562.         else {
  563.             long    bb;
  564.             z = Scsi_RdWr_Rand(SCSI_WRITE, targ, buf, dev_sects,
  565.                                                       dma_sects, bstart);
  566.             bb               = (long)_Scsi_Dma_Len >> bshift;
  567.             bstart          += bb;
  568.             dd->dd_seek_pos += bb;
  569.         }
  570.  
  571.         bytes = (long)_Scsi_Dma_Len << 9;
  572.         len  -= bytes;
  573.  
  574.         if ( z != CBYTE_OK ) {
  575.             if ( sc_dev_error(targ, z, dd) < 0 ) {
  576.                 errno = EIO;
  577.                 return(-1);
  578.             }
  579.             break;
  580.         }
  581.         buf = (void *)(&(((char *)buf)[bytes]));
  582.     }
  583.  
  584.     if ( is_sequential[id] )
  585.         dd->dd_wfm = 1;
  586.  
  587.     return(total - len);
  588. }
  589.  
  590.  
  591. /*
  592.  * Seek to specific blocks on device associated with 'fd'.
  593.  * This function has no effect for sequential devices.
  594.  */
  595. PUBLIC  long
  596. scsi_lseek(int fd, long where, u_short how)
  597. {
  598.     Dev_Desc    *dd;
  599.  
  600.     /*
  601.      * Sanity check...
  602.      */
  603.     if ( (fd < 0) || (fd > (nslots - free_slots)) ||
  604.                      (sc_open_list[fd] == (Dev_Desc *)0) ) {
  605.         errno = EBADF;
  606.         return(-1);
  607.     }
  608.  
  609.     dd = sc_open_list[fd];
  610.  
  611.     /*
  612.      * Just return, if the device is sequential...
  613.      */
  614.     if ( is_sequential[dd->dd_id] )
  615.         return(0);
  616.  
  617.     /*
  618.      * The following code doesn't actually result in the device doing
  619.      * anything. It simply modifies entries in the DDB.
  620.      */
  621.     where >>= 9;
  622.     where >>= dd->dd_blk_shift;
  623.  
  624.     if ( how == 0 )             /* Seek from current position */
  625.         dd->dd_seek_pos = where;
  626.     else
  627.     if ( how == 1 )
  628.         dd->dd_seek_pos += where;
  629.     else
  630.     if ( how == 2 )
  631.         dd->dd_seek_pos  = ((long)(dd->dd_num_blocks) - 1L) - where;
  632.  
  633.     where   = dd->dd_seek_pos << dd->dd_blk_shift;
  634.     where <<= 9;
  635.  
  636.     return(where);
  637. }
  638.  
  639.  
  640. /*
  641.  * Yuck. 'ioctl()' in Unix was a nice idea, which got completely overloaded.
  642.  * Well I suppose I'll have to continue the trend since it's now more or
  643.  * less standard...
  644.  */
  645. PUBLIC  int
  646. scsi_ioctl(int fd, u_short op, long arg)
  647. {
  648.     Dev_Desc    *dd;
  649.     u_char       id, targ;
  650.     short        z;
  651.     u_char       dir;
  652.  
  653.     /*
  654.      * Sanity check...
  655.      */
  656.     if ( (fd < 0) || (fd > (nslots - free_slots)) ||
  657.                      (sc_open_list[fd] == (Dev_Desc *)0) ) {
  658.         errno = EBADF;
  659.         return(-1);
  660.     }
  661.     dd   = sc_open_list[fd];
  662.     id   = dd->dd_id;
  663.     targ = id | (dd->dd_lun << 3);
  664.  
  665.     /*
  666.      * In this code, when an operation is performed on a sequential device
  667.      * which results in the media being repositioned, we have to check if
  668.      * a file-mark is to be written. This results in a fair bit is repetative
  669.      * crud which I couldn't be bothered to extract into a function...
  670.      */
  671.     switch( op ) {
  672.       case  SCIO_REZERO:
  673.       case  SCIO_OFFLIN:
  674.             /*
  675.              * Check for sequential...
  676.              */
  677.             if ( is_sequential[id] && dd->dd_wfm ) {
  678.                 if ( (z = Scsi_File_Marks(targ, 1)) != CBYTE_OK ) {
  679.                     (void) sc_dev_error(targ, z, dd);
  680.                     if ( dd->dd_sense_key != SK_EOT )
  681.                         return(-1);
  682.                 }
  683.                 dd->dd_wfm = 0;
  684.             }
  685.  
  686.             /*
  687.              * Do the rezero.
  688.              */
  689.             if ( (z = Scsi_Rezero(targ)) != CBYTE_OK ) {
  690.                 (void) sc_dev_error(targ, z, dd);
  691.                 return(-1);
  692.             }
  693.  
  694.             /*
  695.              * Reset our 'known state'.
  696.              */
  697.             dd->dd_eof      = dd->dd_eot = 0;
  698.             dd->dd_seek_pos = 0;
  699.  
  700.             /*
  701.              * Bit more to do for OFFLINE...
  702.              */
  703.             if ( op == SCIO_OFFLIN ) {
  704.                 if ( ! is_removable[id] ) {
  705.                     errno = EIO;
  706.                     dd->dd_sense_key = SK_ILLEGAL_REQUEST;
  707.                     return(-1);
  708.                 }
  709.                 if ( (z = Scsi_Load_Unload(targ, 0)) != CBYTE_OK ) {
  710.                     errno = EIO;
  711.                     (void) sc_dev_error(targ, z, (Dev_Desc *)0);
  712.                     return(-1);
  713.                 }
  714.             }
  715.             break;
  716.  
  717.       case  SCIO_WEOF:
  718.             /*
  719.              * Only valid for sequential devices, this explicitly
  720.              * writes a file-mark.
  721.              */
  722.             if ( is_sequential[id] ) {
  723.                 if ( (z = Scsi_File_Marks(targ, 1)) != CBYTE_OK ) {
  724.                     (void) sc_dev_error(targ, z, dd);
  725.                     if ( dd->dd_sense_key != SK_EOT )
  726.                         return(-1);
  727.                 }
  728.                 dd->dd_eof = 1;
  729.                 dd->dd_wfm = 0;
  730.             }
  731.             break;
  732.  
  733.       case  SCIO_RETEN:
  734.             /*
  735.              * This is only valid for sequential devices.
  736.              */
  737.             if ( ! is_sequential[id] ) {
  738.                 errno = EIO;
  739.                 dd->dd_sense_key = SK_ILLEGAL_REQUEST;
  740.                 return(-1);
  741.             }
  742.             if ( dd->dd_wfm ) {
  743.                 if ( (z = Scsi_File_Marks(targ, 1)) != CBYTE_OK ) {
  744.                     (void) sc_dev_error(targ, z, dd);
  745.                     if ( dd->dd_sense_key != SK_EOT )
  746.                         return(-1);
  747.                 }
  748.                 dd->dd_wfm = 0;
  749.             }
  750.  
  751.             /*
  752.              * On my Archive Viper, Retension is accessed via the
  753.              * Load/Unload command. Your mileage may vary...
  754.              */
  755.             if ( (z = Scsi_Load_Unload(targ, 3)) != CBYTE_OK ) {
  756.                 (void) sc_dev_error(targ, z, dd);
  757.                 return(-1);
  758.             }
  759.             dd->dd_eof = 0;
  760.             break;
  761.  
  762.       case  SCIO_ERASE:
  763.             if ( ! is_sequential[id] ) {
  764.                 errno = EIO;
  765.                 dd->dd_sense_key = SK_ILLEGAL_REQUEST;
  766.                 return(-1);
  767.             }
  768.             if ( (z = Scsi_Erase(targ)) != CBYTE_OK ) {
  769.                 (void) sc_dev_error(targ, z, dd);
  770.                 return(-1);
  771.             }
  772.             dd->dd_eof = dd->dd_eot = dd->dd_wfm = 0;
  773.             break;
  774.  
  775.       case  SCIO_EOD:
  776.             /*
  777.              * Skip to end of recorded data on sequential media.
  778.              * On my Archive Viper, this very quickly finds the end
  779.              * of the last recorded tape file, and leaves the tape
  780.              * so that I can lay down a new file.
  781.              */
  782.             if ( ! is_sequential[id] ) {
  783.                 errno = EIO;
  784.                 dd->dd_sense_key = SK_ILLEGAL_REQUEST;
  785.                 return(-1);
  786.             }
  787.             if ( dd->dd_wfm ) {
  788.                 if ( (z = Scsi_File_Marks(targ, 1)) != CBYTE_OK ) {
  789.                     (void) sc_dev_error(targ, z, dd);
  790.                     if ( dd->dd_sense_key != SK_EOT )
  791.                         return(-1);
  792.                 }
  793.                 dd->dd_wfm = 0;
  794.             }
  795.             if ( (z = Scsi_Space(targ, 0, 3)) != CBYTE_OK ) {
  796.                 (void) sc_dev_error(targ, z, dd);
  797.                 return(-1);
  798.             }
  799.             dd->dd_eof = dd->dd_eot = 0;
  800.             break;
  801.  
  802.       /*
  803.        * the following 4 ioctls implement FSF (Forward Skip File),
  804.        * BSF (Backward skip file), FSR (Forward skip record) and
  805.        * BSR (Backward skip record). One argument is expected, which
  806.        * should be the number of filemarks/records to skip.
  807.        */
  808.       case  SCIO_FSF:
  809.       case  SCIO_BSF:   dir = 1;
  810.                         goto skippy;
  811.       case  SCIO_FSR:
  812.       case  SCIO_BSR:   dir = 0;
  813. skippy:   {
  814.             long    where = arg;
  815.             if ( ! is_sequential[id] ) {
  816.                 errno = EIO;
  817.                 dd->dd_sense_key = SK_ILLEGAL_REQUEST;
  818.                 return(-1);
  819.             }
  820.             if ( dd->dd_wfm ) {
  821.                 if ( (z = Scsi_File_Marks(targ, 1)) != CBYTE_OK ) {
  822.                     (void) sc_dev_error(targ, z, dd);
  823.                     if ( dd->dd_sense_key != SK_EOT )
  824.                         return(-1);
  825.                 }
  826.                 dd->dd_wfm = 0;
  827.             }
  828.             if ( (op == SCIO_BSF) || (op == SCIO_BSR) )
  829.                 where = -where;
  830.             if ( (z = Scsi_Space(targ, where, dir)) != CBYTE_OK ) {
  831.                 (void) sc_dev_error(targ, z, dd);
  832.                 return(-1);
  833.             }
  834.             if ( op == SCIO_FSF )
  835.                 dd->dd_eof = dd->dd_eot = 0;
  836.             else
  837.             if ( op == SCIO_BSF )
  838.                 dd->dd_eof = 1;
  839.             else
  840.                 dd->dd_eof = 0;
  841.           }
  842.             break;
  843.  
  844.       case  SCIO_GET:
  845.             /*
  846.              * This allows programmers to get device specific details
  847.              * into a data structure. The argument is a pointer to a
  848.              * structure of type 'Sc_Desc'.
  849.              */
  850.             {
  851.             Sc_Desc *sc = (Sc_Desc *)arg;
  852.  
  853.             sc->sc_id        = dd->dd_id;
  854.             sc->sc_lun       = dd->dd_lun;
  855.             sc->sc_type      = dd->dd_type;
  856.             sc->sc_flags     = dd->dd_flags;
  857.             sc->sc_sense_key = dd->dd_sense_key;
  858.             sc->sc_sense_blk = dd->dd_sense_blk;
  859.             sc->sc_blocks    = dd->dd_num_blocks;
  860.             sc->sc_block_sz  = dd->dd_blk_size;
  861.             Strncpy(sc->sc_vendor, dd->dd_vendor, sizeof(sc->sc_vendor));
  862.             Strncpy(sc->sc_product, dd->dd_product, sizeof(sc->sc_product));
  863.             Strncpy(sc->sc_revision, dd->dd_revision, sizeof(sc->sc_revision));
  864.             }
  865.             break;
  866.  
  867.       default:
  868.             errno = EBADRQ;
  869.             return(-1);
  870.     }
  871.  
  872.     return(0);
  873. }
  874.  
  875.  
  876. /*----------------------------------------------------------------------*/
  877. /* Private 'service' functions....                                      */
  878. /*----------------------------------------------------------------------*/
  879.  
  880. /*
  881.  * Come here if a SCSI error occurs. This function sorts out what kind
  882.  * of error it is, and writes the result in the DDB.
  883.  */
  884. PRIVATE int
  885. sc_dev_error(u_char targ, short z, Dev_Desc *dd)
  886. {
  887.     Sc_X_Sense   xs;
  888.     Sc_Sense    *ss = (Sc_Sense *)&xs;
  889.  
  890.     if ( z != CBYTE_OK ) {
  891.         if ( z == CBYTE_TIMEOUT ) {
  892.             if ( dd != (Dev_Desc *)0 ) dd->dd_sense_key = SK_TIMEOUT;
  893.             errno = EIO;
  894.             return(0);
  895.         } else
  896.         if ( z & CBYTE_BUSY ) {
  897.             if ( dd != (Dev_Desc *)0 ) dd->dd_sense_key = SK_BUSY;
  898.             errno = EIO;
  899.             return(0);
  900.         }
  901.     }
  902.  
  903.     if ( (z = Scsi_Request_Sense(targ, &xs, sizeof(xs))) != CBYTE_OK ) {
  904.         errno = EIO;
  905.         return(-1);
  906.     }
  907.  
  908.     if ( dd == (Dev_Desc *)0 )
  909.         return(0);
  910.  
  911.     if ( xs.ss_class == 0x07 ) {
  912.         if ( xs.ss_fm ) {
  913.             dd->dd_sense_key = SK_EOF;
  914.             dd->dd_eof       = 1;
  915.         } else
  916.         if ( xs.ss_eom ) {
  917.             dd->dd_sense_key = SK_EOT;
  918.             dd->dd_eot       = 1;
  919.         } else
  920.             dd->dd_sense_key = xs.ss_sensekey;
  921.         dd->dd_sense_blk = soft_errors(xs);
  922.     } else {
  923.         dd->dd_sense_key = ss->ss_code;
  924.         dd->dd_sense_blk = (u_long)(ss->ss_addr);
  925.     }
  926.  
  927.     return(0);
  928. }
  929.     
  930.  
  931. /*
  932.  * This function queries the specified target for information as to
  933.  * what type of device it is, how many blocks it has, how many bytes
  934.  * per block etc.
  935.  */
  936. PRIVATE Dev_Desc *
  937. get_dev_info(u_char targ)
  938. {
  939.     u_char          id = targ & 0x07;
  940.     Sc_Inquire      iq;
  941.     Sc_Mode_Sense   ms;
  942.     short           z;
  943.     PRIVATE         Dev_Desc    dd;
  944.  
  945.     if ( (z = Scsi_Mode_Sense(targ, 0, &ms, sizeof(ms))) != CBYTE_OK )
  946.         return((Dev_Desc *)0);
  947.     if ( (z = Scsi_Inquire(targ, &iq, sizeof(iq))) != CBYTE_OK )
  948.         return((Dev_Desc *)0);
  949.  
  950.     is_sequential[id] = (iq.iq_type == DTYPE_SEQUENTIAL);
  951.     is_removable [id] = (iq.iq_remove == 1);
  952.     is_write_prot[id] = (ms.ms_write_prot == 1);
  953.  
  954.     dd.dd_type   = iq.iq_type;
  955.     dd.dd_flags |= (is_removable [id] ? SCF_IS_REMOVABLE : 0);
  956.     dd.dd_flags |= (is_write_prot[id] ? SCF_IS_WRITABLE  : 0);
  957.  
  958.     if ( ms.ms_bdesc_len >= sizeof(ms.ms_bd) ) {
  959.         dd.dd_num_blocks = ms.ms_blocks;
  960.         dd.dd_blk_size   = ms.ms_blk_len;
  961.     } else {
  962.         dd.dd_num_blocks = 0;
  963.         dd.dd_blk_size   = 0x200;
  964.     }
  965.  
  966.     Strncpy(dd.dd_vendor, iq.iq_vendor, sizeof(dd.dd_vendor));
  967.     Strncpy(dd.dd_product, iq.iq_product, sizeof(dd.dd_product));
  968.     Strncpy(dd.dd_revision, iq.iq_revision, sizeof(dd.dd_revision));
  969.  
  970.     return(&dd);
  971. }
  972.  
  973.  
  974.  
  975. PRIVATE short
  976. check_numeric(char *str)
  977. {
  978.     while (*str) {
  979.         if ( (*str < '0') || (*str++ > '9') )
  980.             return(0);
  981.     }
  982.     return(1);
  983. }
  984.  
  985.  
  986. PRIVATE short
  987. get_cfile(void)
  988. {
  989.     char    *s = "scsi.cnf";
  990.     short    z = 0;
  991.  
  992.     if ( !access(s, 0) )
  993.         z = read_config_file(s);
  994.     else if ( (s = (char *)getenv("SCSI_CONFIG_FILE")) && *s
  995.             && !access(s, 0) )
  996.         z = read_config_file(s);
  997.  
  998.     return(z);
  999. }
  1000.  
  1001.  
  1002. PRIVATE short
  1003. read_config_file(char *fn)
  1004. {
  1005.     char   *s;
  1006.     char    str[32];
  1007.     short   z;
  1008.  
  1009.     cfiled = (short)config_file(CFILE_OPEN, fn);
  1010.  
  1011.     if ( cfiled < 0 )
  1012.         return(-1);
  1013.  
  1014.     for (z = 0; z < (MAX_SCSI_ID + 1); z++) {
  1015.         (void)sprintf(str, "SCSI_TIMEOUT_%d", z);
  1016.         if ( (s = (char *)config_file(CFILE_SEARCH, cfiled, str)) != 0 ) {
  1017.             if ( Scsi_Set_Timeouts(z, s, 0L) != (char *)0 )
  1018.                 return(-1);
  1019.         }
  1020.     }
  1021.     return(0);
  1022. }
  1023.