Diff for /embedaddon/smartmontools/os_freebsd.cpp between versions 1.1 and 1.1.1.3

version 1.1, 2012/02/21 16:32:16 version 1.1.1.3, 2013/07/22 01:17:35
Line 11 Line 11
  * any later version.   * any later version.
  *   *
  * You should have received a copy of the GNU General Public License   * You should have received a copy of the GNU General Public License
 * (for example COPYING); if not, write to the Free * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
  */   */
   
 #include <stdio.h>  #include <stdio.h>
Line 46 Line 46
   
 #include "dev_interface.h"  #include "dev_interface.h"
 #include "dev_ata_cmd_set.h"  #include "dev_ata_cmd_set.h"
   #include "dev_areca.h"
   
 #define USBDEV "/dev/usb"  #define USBDEV "/dev/usb"
 #if defined(__FreeBSD_version)  #if defined(__FreeBSD_version)
Line 121  void printwarning(int msgNo, const char* extra) { Line 122  void printwarning(int msgNo, const char* extra) {
   
 // global variable holding byte count of allocated memory  // global variable holding byte count of allocated memory
 long long bytes;  long long bytes;
   extern unsigned char failuretest_permissive;
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
   
Line 189  static const char  smartctl_examples[] = Line 191  static const char  smartctl_examples[] =
          "                                      (Prints Self-Test & Attribute errors)\n\n"           "                                      (Prints Self-Test & Attribute errors)\n\n"
          "  smartctl -a --device=3ware,2 /dev/twa0\n"           "  smartctl -a --device=3ware,2 /dev/twa0\n"
          "  smartctl -a --device=3ware,2 /dev/twe0\n"           "  smartctl -a --device=3ware,2 /dev/twe0\n"
            "  smartctl -a --device=3ware,2 /dev/tws0\n"
          "                              (Prints all SMART information for ATA disk on\n"           "                              (Prints all SMART information for ATA disk on\n"
          "                                 third port of first 3ware RAID controller)\n"           "                                 third port of first 3ware RAID controller)\n"
   "  smartctl -a --device=cciss,0 /dev/ciss0\n"    "  smartctl -a --device=cciss,0 /dev/ciss0\n"
          "                              (Prints all SMART information for first disk \n"           "                              (Prints all SMART information for first disk \n"
          "                               on Common Interface for SCSI-3 Support driver)\n"           "                               on Common Interface for SCSI-3 Support driver)\n"
  "  smartctl -a --device=areca,1 /dev/arcmsr0\n"  "  smartctl -a --device=areca,3/1 /dev/arcmsr0\n"
         "                              (Prints all SMART information for first disk \n"         "                              (Prints all SMART information for 3rd disk in the 1st enclosure \n"
          "                               on first ARECA RAID controller)\n"           "                               on first ARECA RAID controller)\n"
   
          ;           ;
Line 252  freebsd_ata_device::freebsd_ata_device(smart_interface Line 255  freebsd_ata_device::freebsd_ata_device(smart_interface
   
 int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd)  int freebsd_ata_device::do_cmd( struct ata_ioc_request* request, bool is_48bit_cmd)
 {  {
  int fd = get_fd();  int fd = get_fd(), ret;
   ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST    ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the IOCATAREQUEST
  return ioctl(fd, IOCATAREQUEST, request);  ret = ioctl(fd, IOCATAREQUEST, request);
   if (ret) set_err(errno);
   return ret;
 }  }
   
   
Line 269  bool freebsd_ata_device::ata_pass_through(const ata_cm Line 274  bool freebsd_ata_device::ata_pass_through(const ata_cm
     true,  // data_out_support      true,  // data_out_support
     true,  // multi_sector_support      true,  // multi_sector_support
     ata_48bit)       ata_48bit) 
    )     ) {
    return false;      set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers");
       return false;
     }
   
   struct ata_ioc_request request;    struct ata_ioc_request request;
   bzero(&request,sizeof(struct ata_ioc_request));    bzero(&request,sizeof(struct ata_ioc_request));
Line 303  bool freebsd_ata_device::ata_pass_through(const ata_cm Line 310  bool freebsd_ata_device::ata_pass_through(const ata_cm
   clear_err();    clear_err();
   errno = 0;    errno = 0;
   if (do_cmd(&request, in.in_regs.is_48bit_cmd()))    if (do_cmd(&request, in.in_regs.is_48bit_cmd()))
      return set_err(errno);      return false;
   if (request.error)    if (request.error)
       return set_err(EIO, "request failed, error code 0x%02x", request.error);        return set_err(EIO, "request failed, error code 0x%02x", request.error);
   
Line 328  bool freebsd_ata_device::ata_pass_through(const ata_cm Line 335  bool freebsd_ata_device::ata_pass_through(const ata_cm
     {      {
       // We haven't gotten output that makes sense; print out some debugging info        // We haven't gotten output that makes sense; print out some debugging info
       char buf[512];        char buf[512];
      sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",      snprintf(buf, sizeof(buf),
         "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
         (int)request.u.ata.command,          (int)request.u.ata.command,
         (int)request.u.ata.feature,          (int)request.u.ata.feature,
         (int)request.u.ata.count,          (int)request.u.ata.count,
Line 385  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ Line 393  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ
   union ccb ccb;    union ccb ccb;
   int camflags;    int camflags;
   
     // FIXME:
     // 48bit commands are broken in ATACAM before r242422/HEAD
     // and may cause system hang
     // Waiting for MFC to make sure that bug is fixed,
     // later version check needs to be added
     if(!strcmp("ata",m_camdev->sim_name) && is_48bit_cmd) {
       set_err(ENOSYS, "48-bit ATA commands not implemented for legacy controllers");
       return -1;
     }
   
   memset(&ccb, 0, sizeof(ccb));    memset(&ccb, 0, sizeof(ccb));
   
   if (request->count == 0)    if (request->count == 0)
Line 393  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ Line 411  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ
     camflags = CAM_DIR_IN;      camflags = CAM_DIR_IN;
   else    else
     camflags = CAM_DIR_OUT;      camflags = CAM_DIR_OUT;
   if(is_48bit_cmd)  
     camflags |= CAM_ATAIO_48BIT;  
   
   cam_fill_ataio(&ccb.ataio,    cam_fill_ataio(&ccb.ataio,
                  0,                   0,
Line 405  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ Line 421  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ
                  request->count,                   request->count,
                  request->timeout * 1000); // timeout in seconds                   request->timeout * 1000); // timeout in seconds
   
  ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT;  ccb.ataio.cmd.flags = CAM_ATAIO_NEEDRESULT |
     (is_48bit_cmd ? CAM_ATAIO_48BIT : 0);
   // ata_28bit_cmd    // ata_28bit_cmd
   ccb.ataio.cmd.command = request->u.ata.command;    ccb.ataio.cmd.command = request->u.ata.command;
   ccb.ataio.cmd.features = request->u.ata.feature;    ccb.ataio.cmd.features = request->u.ata.feature;
Line 423  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ Line 440  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ
   ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;    ccb.ccb_h.flags |= CAM_DEV_QFRZDIS;
   
   if (cam_send_ccb(m_camdev, &ccb) < 0) {    if (cam_send_ccb(m_camdev, &ccb) < 0) {
    err(1, "cam_send_ccb");    set_err(EIO, "cam_send_ccb failed");
     return -1;      return -1;
   }    }
   
   if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {    if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
     cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);      cam_error_print(m_camdev, &ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr);
       set_err(EIO);
     return -1;      return -1;
   }    }
   
Line 449  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ Line 467  int freebsd_atacam_device::do_cmd( struct ata_ioc_requ
 #endif  #endif
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
/// Implement AMCC/3ware RAID support with old functions/// Implement AMCC/3ware RAID support
   
 class freebsd_escalade_device  class freebsd_escalade_device
: public /*implements*/ ata_device_with_command_set,: public /*implements*/ ata_device,
   public /*extends*/ freebsd_smart_device    public /*extends*/ freebsd_smart_device
 {  {
 public:  public:
Line 460  class freebsd_escalade_device (public) Line 478  class freebsd_escalade_device (public)
     int escalade_type, int disknum);      int escalade_type, int disknum);
   
 protected:  protected:
  virtual int ata_command_interface(smart_command_set command, int select, char * data);  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
   virtual bool open();    virtual bool open();
   
 private:  private:
Line 493  bool freebsd_escalade_device::open() Line 511  bool freebsd_escalade_device::open()
   return true;    return true;
 }  }
   
int freebsd_escalade_device::ata_command_interface(smart_command_set command, int select, char * data)bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 {  {
   // to hold true file descriptor    // to hold true file descriptor
   int fd = get_fd();    int fd = get_fd();
   
  // return value and buffer for ioctl()  if (!ata_cmd_is_ok(in,
  int  ioctlreturn, readdata=0;    true, // data_out_support
     false, // TODO: multi_sector_support
     true) // ata_48bit_support
   )
   return false;
 
   struct twe_usercommand* cmd_twe = NULL;    struct twe_usercommand* cmd_twe = NULL;
   TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL;    TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL;
   TWE_Command_ATA* ata = NULL;    TWE_Command_ATA* ata = NULL;
Line 517  int freebsd_escalade_device::ata_command_interface(sma Line 540  int freebsd_escalade_device::ata_command_interface(sma
   if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) {    if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) {
     cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer;      cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer;
     cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf;      cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf;
    cmd_twa->driver_pkt.buffer_length = 512;    cmd_twa->driver_pkt.buffer_length = in.size;
     // using "old" packet format to speak with SATA devices
     ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k;      ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k;
   } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {    } else if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {
     cmd_twe = (struct twe_usercommand*)ioctl_buffer;      cmd_twe = (struct twe_usercommand*)ioctl_buffer;
     ata = &cmd_twe->tu_command.ata;      ata = &cmd_twe->tu_command.ata;
   } else {    } else {
    pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n"    return set_err(ENOSYS,
      "Please contact " PACKAGE_BUGREPORT "\n", m_escalade_type, m_disknum);      "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
    errno=ENOSYS;      "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum);
    return -1; 
   }    }
   
   ata->opcode = TWE_OP_ATA_PASSTHROUGH;    ata->opcode = TWE_OP_ATA_PASSTHROUGH;
Line 534  int freebsd_escalade_device::ata_command_interface(sma Line 557  int freebsd_escalade_device::ata_command_interface(sma
   // Same for (almost) all commands - but some reset below    // Same for (almost) all commands - but some reset below
   ata->request_id    = 0xFF;    ata->request_id    = 0xFF;
   ata->unit          = m_disknum;    ata->unit          = m_disknum;
  ata->status        = 0;             ata->status        = 0;
   ata->flags         = 0x1;    ata->flags         = 0x1;
  ata->drive_head    = 0x0;  ata->size         = 0x5; // TODO: multisector support
  ata->sector_num    = 0;  // Set registers
   {
     const ata_in_regs_48bit & r = in.in_regs;
     ata->features     = r.features_16;
     ata->sector_count = r.sector_count_16;
     ata->sector_num   = r.lba_low_16;
     ata->cylinder_lo  = r.lba_mid_16;
     ata->cylinder_hi  = r.lba_high_16;
     ata->drive_head   = r.device;
     ata->command      = r.command;
   }
   
   // All SMART commands use this CL/CH signature.  These are magic  
   // values from the ATA specifications.  
   ata->cylinder_lo   = 0x4F;  
   ata->cylinder_hi   = 0xC2;  
   
   // SMART ATA COMMAND REGISTER value  
   ata->command       = ATA_SMART_CMD;  
   
   // Is this a command that reads or returns 512 bytes?    // Is this a command that reads or returns 512 bytes?
   // passthru->param values are:    // passthru->param values are:
   // 0x0 - non data command without TFR write check,    // 0x0 - non data command without TFR write check,
Line 554  int freebsd_escalade_device::ata_command_interface(sma Line 579  int freebsd_escalade_device::ata_command_interface(sma
   // 0xD - data command that returns data to host from device    // 0xD - data command that returns data to host from device
   // 0xF - data command that writes data from host to device    // 0xF - data command that writes data from host to device
   // passthru->size values are 0x5 for non-data and 0x07 for data    // passthru->size values are 0x5 for non-data and 0x07 for data
  if (command == READ_VALUES     ||  bool readdata = false;
      command == READ_THRESHOLDS ||  if (in.direction == ata_cmd_in::data_in) {
      command == READ_LOG        || 
      command == IDENTIFY        || 
      command == WRITE_LOG )  
  { 
    readdata=1; 
     if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {      if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {
      cmd_twe->tu_data = data;      cmd_twe->tu_data = in.buffer;
       cmd_twe->tu_size = 512;        cmd_twe->tu_size = 512;
     }      }
    ata->sgl_offset = 0x5;
    ata->size         = 0x5;    readdata=true;
     ata->sgl_offset   = 0x5;
     ata->param        = 0xD;      ata->param        = 0xD;
     ata->sector_count = 0x1;  
     // For 64-bit to work correctly, up the size of the command packet      // For 64-bit to work correctly, up the size of the command packet
     // in dwords by 1 to account for the 64-bit single sgl 'address'      // in dwords by 1 to account for the 64-bit single sgl 'address'
     // field. Note that this doesn't agree with the typedefs but it's      // field. Note that this doesn't agree with the typedefs but it's
     // right (agree with kernel driver behavior/typedefs).      // right (agree with kernel driver behavior/typedefs).
    //if (sizeof(long)==8)    // if (sizeof(long)==8)
     //  ata->size++;      //  ata->size++;
   }    }
  else {  else if (in.direction == ata_cmd_in::no_data) {
     // Non data command -- but doesn't use large sector       // Non data command -- but doesn't use large sector 
    // count register values.      // count register values.
    ata->sgl_offset = 0x0;    ata->sgl_offset   = 0x0;
    ata->size         = 0x5; 
     ata->param        = 0x8;      ata->param        = 0x8;
     ata->sector_count = 0x0;      ata->sector_count = 0x0;
   }    }
     else if (in.direction == ata_cmd_in::data_out) {
       ata->sgl_offset   = 0x5;
       ata->param        = 0xF; // PIO data write
       if (m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {
         cmd_twe->tu_data = in.buffer;
         cmd_twe->tu_size = 512;
       }
       else if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) {
          memcpy(cmd_twa->pdata, in.buffer, in.size);
       }
     }
     else
       return set_err(EINVAL);
   
  // Now set ATA registers depending upon command  // 3WARE controller can NOT have packet device internally
  switch (command){  if (in.in_regs.command == ATA_IDENTIFY_PACKET_DEVICE) {
  case CHECK_POWER_MODE:    return set_err(ENODEV, "No drive on port %d", m_disknum);
    ata->command     = ATA_CHECK_POWER_MODE; 
    ata->features    = 0; 
    ata->cylinder_lo = 0; 
    ata->cylinder_hi = 0; 
    break; 
  case READ_VALUES: 
    ata->features = ATA_SMART_READ_VALUES; 
    break; 
  case READ_THRESHOLDS: 
    ata->features = ATA_SMART_READ_THRESHOLDS; 
    break; 
  case READ_LOG: 
    ata->features = ATA_SMART_READ_LOG_SECTOR; 
    // log number to return 
    ata->sector_num  = select; 
    break; 
  case WRITE_LOG: 
    readdata=0; 
    ata->features     = ATA_SMART_WRITE_LOG_SECTOR; 
    ata->sector_count = 1; 
    ata->sector_num   = select; 
    ata->param        = 0xF;  // PIO data write 
    break; 
  case IDENTIFY: 
    // ATA IDENTIFY DEVICE 
    ata->command     = ATA_IDENTIFY_DEVICE; 
    ata->features    = 0; 
    ata->cylinder_lo = 0; 
    ata->cylinder_hi = 0; 
    break; 
  case PIDENTIFY: 
    // 3WARE controller can NOT have packet device internally 
    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", m_disknum); 
    errno=ENODEV; 
    return -1; 
  case ENABLE: 
    ata->features = ATA_SMART_ENABLE; 
    break; 
  case DISABLE: 
    ata->features = ATA_SMART_DISABLE; 
    break; 
  case AUTO_OFFLINE: 
    ata->features     = ATA_SMART_AUTO_OFFLINE; 
    // Enable or disable? 
    ata->sector_count = select; 
    break; 
  case AUTOSAVE: 
    ata->features     = ATA_SMART_AUTOSAVE; 
    // Enable or disable? 
    ata->sector_count = select; 
    break; 
  case IMMEDIATE_OFFLINE: 
    ata->features    = ATA_SMART_IMMEDIATE_OFFLINE; 
    // What test type to run? 
    ata->sector_num  = select; 
    break; 
  case STATUS_CHECK: 
    ata->features = ATA_SMART_STATUS; 
    break; 
  case STATUS: 
    // This is JUST to see if SMART is enabled, by giving SMART status 
    // command. But it doesn't say if status was good, or failing. 
    // See below for the difference. 
    ata->features = ATA_SMART_STATUS; 
    break; 
  default: 
    pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n" 
         "Please contact " PACKAGE_BUGREPORT "\n", command, m_disknum); 
    errno=ENOSYS; 
    return -1; 
   }    }
   
   // Now send the command down through an ioctl()    // Now send the command down through an ioctl()
     int ioctlreturn;
   if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) {    if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR) {
     ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);      ioctlreturn=ioctl(fd,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
   } else {    } else {
Line 669  int freebsd_escalade_device::ata_command_interface(sma Line 632  int freebsd_escalade_device::ata_command_interface(sma
   
   // Deal with the different error cases    // Deal with the different error cases
   if (ioctlreturn) {    if (ioctlreturn) {
    if (!errno)    return set_err(EIO);
      errno=EIO; 
    return -1; 
   }    }
   
   // See if the ATA command failed.  Now that we have returned from    // See if the ATA command failed.  Now that we have returned from
Line 687  int freebsd_escalade_device::ata_command_interface(sma Line 648  int freebsd_escalade_device::ata_command_interface(sma
   // happened.    // happened.
   
   if (ata->status || (ata->command & 0x21)) {    if (ata->status || (ata->command & 0x21)) {
    pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags);    if (scsi_debugmode)
    errno=EIO;      pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags);
    return -1;    return set_err(EIO);
   }    }
   
   // If this is a read data command, copy data to output buffer    // If this is a read data command, copy data to output buffer
   if (readdata) {    if (readdata) {
     if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR)      if (m_escalade_type==CONTROLLER_3WARE_9000_CHAR)
      memcpy(data, cmd_twa->pdata, 512);      memcpy(in.buffer, cmd_twa->pdata, in.size);
     else if(m_escalade_type==CONTROLLER_3WARE_678K_CHAR) {
       memcpy(in.buffer, cmd_twe->tu_data, in.size); // untested
     }
   }    }
  // Return register values
  // For STATUS_CHECK, we need to check register values  if (ata) {
  if (command==STATUS_CHECK) {    ata_out_regs_48bit & r = out.out_regs;
    r.error           = ata->features;
    // To find out if the SMART RETURN STATUS is good or failing, we    r.sector_count_16 = ata->sector_count;
    // need to examine the values of the Cylinder Low and Cylinder    r.lba_low_16      = ata->sector_num;
    // High Registers.    r.lba_mid_16      = ata->cylinder_lo;
    r.lba_high_16     = ata->cylinder_hi;
    unsigned short cyl_lo=ata->cylinder_lo;    r.device          = ata->drive_head;
    unsigned short cyl_hi=ata->cylinder_hi;    r.status          = ata->command;
 
    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good. 
    if (cyl_lo==0x4F && cyl_hi==0xC2) 
      return 0; 
 
    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL 
    if (cyl_lo==0xF4 && cyl_hi==0x2C) 
      return 1; 
 
      errno=EIO; 
      return -1; 
   }    }
   
   // copy sector count register (one byte!) to return data  
   if (command==CHECK_POWER_MODE)  
     *data=*(char *)&(ata->sector_count);  
   
   // look for nonexistent devices/ports    // look for nonexistent devices/ports
  if (command==IDENTIFY && !nonempty(data, 512)) {  if (in.in_regs.command == ATA_IDENTIFY_DEVICE
    errno=ENODEV;  && !nonempty((unsigned char *)in.buffer, in.size)) {
    return -1;    return set_err(ENODEV, "No drive on port %d", m_disknum);
   }    }
  return true;
  return 0; 
 }  }
   
   
Line 911  int freebsd_highpoint_device::ata_command_interface(sm Line 858  int freebsd_highpoint_device::ata_command_interface(sm
   
     // We haven't gotten output that makes sense; print out some debugging info      // We haven't gotten output that makes sense; print out some debugging info
     char buf[512];      char buf[512];
    sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",    snprintf(buf, sizeof(buf),
             "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
             (int)pide_pt_hdr_out->command,              (int)pide_pt_hdr_out->command,
             (int)pide_pt_hdr_out->feature,              (int)pide_pt_hdr_out->feature,
             (int)pide_pt_hdr_out->sectorcount,              (int)pide_pt_hdr_out->sectorcount,
Line 931  int freebsd_highpoint_device::ata_command_interface(sm Line 879  int freebsd_highpoint_device::ata_command_interface(sm
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
/// Implement standard SCSI support with old functions/// Standard SCSI support
   
 class freebsd_scsi_device  class freebsd_scsi_device
 : public /*implements*/ scsi_device,  : public /*implements*/ scsi_device,
Line 1013  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_ Line 961  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_
     warnx("error allocating ccb");      warnx("error allocating ccb");
     return -ENOMEM;      return -ENOMEM;
   }    }
  // mfi SAT layer is known to be buggy
   if(!strcmp("mfi",m_camdev->sim_name)) {
     if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) { 
       // Controller does not return ATA output registers in SAT sense data
       if (iop->cmnd[2] & (1 << 5)) // chk_cond
         return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware");
     }
     // SMART WRITE LOG SECTOR causing media errors
     if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16
         && iop->cmnd[14] == ATA_SMART_CMD  && iop->cmnd[3]==0 &&
         iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) ||
         (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12
         && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR))
     {
       if(!failuretest_permissive)
         return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force"); 
     }
   }
   // clear out structure, except for header that was filled in for us    // clear out structure, except for header that was filled in for us
   bzero(&(&ccb->ccb_h)[1],    bzero(&(&ccb->ccb_h)[1],
     sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));      sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
Line 1044  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_ Line 1009  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_
   }    }
   
   if (iop->sensep) {    if (iop->sensep) {
    memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data));    iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
    iop->resp_sense_len = sizeof(struct scsi_sense_data);    memcpy(iop->sensep,&(ccb->csio.sense_data),iop->resp_sense_len);
   }    }
   
   iop->scsi_status = ccb->csio.scsi_status;    iop->scsi_status = ccb->csio.scsi_status;
