Skip to content
Snippets Groups Projects
Select Git revision
  • cc28dbd0189d45c7c3b75dff5dd48c3abb7feefd
  • trunk
  • RELEASE_6_5_DRIVEDB
  • RELEASE_6_6_DRIVEDB
  • RELEASE_7_0_DRIVEDB
  • RELEASE_7_2_DRIVEDB
  • RELEASE_7_3_DRIVEDB
  • RELEASE_6_0_DRIVEDB
  • RELEASE_6_1_DRIVEDB
  • RELEASE_6_2_DRIVEDB
  • RELEASE_6_3_DRIVEDB
  • RELEASE_6_4_DRIVEDB
  • tags/RELEASE_7_4
  • tags/RELEASE_7_3
  • RELEASE_5_41_DRIVEDB
  • RELEASE_5_42_DRIVEDB
  • RELEASE_5_43_DRIVEDB
  • tags/RELEASE_7_2
  • tags/RELEASE_7_1
  • tags/RELEASE_7_0
  • RELEASE_5_40_DRIVEDB
21 results

autogen.sh

Blame
  • dev_areca.cpp 19.57 KiB
    /*
     * dev_areca.cpp
     *
     * Home page of code is: http://www.smartmontools.org
     *
     * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
     *
     * SPDX-License-Identifier: GPL-2.0-or-later
     */
    
    #include "config.h"
    
    #include "dev_interface.h"
    #include "dev_areca.h"
    
    const char * dev_areca_cpp_cvsid = "$Id$"
      DEV_ARECA_H_CVSID;
    
    #include "atacmds.h"
    #include "scsicmds.h"
    
    #include <errno.h>
    
    #if 0 // For debugging areca code
    static void dumpdata(unsigned char *block, int len)
    {
      int ln = (len / 16) + 1;   // total line#
      unsigned char c;
      int pos = 0;
    
      printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);
      printf("      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      ASCII      \n");
      printf("=====================================================================\n");
    
      for ( int l = 0; l < ln && len; l++ )
      {
        // printf the line# and the HEX data
        // if a line data length < 16 then append the space to the tail of line to reach 16 chars
        printf("%02X | ", l);
        for ( pos = 0; pos < 16 && len; pos++, len-- )
        {
          c = block[l*16+pos];
          printf("%02X ", c);
        }
    
        if ( pos < 16 )
        {
          for ( int loop = pos; loop < 16; loop++ )
          {
            printf("   ");
          }
        }
    
        // print ASCII char
        for ( int loop = 0; loop < pos; loop++ )
        {
          c = block[l*16+loop];
          if ( c >= 0x20 && c <= 0x7F )
          {
            printf("%c", c);
          }
          else
          {
            printf(".");
          }
        }
        printf("\n");
      }
      printf("=====================================================================\n");
    }
    #endif
    
    generic_areca_device::generic_areca_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
    : smart_device(intf, dev_name, "areca", "areca"),
      m_disknum(disknum),
      m_encnum(encnum)
    {
      set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
    }
    
    generic_areca_device::~generic_areca_device() throw()
    {
    
    }
    
    // PURPOSE
    //   This is an interface routine meant to isolate the OS dependent
    //   parts of the code, and to provide a debugging interface.  Each
    //   different port and OS needs to provide it's own interface.  This
    //   is the Windows interface to the Areca "arcmsr" driver.  It allows ATA
    //   commands to be passed through the SCSI driver.
    // DETAILED DESCRIPTION OF ARGUMENTS
    //   fd: is the file descriptor provided by open()
    //   disknum is the disk number (0 to 127) in the RAID array
    //   command: defines the different operations.
    //   select: additional input data if needed (which log, which type of
    //           self-test).
    //   data:   location to write output data, if needed (512 bytes).
    //   Note: not all commands use all arguments.
    // RETURN VALUES
    //  -1 if the command failed
    //   0 if the command succeeded,
    //   STATUS_CHECK routine:
    //  -1 if the command failed
    //   0 if the command succeeded and disk SMART status is "OK"
    //   1 if the command succeeded and disk SMART status is "FAILING"
    int generic_areca_device::arcmsr_command_handler(unsigned long arcmsr_cmd, unsigned char *data, int data_len)
    {
      if (arcmsr_cmd >= ARCMSR_CMD_TOTAL)
        return -1;
    
      static const unsigned int cmds[ARCMSR_CMD_TOTAL] =
      {
        ARCMSR_IOCTL_READ_RQBUFFER,
        ARCMSR_IOCTL_WRITE_WQBUFFER,
        ARCMSR_IOCTL_CLEAR_RQBUFFER,
        ARCMSR_IOCTL_CLEAR_WQBUFFER,
        ARCMSR_IOCTL_RETURN_CODE_3F
      };
    
      int ioctlreturn = 0;
      sSRB_BUFFER sBuf;
      struct scsi_cmnd_io iop;
      int dir = DXFER_TO_DEVICE;
    
      uint8_t cdb[10]={0};
      uint8_t sense[32]={0};
    
      unsigned char *areca_return_packet;
      int total = 0;
      int expected = -1;
      unsigned char return_buff[2048]={0};
      unsigned char *ptr = &return_buff[0];
    
      memset((unsigned char *)&sBuf, 0, sizeof(sBuf));
      memset(&iop, 0, sizeof(iop));
    
      sBuf.srbioctl.HeaderLength = sizeof(sARCMSR_IO_HDR);
      memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));
      sBuf.srbioctl.Timeout = 10000;
      sBuf.srbioctl.ControlCode = cmds[arcmsr_cmd];
    
      switch ( arcmsr_cmd )
      {
      // command for writing data to driver
      case ARCMSR_WRITE_WQBUFFER:
        if ( data && data_len )
        {
          sBuf.srbioctl.Length = data_len;
          memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len);
        }
        /* FALLTHRU */
        // commands for clearing related buffer of driver
      case ARCMSR_CLEAR_RQBUFFER:
      case ARCMSR_CLEAR_WQBUFFER:
        cdb[0] = 0x3B; //SCSI_WRITE_BUF command;
        break;
      // command for reading data from driver
      case ARCMSR_READ_RQBUFFER:
      // command for identifying driver
      case ARCMSR_RETURN_CODE_3F:
        cdb[0] = 0x3C; //SCSI_READ_BUF command;
        dir = DXFER_FROM_DEVICE;
        break;
      default:
        // unknown arcmsr commands
        return -1;
      }
    
      cdb[1] = 0x01;
      cdb[2] = 0xf0;
      cdb[5] = cmds[arcmsr_cmd] >> 24;
      cdb[6] = cmds[arcmsr_cmd] >> 16;
      cdb[7] = cmds[arcmsr_cmd] >> 8;
      cdb[8] = cmds[arcmsr_cmd] & 0x0F;
    
      iop.dxfer_dir = dir;
      iop.dxfer_len = sizeof(sBuf);
      iop.dxferp = (unsigned char *)&sBuf;
      iop.cmnd = cdb;
      iop.cmnd_len = sizeof(cdb);
      iop.sensep = sense;
      iop.max_sense_len = sizeof(sense);
      iop.timeout = SCSI_TIMEOUT_DEFAULT;
    
      while ( 1 )
      {
        ioctlreturn = arcmsr_do_scsi_io(&iop);
        if(ioctlreturn || iop.scsi_status)
        {
          break;
        }
    
        if ( arcmsr_cmd != ARCMSR_READ_RQBUFFER )
        {
          // if succeeded, just returns the length of outgoing data
          return data_len;
        }
    
        if ( sBuf.srbioctl.Length )
        {
          memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
          ptr += sBuf.srbioctl.Length;
          total += sBuf.srbioctl.Length;
          // the returned bytes enough to compute payload length ?
          if ( expected < 0 && total >= 5 )
          {
            areca_return_packet = (unsigned char *)&return_buff[0];
            if ( areca_return_packet[0] == 0x5E &&
               areca_return_packet[1] == 0x01 &&
               areca_return_packet[2] == 0x61 )
            {
              // valid header, let's compute the returned payload length,
              // we expected the total length is
              // payload + 3 bytes header + 2 bytes length + 1 byte checksum
              expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6;
            }
          }
    
          if ( total >= 7 && total >= expected )
          {
            //printf("total bytes received = %d, expected length = %d\n", total, expected);
    
            // ------ Okay! we received enough --------
            break;
          }
        }
      }
    
      // Deal with the different error cases
      if ( arcmsr_cmd == ARCMSR_RETURN_CODE_3F )
      {
        // Silence the ARCMSR_IOCTL_RETURN_CODE_3F's error, no pout(...)
        return -4;
      }
    
      if ( ioctlreturn )
      {
        pout("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn);
        return -2;
      }
    
      if ( iop.scsi_status )
      {
        pout("io_hdr.scsi_status with write buffer failed code = %x\n", iop.scsi_status);
        return -3;
      }
    
      if ( data )
      {
        memcpy(data, return_buff, total);
      }
    
      return total;
    }
    
    bool generic_areca_device::arcmsr_probe()
    {
      if(!is_open())
      {
        open();
      }
    
      if(arcmsr_command_handler(ARCMSR_RETURN_CODE_3F, NULL, 0) != 0)
      {
        return false;
      }
      return true;
    }
    
    int generic_areca_device::arcmsr_ui_handler(unsigned char *areca_packet, int areca_packet_len, unsigned char *result)
    {
      int expected = 0;
      unsigned char return_buff[2048];
      unsigned char cs = 0;
      int cs_pos = 0;
    
      // ----- ADD CHECKSUM -----
      cs_pos = areca_packet_len - 1;
      for(int i = 3; i < cs_pos; i++)
      {
          areca_packet[cs_pos] += areca_packet[i];
      }
    
      if(!arcmsr_lock())
      {
        return -1;
      }
      expected = arcmsr_command_handler(ARCMSR_CLEAR_RQBUFFER, NULL, 0);
      if (expected==-3) {
        return set_err(EIO);
      }
      arcmsr_command_handler(ARCMSR_CLEAR_WQBUFFER, NULL, 0);
      expected = arcmsr_command_handler(ARCMSR_WRITE_WQBUFFER, areca_packet, areca_packet_len);
      if ( expected > 0 )
      {
        expected = arcmsr_command_handler(ARCMSR_READ_RQBUFFER, return_buff, sizeof(return_buff));
      }
    
      if ( expected < 3 + 1 ) // Prefix + Checksum
      {
        return -1;
      }
    
      if(!arcmsr_unlock())
      {
        return -1;
      }
    
      // ----- VERIFY THE CHECKSUM -----
      cs = 0;
      for ( int loop = 3; loop < expected - 1; loop++ )
      {
          cs += return_buff[loop];
      }
    
      if ( return_buff[expected - 1] != cs )
      {
        return -1;
      }
    
      memcpy(result, return_buff, expected);
    
      return expected;
    }
    
    int generic_areca_device::arcmsr_get_controller_type()
    {
      int expected = 0;
      unsigned char return_buff[2048];
      unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x01, 0x00, 0x23, 0x00};
    
      memset(return_buff, 0, sizeof(return_buff));
      expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff);
      if ( expected < 0 )
      {
        return -1;
      }
    
      return return_buff[0xc2];
    }
    
    int generic_areca_device::arcmsr_get_dev_type()
    {
      int expected = 0;
      unsigned char return_buff[2048];
      int ctlr_type = -1;
      int encnum = get_encnum();
      int disknum = get_disknum();
      unsigned char areca_packet[] = {0x5E, 0x01, 0x61, 0x03, 0x00, 0x22,
        (unsigned char)(disknum - 1), (unsigned char)(encnum - 1), 0x00};
    
      memset(return_buff, 0, sizeof(return_buff));
      expected = arcmsr_ui_handler(areca_packet, sizeof(areca_packet), return_buff);
      if ( expected < 0 )
      {
        return -1;
      }
    
      ctlr_type = arcmsr_get_controller_type();
    
      if( ctlr_type < 0 )
      {
        return ctlr_type;
      }
    
      if( ctlr_type == 0x02/* SATA Controllers */ ||
         (ctlr_type == 0x03 /* SAS Controllers */ && return_buff[0x52] & 0x01 /* SATA devices behind SAS Controller */) )
      {
        // SATA device
        return 1;
      }
    
      // SAS device
      return 0;
    }
    
    bool generic_areca_device::arcmsr_ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
    {
      // ATA input registers
      typedef struct _ATA_INPUT_REGISTERS
      {
        unsigned char features;
        unsigned char sector_count;
        unsigned char sector_number;
        unsigned char cylinder_low;
        unsigned char cylinder_high;
        unsigned char device_head;
        unsigned char command;
        unsigned char reserved[8];
        unsigned char data[512]; // [in/out] buffer for outgoing/incoming data
      } sATA_INPUT_REGISTERS;
    
      // ATA output registers
      // Note: The output registers is re-sorted for areca internal use only
      typedef struct _ATA_OUTPUT_REGISTERS
      {
        unsigned char error;
        unsigned char status;
        unsigned char sector_count;
        unsigned char sector_number;
        unsigned char cylinder_low;
        unsigned char cylinder_high;
      } sATA_OUTPUT_REGISTERS;
    
      // Areca packet format for outgoing:
      // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
      // B[3~4] : 2 bytes command length + variant data length, little endian
      // B[5]   : 1 bytes areca defined command code, ATA passthrough command code is 0x1c
      // B[6~last-1] : variant bytes payload data
      // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
      //
      //
      //   header 3 bytes  length 2 bytes   cmd 1 byte    payload data x bytes  cs 1 byte
      // +--------------------------------------------------------------------------------+
      // + 0x5E 0x01 0x61 |   0x00 0x00   |     0x1c   | .................... |   0x00    |
      // +--------------------------------------------------------------------------------+
      //
    
      //Areca packet format for incoming:
      // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
      // B[3~4] : 2 bytes payload length, little endian
      // B[5~last-1] : variant bytes returned payload data
      // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
      //
      //
      //   header 3 bytes  length 2 bytes   payload data x bytes  cs 1 byte
      // +-------------------------------------------------------------------+
      // + 0x5E 0x01 0x61 |   0x00 0x00   | .................... |   0x00    |
      // +-------------------------------------------------------------------+
      unsigned char    areca_packet[640];
      int areca_packet_len = sizeof(areca_packet);
      unsigned char return_buff[2048];
      int expected = 0;
    
      sATA_INPUT_REGISTERS *ata_cmd;
    
      // For debugging
    #if 0
      memset(sInq, 0, sizeof(sInq));
      scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq));
      dumpdata((unsigned char *)sInq, sizeof(sInq));
    #endif
      memset(areca_packet, 0, areca_packet_len);
    
      // ----- BEGIN TO SETUP HEADERS -------
      areca_packet[0] = 0x5E;
      areca_packet[1] = 0x01;
      areca_packet[2] = 0x61;
      areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff);
      areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
      areca_packet[5] = 0x1c;  // areca defined code for ATA passthrough command
    
      // ----- BEGIN TO SETUP PAYLOAD DATA -----
      memcpy(&areca_packet[7], "SmrT", 4);  // areca defined password
      ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
    
      // Set registers
      {
        const ata_in_regs & r = in.in_regs;
        ata_cmd->features      = r.features;
        ata_cmd->sector_count  = r.sector_count;
        ata_cmd->sector_number = r.lba_low;
        ata_cmd->cylinder_low  = r.lba_mid;
        ata_cmd->cylinder_high = r.lba_high;
        ata_cmd->device_head   = r.device;
        ata_cmd->command       = r.command;
      }
      bool readdata = false;
      if (in.direction == ata_cmd_in::data_in) {
          readdata = true;
          // the command will read data
          areca_packet[6] = 0x13;
      }
      else if ( in.direction == ata_cmd_in::no_data )
      {
        // the commands will return no data
        areca_packet[6] = 0x15;
      }
      else if (in.direction == ata_cmd_in::data_out)
      {
        // the commands will write data
        memcpy(ata_cmd->data, in.buffer, in.size);
        areca_packet[6] = 0x14;
      }
      else {
          // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
          return set_err(ENOSYS);
      }
    
      areca_packet[11] = get_disknum() - 1;  // disk#
      areca_packet[19] = get_encnum() - 1;   // enc#
    
      // ----- BEGIN TO SEND TO ARECA DRIVER ------
      expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff);
      if ( expected < 0 )
      {
        return set_err(EIO);
      }
    
      sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ;
      if ( ata_out->status )
      {
        if ( in.in_regs.command == ATA_IDENTIFY_DEVICE
         && !nonempty((unsigned char *)in.buffer, in.size))
         {
            return set_err(ENODEV, "No drive on port %d", get_disknum());
         }
      }
    
      // returns with data
      if (readdata)
      {
        memcpy(in.buffer, &return_buff[7], in.size);
      }
    
      // Return register values
      {
        ata_out_regs & r = out.out_regs;
        r.error          = ata_out->error;
        r.sector_count   = ata_out->sector_count;
        r.lba_low        = ata_out->sector_number;
        r.lba_mid        = ata_out->cylinder_low;
        r.lba_high       = ata_out->cylinder_high;
        r.status         = ata_out->status;
      }
      return true;
    }
    
    bool generic_areca_device::arcmsr_scsi_pass_through(struct scsi_cmnd_io * iop)
    {
      // Areca packet format for outgoing:
      // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
      // B[3~4] : 2 bytes command length + variant data length, little endian
      // B[5]   : 1 bytes areca defined command code
      // B[6~last-1] : variant bytes payload data
      // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
      //
      //
      //   header 3 bytes  length 2 bytes   cmd 1 byte    payload data x bytes  cs 1 byte
      // +--------------------------------------------------------------------------------+
      // + 0x5E 0x01 0x61 |   0x00 0x00   |     0x1c   | .................... |   0x00    |
      // +--------------------------------------------------------------------------------+
      //
    
      //Areca packet format for incoming:
      // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
      // B[3~4] : 2 bytes payload length, little endian
      // B[5~last-1] : variant bytes returned payload data
      // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
      //
      //
      //   header 3 bytes  length 2 bytes   payload data x bytes  cs 1 byte
      // +-------------------------------------------------------------------+
      // + 0x5E 0x01 0x61 |   0x00 0x00   | .................... |   0x00    |
      // +-------------------------------------------------------------------+
      unsigned char    areca_packet[640];
      int areca_packet_len = sizeof(areca_packet);
      unsigned char return_buff[2048];
      int expected = 0;
    
      if (iop->cmnd_len > 16) {
        set_err(EINVAL, "cmnd_len too large");
        return false;
      }
    
      memset(areca_packet, 0, areca_packet_len);
    
      // ----- BEGIN TO SETUP HEADERS -------
      areca_packet[0] = 0x5E;
      areca_packet[1] = 0x01;
      areca_packet[2] = 0x61;
      areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff);
      areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
      areca_packet[5] = 0x1c;
    
      // ----- BEGIN TO SETUP PAYLOAD DATA -----
      areca_packet[6] = 0x16; // scsi pass through
      memcpy(&areca_packet[7], "SmrT", 4);  // areca defined password
      areca_packet[12] = iop->cmnd_len; // cdb length
      memcpy( &areca_packet[35], iop->cmnd, iop->cmnd_len); // cdb
      areca_packet[15] = (unsigned char)iop->dxfer_len; // 15(LSB) ~ 18(MSB): data length ( max=512 bytes)
      areca_packet[16] = (unsigned char)(iop->dxfer_len >> 8);
      areca_packet[17] = (unsigned char)(iop->dxfer_len >> 16);
      areca_packet[18] = (unsigned char)(iop->dxfer_len >> 24);
      if(iop->dxfer_dir == DXFER_TO_DEVICE)
      {
        areca_packet[13] |= 0x01;
        memcpy(&areca_packet[67], iop->dxferp, iop->dxfer_len);
      }
      else if (iop->dxfer_dir == DXFER_FROM_DEVICE)
      {
      }
      else if( iop->dxfer_dir == DXFER_NONE)
      {
      }
      else {
        // COMMAND NOT SUPPORTED VIA ARECA IOCTL INTERFACE
        return set_err(ENOSYS);
      }
    
      areca_packet[11] = get_disknum() - 1;  // disk#
      areca_packet[19] = get_encnum() - 1;   // enc#
    
      // ----- BEGIN TO SEND TO ARECA DRIVER ------
      expected = arcmsr_ui_handler(areca_packet, areca_packet_len, return_buff);
    
      if (expected < 0)
        return set_err(EIO, "arcmsr_scsi_pass_through: I/O error");
      if (expected < 15) // 7 bytes if port is empty
        return set_err(EIO, "arcmsr_scsi_pass_through: missing data (%d bytes, expected %d)", expected, 15);
    
      int scsi_status = return_buff[5];
      int in_data_len = return_buff[11] | return_buff[12] << 8 | return_buff[13] << 16 | return_buff[14] << 24;
    
      if (iop->dxfer_dir == DXFER_FROM_DEVICE)
      {
        memset(iop->dxferp, 0, iop->dxfer_len); // need?
        memcpy(iop->dxferp, &return_buff[15], in_data_len);
      }
    
      if(scsi_status == 0xE1 /* Underrun, actual data length < requested data length */)
      {
          // don't care, just ignore
          scsi_status = 0x0;
      }
    
      if(scsi_status != 0x00 && scsi_status != SCSI_STATUS_CHECK_CONDITION)
      {
        return set_err(EIO);
      }
    
      if(scsi_status == SCSI_STATUS_CHECK_CONDITION)
      {
        // check condition
        iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
        iop->resp_sense_len = 4;
        iop->sensep[0] = return_buff[7];
        iop->sensep[1] = return_buff[8];
        iop->sensep[2] = return_buff[9];
        iop->sensep[3] = return_buff[10];
      }
    
      return true;
    }
    
    /////////////////////////////////////////////////////////////
    areca_ata_device::areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
    : smart_device(intf, dev_name, "areca", "areca")
    {
      set_encnum(encnum);
      set_disknum(disknum);
      set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
    }
    
    areca_ata_device::~areca_ata_device() throw()
    {
    
    }
    
    bool areca_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
    {
      if (!ata_cmd_is_supported(in,
        ata_device::supports_data_out |
        ata_device::supports_output_regs |
      //ata_device::supports_multi_sector | // TODO
        ata_device::supports_48bit_hi_null,
        "Areca")
      )
        return false;
    
      return arcmsr_ata_pass_through(in, out);
    }
    
    /////////////////////////////////////////////////////////////
    areca_scsi_device::areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
    : smart_device(intf, dev_name, "areca", "areca")
    {
      set_encnum(encnum);
      set_disknum(disknum);
      set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
    }
    
    areca_scsi_device::~areca_scsi_device() throw()
    {
    
    }
    
    bool areca_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
    {
      return arcmsr_scsi_pass_through(iop);
    }