text: /*
   * Amanda, The Advanced Maryland Automatic Network Disk Archiver
   * Copyright (c) 1991-2000 University of Maryland at College Park
   * All Rights Reserved.
   *
   * Permission to use, copy, modify, distribute, and sell this software and its
   * documentation for any purpose is hereby granted without fee, provided that
   * the above copyright notice appear in all copies and that both that
   * copyright notice and this permission notice appear in supporting
   * documentation, and that the name of U.M. not be used in advertising or
   * publicity pertaining to distribution of the software without specific,
   * written prior permission.  U.M. makes no representations about the
   * suitability of this software for any purpose.  It is provided "as is"
   * without express or implied warranty.
   *
   * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
   * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
   * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
   * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   *
   * Authors: the Amanda Development Team.  Its members are listed in a
   * file named AUTHORS, in the root directory of this distribution.
   */
  /*
   * $Id: scsi-aix.c,v 1.23 2006/05/25 01:47:07 johnfranks Exp $
   *
   * Interface to execute SCSI commands on an AIX System
   *
   * Copyright (c) Thomas Hepper th@ant.han.de
   */
  
  
  #include <amanda.h>
  
  #include <scsi-defs.h>
  
  #ifdef HAVE_SYS_TYPES_H
  #include <sys/types.h>
  #endif
  #ifdef HAVE_STAT_H
  #include <sys/stat.h>
  #endif
  #ifdef HAVE_FCNTL_H
  #include <fcntl.h>
  #endif
  #ifdef HAVE_UNISTD_H
  #include <unistd.h>
  #endif
  #ifdef HAVE_STDIO_H
  #include <stdio.h>
  #endif
  #ifdef HAVE_ERRNO_H
  #include <errno.h>
  #endif
  
  #include <sys/scarray.h>
  #include <sys/tape.h>
  
  #include <scsi-defs.h>
  #include <gscdds.h>
  
  void SCSI_OS_Version(void)
  {
  #ifndef lint
     static char rcsid[] = "$Id: scsi-aix.c,v 1.23 2006/05/25 01:47:07 johnfranks Exp $";
     DebugPrint(DEBUG_INFO, SECTION_INFO, "scsi-os-layer: %s\n",rcsid);
  #endif
  }
  
  
  int SCSI_OpenDevice(int ip)
  {
    int DeviceFD;
    int i;
    extern OpenFiles_T *pDev;
    
    if (pDev[ip].inqdone == 0)
      {
        pDev[ip].inqdone = 1;
        /*
         * Check if it is an gsc (generic SCSI device)
         */
        if (strncmp("/dev/gsc", pDev[ip].dev, 8) == 0)
          {
  	  pDev[ip].flags = AIX_USE_GSC;
            DeviceFD = open(pDev[ip].dev, 0);
          } else {
            DeviceFD = openx(pDev[ip].dev, O_RDWR, 0, SC_DIAGNOSTIC);
          }
        if (DeviceFD >= 0)
  	{
  	  pDev[ip].avail = 1;
            pDev[ip].fd = DeviceFD;
            pDev[ip].SCSI = 0;
            pDev[ip].devopen = 1;
            pDev[ip].inquiry = (SCSIInquiry_T *)malloc(INQUIRY_SIZE);
  
            if (SCSI_Inquiry(ip, pDev[ip].inquiry, INQUIRY_SIZE) == 0)
  	    {
               if (pDev[ip].inquiry->type == TYPE_TAPE || pDev[ip].inquiry->type == TYPE_CHANGER)
                 {
                   for (i=0;i < 16;i++)
                     pDev[ip].ident[i] = pDev[ip].inquiry->prod_ident[i];
                   for (i=15; i >= 0 && !isalnum(pDev[ip].ident[i]) ; i--)
                     {
                       pDev[ip].ident[i] = '\0';
                     }
                   pDev[ip].SCSI = 1;
  
  		  if (pDev[ip].inquiry->type == TYPE_TAPE)
  		  {
  		          pDev[ip].type = stralloc("tape");
  		  }
  
  		  if (pDev[ip].inquiry->type == TYPE_CHANGER)
  		  {
  		          pDev[ip].type = stralloc("changer");
  		  }
  
                   PrintInquiry(pDev[ip].inquiry);
                   return(1); 
                 } else {
                   close(DeviceFD);
                   free(pDev[ip].inquiry);
                   return(0);
                 }
             }
             free(pDev[ip].inquiry);
             pDev[ip].inquiry = NULL;
             return(1);
         } else {
  	 dbprintf(_("SCSI_OpenDevice %s failed\n"), pDev[ip].dev);
           return(0);
         }
      } else {
        if ((DeviceFD = openx(pDev[ip].dev, O_RDWR, 0, SC_DIAGNOSTIC)) >= 0)
          {
            pDev[ip].fd = DeviceFD;
            pDev[ip].devopen = 1;
            return(1);
          }
      }
    return(0);
  }
  
  int SCSI_CloseDevice(int DeviceFD)
  {
    int ret;
    extern OpenFiles_T *pDev;
  
    ret = close(pDev[DeviceFD].fd);
    pDev[DeviceFD].devopen = 0;
    return(ret);
  
  }
  
  int SCSI_ExecuteCommand(int DeviceFD,
                          Direction_T Direction,
                          CDB_T CDB,
                          size_t CDB_Length,
                          void *DataBuffer,
                          size_t DataBufferLength,
                          RequestSense_T *RequestSenseBuf,
                          size_t RequestSenseLength)
  {
    extern OpenFiles_T *pDev;
    extern FILE * debug_file;
    CDB_T CDBSENSE;
    CDB_T SINQ;
    ExtendedRequestSense_T ExtendedRequestSense;
    struct sc_iocmd ds;
    scmd_t scmd;
    char sbyte;
    int Result;
    int isbusy = 0;
    int target = 3;
  
    /* Basic sanity checks */
    assert(CDB_Length <= UCHAR_MAX);
    assert(RequestSenseLength <= UCHAR_MAX);
  
    /* Clear buffer for cases where sense is not returned */
    memset(RequestSenseBuf, 0, RequestSenseLength);
  
    if (pDev[DeviceFD].avail == 0)
      {
        return(SCSI_ERROR);
      }
  
  
    if (pDev[DeviceFD].flags == AIX_USE_GSC)
      {
        scmd.cdb = CDB;
        scmd.cdblen = CDB_Length;
        scmd.data_buf = DataBuffer;
        scmd.datalen = DataBufferLength;
        scmd.sense_buf = (unsigned char *)RequestSenseBuf;
        scmd.senselen = RequestSenseLength;
        scmd.statusp = &sbyte;
        scmd.timeval = 60;
        switch (Direction) 
          {
          case Input:
            scmd.rw = 0;
            break;
          case Output:
            scmd.rw = 1;
            break;
          }
  
        if (ioctl(pDev[DeviceFD].fd, GSC_CMD, (caddr_t) &scmd) < 0) {
          return(SCSI_ERROR);
        }
        return(SCSI_OK);
  
      } else {
        bzero(&ds, SIZEOF(struct sc_iocmd));
        bzero(RequestSenseBuf, RequestSenseLength);
        bzero(&ExtendedRequestSense, SIZEOF(ExtendedRequestSense_T));
        
        ds.flags = SC_ASYNC; 
        /* Timeout */
        ds.timeout_value = 60;
        bcopy(CDB, ds.scsi_cdb, CDB_Length);
        ds.command_length = CDB_Length;
        /* 
         *  Data buffer for results 
         * If the size of the buffer is 0
         * then keep this fields untouched           
         */
        if (DataBufferLength > 0)
          {
            ds.buffer = DataBuffer;
            ds.data_length = DataBufferLength;
          }
        
        /* Sense Buffer is not available on AIX ?*/
        /*
          ds.req_sense_length = 255;
          ds.request_sense_ptr = (unsigned char *)RequestSense;
        */
        switch (Direction) 
          {
          case Input:
            ds.flags = ds.flags | B_READ;
            break;
          case Output:
            ds.flags = ds.flags | B_WRITE;
            break;
          }
        DecodeSCSI(CDB, "SCSI_ExecuteCommand : ");
        
        if (pDev[DeviceFD].devopen == 0)
          if (SCSI_OpenDevice(DeviceFD) == 0)
            return(SCSI_ERROR);
        Result = ioctl(pDev[DeviceFD].fd, STIOCMD, &ds);
        SCSI_CloseDevice(DeviceFD);
        
        if ( Result < 0)
          {
            switch (ds.scsi_bus_status)
              {
              case SC_GOOD_STATUS:
                SINQ[0] = SC_COM_REQUEST_SENSE;
                SINQ[1] = 0; 
                SINQ[2] = 0;
                SINQ[3] = 0;
                SINQ[4] = 0x1D;
                SINQ[5] = 0x80;
                bcopy(SINQ, ds.scsi_cdb, 6);
                ds.command_length = 6;
                ds.buffer = (unsigned char *)RequestSenseBuf;
                ds.data_length = RequestSenseLength;
                
                if (pDev[DeviceFD].devopen == 0)
                  if (SCSI_OpenDevice(DeviceFD) == 0)
                    return(SCSI_ERROR);
                Result = ioctl(pDev[DeviceFD].fd, STIOCMD, &ds);
                SCSI_CloseDevice(DeviceFD);
                return(SCSI_OK);
  
              case SC_BUSY_STATUS:
                return(SCSI_BUSY);
  
              case SC_CHECK_CONDITION:
                SINQ[0] = SC_COM_REQUEST_SENSE;
                SINQ[1] = 0; 
                SINQ[2] = 0;
                SINQ[3] = 0;
                SINQ[4] = 0x1D;
                SINQ[5] = 0x80;
                bcopy(SINQ, ds.scsi_cdb, 6);
                ds.command_length = 6;
                ds.buffer = (unsigned char *)RequestSenseBuf;
                ds.data_length = (unsigned char)RequestSenseLength;
  
                if (pDev[DeviceFD].devopen == 0)
                  if (SCSI_OpenDevice(DeviceFD) == 0)
                    return(SCSI_ERROR);
                Result = ioctl(pDev[DeviceFD].fd, STIOCMD, &ds);
                SCSI_CloseDevice(DeviceFD);
                return(SCSI_CHECK);
  
              default:
                /*
                 * Makes no sense yet, may result in an endless loop
                 *
                 RequestSense(DeviceFD, &ExtendedRequestSense, 0);
                 DecodeExtSense(&ExtendedRequestSense, "SCSI_ExecuteCommand:", debug_file);
                 bcopy(&ExtendedRequestSense, RequestSenseBuf, RequestSenseLength);
                */
                dbprintf(_("ioctl on %d return %d\n"), pDev[DeviceFD].fd, Result);
                dbprintf(_("ret: %d errno: %d (%s)\n"), Result, errno, "");
                dbprintf(_("data_length:     %d\n"), ds.data_length);
                dbprintf(_("buffer:          0x%X\n"), ds.buffer);
                dbprintf(_("timeout_value:   %d\n"), ds.timeout_value);
                dbprintf(_("status_validity: %d\n"), ds.status_validity);
                dbprintf(_("scsi_bus_status: 0x%X\n"), ds.scsi_bus_status);
                dbprintf(_("adapter_status:  0x%X\n"), ds.adapter_status);
                dbprintf(_("adap_q_status:   0x%X\n"), ds.adap_q_status);
                dbprintf(_("q_tag_msg:       0x%X\n"), ds.q_tag_msg);
                dbprintf(_("flags:           0X%X\n"), ds.flags);
                return(SCSI_ERROR);
              }
          }
        return(SCSI_OK);
      }
  }
  
  int SCSI_Scan(void)
  {
    int fd;
    struct sc_inquiry si;
    u_char buf[255];
    int target;
    int lun;
    int isbusy;
    char type;
    char bus[] = "/dev/scsi0";
    
    if ((fd = open(bus, O_RDWR)) == -1)
      return(1);
  
    for (target = 0; target < 7; target++) 
      {
        for (lun = 0; lun < 7; lun++)
          {
            g_printf(_("Target:Lun %d:%d\n"), target,lun);
            if (ioctl(fd, SCIOSTART, IDLUN(target, lun)) == -1) {
              if (errno == EINVAL) {
                g_printf(_("is in use\n"));
                isbusy = 1;
              } else {
                return(1);
              }
            } else {
              isbusy = 0;
            }
            
            bzero(&si, SIZEOF(si));
            si.scsi_id = target;
            si.lun_id = lun;
            si.inquiry_len = 255;
            si.inquiry_ptr = (char *)&buf;
            if (ioctl(fd, SCIOINQU, &si) == -1)
              {
                g_printf("SCIOINQU: %s\n", strerror(errno));
              } else {
                dump_hex(&buf, 255, DEBUG_INFO, SECTION_SCSI);
                type = buf[0] & 0x1lf;
                buf[8+28] = 0;
                g_printf(_("%-28s|Device Type %d\n"),buf[8], type);
              }
            if (!isbusy && ioctl(fd, SCIOSTOP, IDLUN(target, lun)) == -1)
              return(1);
          }
      }
  }
  
  int Tape_Ioctl(int DeviceFD, int command)
  {
    extern OpenFiles_T *pDev;
    int ret = -1;
    return(ret);
  }
  
  int Tape_Status( int DeviceFD)
  {
  /*
    Not yet
  */
    return(-1);
  }
  
  int ScanBus(int print)
  {
  /*
    Not yet
  */
    SCSI_Scan();
    return(-1);
  }
  
  /*
   * Local variables:
   * indent-tabs-mode: nil
   * c-file-style: gnu
   * End:
   */
  