Line 1063  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_ Line 1028  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_
     dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
   }    }
   
     // mfip replacing PDT of the device so response does not make a sense
     // this sets PDT to 00h - direct-access block device
     if((!strcmp("mfi", m_camdev->sim_name) || !strcmp("mpt", m_camdev->sim_name))
      && iop->cmnd[0] == INQUIRY) {
        if (report > 0) {
           pout("device on %s controller, patching PDT\n", m_camdev->sim_name);
        }
        iop->dxferp[0] = iop->dxferp[0] & 0xe0;
     }
   
   return true;    return true;
 }  }
   
Line 1070  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_ Line 1045  bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 /// Areca RAID support  /// Areca RAID support
   
class freebsd_areca_device///////////////////////////////////////////////////////////////////
: public /*implements*/ ata_device,// SATA(ATA) device behind Areca RAID Controller
 class freebsd_areca_ata_device
 : public /*implements*/ areca_ata_device,
   public /*extends*/ freebsd_smart_device    public /*extends*/ freebsd_smart_device
 {  {
 public:  public:
  freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum);  freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
   virtual smart_device * autodetect_open();
   virtual bool arcmsr_lock();
   virtual bool arcmsr_unlock();
   virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
 };
   
protected:///////////////////////////////////////////////////////////////////
  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); // SAS(SCSI) device behind Areca RAID Controller
class freebsd_areca_scsi_device
private:: public /*implements*/ areca_scsi_device,
  int m_disknum; ///< Disk number.  public /*extends*/ freebsd_smart_device
 {
 public:
   freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum = 1);
   virtual smart_device * autodetect_open();
   virtual bool arcmsr_lock();
   virtual bool arcmsr_unlock();
   virtual int arcmsr_do_scsi_io(struct scsi_cmnd_io * iop);
 };  };
   
   
// PURPOSE// Areca RAID Controller(SATA Disk)
//   This is an interface routine meant to isolate the OS dependentfreebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
//   parts of the code, and to provide a debugging interface.  Each: smart_device(intf, dev_name, "areca", "areca"),
//   different port and OS needs to provide it's own interface.  This  freebsd_smart_device("ATA")
//   is the linux 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 15) 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" 
 
 
/*FunctionCode*/ 
#define FUNCTION_READ_RQBUFFER                  0x0801 
#define FUNCTION_WRITE_WQBUFFER                 0x0802 
#define FUNCTION_CLEAR_RQBUFFER                 0x0803 
#define FUNCTION_CLEAR_WQBUFFER                 0x0804 
 
/* ARECA IO CONTROL CODE*/ 
#define ARCMSR_IOCTL_READ_RQBUFFER           _IOWR('F', FUNCTION_READ_RQBUFFER, sSRB_BUFFER) 
#define ARCMSR_IOCTL_WRITE_WQBUFFER          _IOWR('F', FUNCTION_WRITE_WQBUFFER, sSRB_BUFFER) 
#define ARCMSR_IOCTL_CLEAR_RQBUFFER          _IOWR('F', FUNCTION_CLEAR_RQBUFFER, sSRB_BUFFER) 
#define ARCMSR_IOCTL_CLEAR_WQBUFFER          _IOWR('F', FUNCTION_CLEAR_WQBUFFER, sSRB_BUFFER) 
#define ARECA_SIG_STR                                                   "ARCMSR" 
 
 
 
// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver 
typedef struct _SRB_IO_CONTROL 
 {  {
        unsigned int HeaderLength;  set_disknum(disknum);
        unsigned char Signature[8];  set_encnum(encnum);
        unsigned int Timeout;  set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
        unsigned int ControlCode;}
        unsigned int ReturnCode; 
        unsigned int Length; 
} sSRB_IO_CONTROL; 
   
 typedef struct _SRB_BUFFER  
 {  
         sSRB_IO_CONTROL srbioctl;  
         unsigned char   ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware  
 } sSRB_BUFFER;  
   
smart_device * freebsd_areca_ata_device::autodetect_open()
// For debugging areca code 
 
static void areca_dumpdata(unsigned char *block, int len) 
 {  {
        int ln = (len / 16) + 1;     // total line#  int is_ata = 1;
        unsigned char c; 
        int pos = 0; 
   
        printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);  // autodetect device type
        printf("      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      ASCII      \n");  is_ata = arcmsr_get_dev_type();
        printf("=====================================================================\n");  if(is_ata < 0)
   {
     set_err(EIO);
     return this;
   }
   
        for ( int l = 0; l < ln && len; l++ )  if(is_ata == 1)
        {  {
                // printf the line# and the HEX data    // SATA device
                // if a line data length < 16 then append the space to the tail of line to reach 16 chars    return this;
                printf("%02X | ", l);  }
                for ( pos = 0; pos < 16 && len; pos++, len-- ) 
                { 
                        c = block[l*16+pos];     
                        printf("%02X ", c); 
                } 
   
                if ( pos < 16 )  // SAS device
                {  smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
                        for ( int loop = pos; loop < 16; loop++ )  close();
                        {  delete this;
                                printf("   ");  newdev->open();       // TODO: Can possibly pass open fd
                        } 
                } 
   
                // print ASCII char  return newdev.release();
                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"); 
 }  }
   
int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
static int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */) 
 {  {
        ARGUSED(ext_data);  int ioctlreturn = 0;
   
        int ioctlreturn = 0;  if(!is_open()) {
        sSRB_BUFFER sBuf;      if(!open()){
       }
   }
   
        unsigned char *areca_return_packet;  ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp);
        int total = 0;  if (ioctlreturn)
        int expected = -1;  {
        unsigned char return_buff[2048];    // errors found
        unsigned char *ptr = &return_buff[0];    return -1;
        memset(return_buff, 0, sizeof(return_buff));  }
   
        memset((unsigned char *)&sBuf, 0, sizeof(sBuf));  return ioctlreturn;
 }
   
   bool freebsd_areca_ata_device::arcmsr_lock()
   {
     return true;
   }
   
         sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL);     
         memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));  
         sBuf.srbioctl.Timeout = 10000;        
         sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER;  
   
        switch ( arcmsr_cmd )bool freebsd_areca_ata_device::arcmsr_unlock()
        {{
        // command for writing data to driver  return true;
        case ARCMSR_IOCTL_WRITE_WQBUFFER:    
                if ( data && data_len ) 
                { 
                        sBuf.srbioctl.Length = data_len;     
                        memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len); 
                } 
                // commands for clearing related buffer of driver 
        case ARCMSR_IOCTL_CLEAR_RQBUFFER: 
        case ARCMSR_IOCTL_CLEAR_WQBUFFER: 
                break; 
                // command for reading data from driver 
        case ARCMSR_IOCTL_READ_RQBUFFER:     
                break; 
        default: 
                // unknown arcmsr commands 
                return -1; 
        } 
 
 
        while ( 1 ) 
        { 
                ioctlreturn = ioctl(fd,arcmsr_cmd,&sBuf); 
                if ( ioctlreturn  ) 
                { 
                        // errors found 
                        break; 
                } 
 
                if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER ) 
                { 
                        // if succeeded, just returns the length of outgoing data 
                        return data_len; 
                } 
 
                if ( sBuf.srbioctl.Length ) 
                { 
                        if(ata_debugmode) 
                            areca_dumpdata(&sBuf.ioctldatabuffer[0], 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 ( ioctlreturn ) 
        { 
                pout("ioctl write buffer failed code = %x\n", ioctlreturn); 
                return -2; 
        } 
 
 
        if ( data ) 
        { 
                memcpy(data, return_buff, total); 
        } 
 
        return total; 
 }  }
   
   
freebsd_areca_device::freebsd_areca_device(smart_interface * intf, const char * dev_name, int disknum)// Areca RAID Controller(SAS Device)
 freebsd_areca_scsi_device::freebsd_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
 : smart_device(intf, dev_name, "areca", "areca"),  : smart_device(intf, dev_name, "areca", "areca"),
  freebsd_smart_device("ATA"),  freebsd_smart_device("SCSI")
  m_disknum(disknum) 
 {  {
  set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);  set_disknum(disknum);
   set_encnum(encnum);
   set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
 }  }
   
// Areca RAID Controllersmart_device * freebsd_areca_scsi_device::autodetect_open()
// int freebsd_areca_device::ata_command_interface(smart_command_set command, int select, char * data) 
bool freebsd_areca_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)  
 {  {
if (!ata_cmd_is_ok(in,   return this;
    true, // data_out_support }
    false, // TODO: multi_sector_support  
    true) // ata_48bit_support  
    ) 
    return false;  
   
        // ATA input registersint freebsd_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
        typedef struct _ATA_INPUT_REGISTERS{
        {  int ioctlreturn = 0;
                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  if(!is_open()) {
        // Note: The output registers is re-sorted for areca internal use only      if(!open()){
        typedef struct _ATA_OUTPUT_REGISTERS      }
        {  }
                unsigned char error;  ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp);
                unsigned char status;  if (ioctlreturn)
                unsigned char sector_count;  {
                unsigned char sector_number;    // errors found
                unsigned char cylinder_low;     return -1;
                unsigned char cylinder_high;  }
        }sATA_OUTPUT_REGISTERS; 
   
        // Areca packet format for outgoing:  return ioctlreturn;
        // 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:bool freebsd_areca_scsi_device::arcmsr_lock()
        // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61{
        // B[3~4] : 2 bytes payload length, little endian  return true;
        // 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 cs = 0;    
   
         sATA_INPUT_REGISTERS *ata_cmd;  
   
        // For debuggingbool freebsd_areca_scsi_device::arcmsr_unlock()
        memset(areca_packet, 0, areca_packet_len);{
  return true;
        // ----- 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_48bit & r = in.in_regs; 
            ata_cmd->features     = r.features_16; 
            ata_cmd->sector_count  = r.sector_count_16; 
            ata_cmd->sector_number = r.lba_low_16; 
            ata_cmd->cylinder_low  = r.lba_mid_16; 
            ata_cmd->cylinder_high = r.lba_high_16; 
            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(ENOTSUP, "DATA OUT not supported for this Areca controller type"); 
        } 
 
        areca_packet[11] = m_disknum - 1;                  // drive number 
 
        // ----- BEGIN TO SETUP CHECKSUM ----- 
        for ( int loop = 3; loop < areca_packet_len - 1; loop++ ) 
        { 
                cs += areca_packet[loop];  
        } 
        areca_packet[areca_packet_len-1] = cs; 
 
        // ----- BEGIN TO SEND TO ARECA DRIVER ------ 
        int expected = 0;        
        unsigned char return_buff[2048]; 
        memset(return_buff, 0, sizeof(return_buff)); 
 
        expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL); 
        if (expected==-3) { 
            return set_err(EIO); 
        } 
 
        expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL); 
        expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL); 
        if ( expected > 0 ) 
        { 
                expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL); 
        } 
        if ( expected < 0 ) 
        { 
                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 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", m_disknum); 
                 }  
        } 
 
        // returns with data 
        if (readdata) 
        { 
                memcpy(in.buffer, &return_buff[7], in.size);  
        } 
 
        // Return register values 
        { 
            ata_out_regs_48bit & r = out.out_regs; 
            r.error           = ata_out->error; 
            r.sector_count_16 = ata_out->sector_count; 
            r.lba_low_16      = ata_out->sector_number; 
            r.lba_mid_16      = ata_out->cylinder_low; 
            r.lba_high_16     = ata_out->cylinder_high; 
            r.status          = ata_out->status; 
        } 
        return true; 
 }  }
   
   
   
 /////////////////////////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////////////////////////
 /// Implement CCISS RAID support with old functions  /// Implement CCISS RAID support with old functions
   
Line 1583  smart_device * freebsd_scsi_device::autodetect_open() Line 1275  smart_device * freebsd_scsi_device::autodetect_open()
   // Use INQUIRY to detect type    // Use INQUIRY to detect type
   
   // 3ware ?    // 3ware ?
  if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {  if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4) ||
       !strcmp("tws",m_camdev->sim_name) || !strcmp("twa",m_camdev->sim_name)) {
     close();      close();
    set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"    set_err(EINVAL, "3ware/LSI controller, please try adding '-d 3ware,N',\n"
                    "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());                    "you may need to replace %s with /dev/twaN, /dev/tweN or /dev/twsN", get_dev_name());
     return this;      return this;
   }    }
   
  // SAT or USB ?  // SAT or USB, skip MFI controllers because of bugs
   {    {
     smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);      smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
    if (newdev)    if (newdev) {
       // NOTE: 'this' is now owned by '*newdev'        // NOTE: 'this' is now owned by '*newdev'
         if(!strcmp("mfi",m_camdev->sim_name)) {
           newdev->close();
           newdev->set_err(ENOSYS, "SATA device detected,\n"
             "MegaRAID SAT layer is reportedly buggy, use '-d sat' to try anyhow");
         }
       return newdev;        return newdev;
       }
   }    }
   
   // Nothing special found    // Nothing special found
Line 1749  bool get_dev_names_cam(std::vector<std::string> & name Line 1448  bool get_dev_names_cam(std::vector<std::string> & name
       if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) {        if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) {
         bus_result = &ccb.cdm.matches[i].result.bus_result;          bus_result = &ccb.cdm.matches[i].result.bus_result;
   
        if (strcmp(bus_result->dev_name,"ata") == 0 /* ATAPICAM devices will be probed as ATA devices, skip'em there */        if (strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */
          || strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */ 
         skip_bus = 1;          skip_bus = 1;
         else          else
           skip_bus = 0;            skip_bus = 0;
Line 1770  bool get_dev_names_cam(std::vector<std::string> & name Line 1468  bool get_dev_names_cam(std::vector<std::string> & name
       } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH &&         } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && 
           (skip_device == 0 || show_all)) {             (skip_device == 0 || show_all)) { 
         /* One device may be populated as many peripherals (pass0 & da0 for example).           /* One device may be populated as many peripherals (pass0 & da0 for example). 
        * We are searching for latest name        * We are searching for best name
         */          */
         periph_result =  &ccb.cdm.matches[i].result.periph_result;          periph_result =  &ccb.cdm.matches[i].result.periph_result;
        devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number);        /* Prefer non-"pass" names */
         if (devname.empty() || strncmp(periph_result->periph_name, "pass", 4) != 0) {
           devname = strprintf("%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number);
             }
         changed = 0;          changed = 0;
       };        };
       if ((changed == 1 || show_all) && !devname.empty()) {        if ((changed == 1 || show_all) && !devname.empty()) {
Line 2079  smart_device * freebsd_smart_interface::autodetect_sma Line 1780  smart_device * freebsd_smart_interface::autodetect_sma
   int bus=-1;    int bus=-1;
   int i,c;    int i,c;
   int len;    int len;
     const char * test_name = name;
   
   // if dev_name null, or string length zero    // if dev_name null, or string length zero
   if (!name || !(len = strlen(name)))    if (!name || !(len = strlen(name)))
     return 0;      return 0;
   
     // Dereference symlinks
     struct stat st;
     std::string pathbuf;
     if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
       char * p = realpath(name, (char *)0);
       if (p) {
         pathbuf = p;
         free(p);
         test_name = pathbuf.c_str();
       }
     }
   
   // check ATA bus    // check ATA bus
   char * * atanames = 0; int numata = 0;    char * * atanames = 0; int numata = 0;
   numata = get_dev_names_ata(&atanames);    numata = get_dev_names_ata(&atanames);
   if (numata > 0) {    if (numata > 0) {
     // check ATA/ATAPI devices      // check ATA/ATAPI devices
     for (i = 0; i < numata; i++) {      for (i = 0; i < numata; i++) {
      if(!strcmp(atanames[i],name)) {      if(!strcmp(atanames[i],test_name)) {
         for (c = i; c < numata; c++) free(atanames[c]);          for (c = i; c < numata; c++) free(atanames[c]);
         free(atanames);          free(atanames);
        return new freebsd_ata_device(this, name, "");        return new freebsd_ata_device(this, test_name, "");
       }        }
       else free(atanames[i]);        else free(atanames[i]);
     }      }
Line 2111  smart_device * freebsd_smart_interface::autodetect_sma Line 1825  smart_device * freebsd_smart_interface::autodetect_sma
   else if (!scsinames.empty()) {    else if (!scsinames.empty()) {
     // check all devices on CAM bus      // check all devices on CAM bus
     for (i = 0; i < (int)scsinames.size(); i++) {      for (i = 0; i < (int)scsinames.size(); i++) {
      if(strcmp(scsinames[i].c_str(), name)==0)      if(strcmp(scsinames[i].c_str(), test_name)==0)
       { // our disk device is CAM        { // our disk device is CAM
        if ((cam_dev = cam_open_device(name, O_RDWR)) == NULL) {        if ((cam_dev = cam_open_device(test_name, O_RDWR)) == NULL) {
           // open failure            // open failure
           set_err(errno);            set_err(errno);
           return 0;            return 0;
         }          }
           
         // zero the payload          // zero the payload
         bzero(&(&ccb.ccb_h)[1], PATHINQ_SETTINGS_SIZE);          bzero(&(&ccb.ccb_h)[1], PATHINQ_SETTINGS_SIZE);
         ccb.ccb_h.func_code = XPT_PATH_INQ; // send PATH_INQ to the device          ccb.ccb_h.func_code = XPT_PATH_INQ; // send PATH_INQ to the device
Line 2136  smart_device * freebsd_smart_interface::autodetect_sma Line 1849  smart_device * freebsd_smart_interface::autodetect_sma
           if(usbdevlist(bus,vendor_id, product_id, version)){            if(usbdevlist(bus,vendor_id, product_id, version)){
             const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version);              const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version);
             if (usbtype)              if (usbtype)
              return get_sat_device(usbtype, new freebsd_scsi_device(this, name, ""));              return get_sat_device(usbtype, new freebsd_scsi_device(this, test_name, ""));
           }            }
           return 0;            return 0;
         }          }
Line 2144  smart_device * freebsd_smart_interface::autodetect_sma Line 1857  smart_device * freebsd_smart_interface::autodetect_sma
         // check if we have ATA device connected to CAM (ada)          // check if we have ATA device connected to CAM (ada)
         if(ccb.cpi.protocol == PROTO_ATA){          if(ccb.cpi.protocol == PROTO_ATA){
           cam_close_device(cam_dev);            cam_close_device(cam_dev);
          return new freebsd_atacam_device(this, name, "");          return new freebsd_atacam_device(this, test_name, "");
         }          }
 #endif  #endif
         // close cam device, we don`t need it anymore          // close cam device, we don`t need it anymore
         cam_close_device(cam_dev);          cam_close_device(cam_dev);
         // handle as usual scsi          // handle as usual scsi
        return new freebsd_scsi_device(this, name, "");              return new freebsd_scsi_device(this, test_name, "");      
       }        }
     }      }
   }    }
   // device is LSI raid supported by mfi driver    // device is LSI raid supported by mfi driver
  if(!strncmp("/dev/mfid", name, strlen("/dev/mfid")))  if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid")))
     set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information");      set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information");
   // device type unknown    // device type unknown
   return 0;    return 0;
Line 2167  smart_device * freebsd_smart_interface::get_custom_sma Line 1880  smart_device * freebsd_smart_interface::get_custom_sma
   // 3Ware ?    // 3Ware ?
   static const char * fbsd_dev_twe_ctrl = "/dev/twe";    static const char * fbsd_dev_twe_ctrl = "/dev/twe";
   static const char * fbsd_dev_twa_ctrl = "/dev/twa";    static const char * fbsd_dev_twa_ctrl = "/dev/twa";
     static const char * fbsd_dev_tws_ctrl = "/dev/tws";
   int disknum = -1, n1 = -1, n2 = -1, contr = -1;    int disknum = -1, n1 = -1, n2 = -1, contr = -1;
   
   if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {    if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
Line 2180  smart_device * freebsd_smart_interface::get_custom_sma Line 1894  smart_device * freebsd_smart_interface::get_custom_sma
     }      }
   
     // guess 3ware device type based on device name      // guess 3ware device type based on device name
    if (!strncmp(fbsd_dev_twa_ctrl, name, strlen(fbsd_dev_twa_ctrl))){    if (str_starts_with(name, fbsd_dev_twa_ctrl) ||
         str_starts_with(name, fbsd_dev_tws_ctrl)   ) {
       contr=CONTROLLER_3WARE_9000_CHAR;        contr=CONTROLLER_3WARE_9000_CHAR;
     }      }
     if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){      if (!strncmp(fbsd_dev_twe_ctrl, name, strlen(fbsd_dev_twe_ctrl))){
Line 2188  smart_device * freebsd_smart_interface::get_custom_sma Line 1903  smart_device * freebsd_smart_interface::get_custom_sma
     }      }
   
     if(contr == -1){      if(contr == -1){
      set_err(EINVAL, "3ware controller type unknown, use %sX or %sX devices",       set_err(EINVAL, "3ware controller type unknown, use %sX, %sX or %sX devices", 
        fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl);        fbsd_dev_twe_ctrl, fbsd_dev_twa_ctrl, fbsd_dev_tws_ctrl);
       return 0;        return 0;
     }      }
     return new freebsd_escalade_device(this, name, contr, disknum);      return new freebsd_escalade_device(this, name, contr, disknum);
  }   }
   
   // Highpoint ?    // Highpoint ?
   int controller = -1, channel = -1; disknum = 1;    int controller = -1, channel = -1; disknum = 1;
Line 2230  smart_device * freebsd_smart_interface::get_custom_sma Line 1945  smart_device * freebsd_smart_interface::get_custom_sma
       set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum);        set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum);
       return 0;        return 0;
     }      }
    return new freebsd_cciss_device(this, name, disknum);    return get_sat_device("sat,auto", new freebsd_cciss_device(this, name, disknum));
   }    }
 #if FREEBSDVER > 800100  #if FREEBSDVER > 800100
   // adaX devices ?    // adaX devices ?
Line 2239  smart_device * freebsd_smart_interface::get_custom_sma Line 1954  smart_device * freebsd_smart_interface::get_custom_sma
 #endif  #endif
   // Areca?    // Areca?
   disknum = n1 = n2 = -1;    disknum = n1 = n2 = -1;
  if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {  int encnum = 1;
    if (n2 != (int)strlen(type)) {  if (sscanf(type, "areca,%n%d/%d%n", &n1, &disknum, &encnum, &n2) >= 1 || n1 == 6) {
      set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer");    if (!(1 <= disknum && disknum <= 128)) {
       set_err(EINVAL, "Option -d areca,N/E (N=%d) must have 1 <= N <= 128", disknum);
       return 0;        return 0;
     }      }
    if (!(1 <= disknum && disknum <= 24)) {    if (!(1 <= encnum && encnum <= 8)) {
      set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);      set_err(EINVAL, "Option -d areca,N/E (E=%d) must have 1 <= E <= 8", encnum);
       return 0;        return 0;
     }      }
    return new freebsd_areca_device(this, name, disknum);    return new freebsd_areca_ata_device(this, name, disknum, encnum);
   }    }
   
   return 0;    return 0;
Line 2256  smart_device * freebsd_smart_interface::get_custom_sma Line 1972  smart_device * freebsd_smart_interface::get_custom_sma
   
 std::string freebsd_smart_interface::get_valid_custom_dev_types_str()  std::string freebsd_smart_interface::get_valid_custom_dev_types_str()
 {  {
  return "3ware,N, hpt,L/M/N, cciss,N, areca,N"  return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E"
 #if FREEBSDVER > 800100  #if FREEBSDVER > 800100
   ", atacam"    ", atacam"
 #endif  #endif

Removed from v.1.1  
changed lines
  Added in v.1.1.1.3


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>