+private:
+ int m_fd;
+ struct cam_device *m_camdev;
+};
+
+bool freebsd_scsi_device::open(){
+ const char *dev = get_dev_name();
+
+ if ((m_camdev = cam_open_device(dev, O_RDWR)) == NULL) {
+ set_err(errno);
+ return false;
+ }
+ set_fd(m_camdev->fd);
+ return true;
+}
+
+bool freebsd_scsi_device::close(){
+ cam_close_device(m_camdev);
+ set_fd(-1);
+ return true;
+}
+
+freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf,
+ const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+ freebsd_smart_device("SCSI")
+{
+}
+
+
+bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+ int report=scsi_debugmode;
+ union ccb *ccb;
+
+ if (report > 0) {
+ unsigned int k;
+ const unsigned char * ucp = iop->cmnd;
+ const char * np;
+
+ np = scsi_get_opcode_name(ucp[0]);
+ pout(" [%s: ", np ? np : "<unknown opcode>");
+ for (k = 0; k < iop->cmnd_len; ++k)
+ pout("%02x ", ucp[k]);
+ if ((report > 1) &&
+ (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+ pout("]\n Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+ }
+ else
+ pout("]");
+ }
+
+ if(m_camdev==NULL) {
+ warnx("error: camdev=0!");
+ return -ENOTTY;
+ }
+
+ if (!(ccb = cam_getccb(m_camdev))) {
+ warnx("error allocating ccb");
+ 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
+ bzero(&(&ccb->ccb_h)[1],
+ sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+ cam_fill_csio(&ccb->csio,
+ /*retrires*/ 1,
+ /*cbfcnp*/ NULL,
+ /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)),
+ /* tagaction */ MSG_SIMPLE_Q_TAG,
+ /* dataptr */ iop->dxferp,
+ /* datalen */ iop->dxfer_len,
+ /* senselen */ iop->max_sense_len,
+ /* cdblen */ iop->cmnd_len,
+ /* timout (converted to seconds) */ iop->timeout*1000);
+ memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len);
+
+ if (cam_send_ccb(m_camdev,ccb) < 0) {
+ warn("error sending SCSI ccb");
+ cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ cam_freeccb(ccb);
+ return -EIO;
+ }
+
+ if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) && ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR)) {
+ cam_error_print(m_camdev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ cam_freeccb(ccb);
+ return -EIO;
+ }
+
+ if (iop->sensep) {
+ iop->resp_sense_len = ccb->csio.sense_len - ccb->csio.sense_resid;
+ memcpy(iop->sensep,&(ccb->csio.sense_data),iop->resp_sense_len);
+ }
+
+ iop->scsi_status = ccb->csio.scsi_status;
+
+ cam_freeccb(ccb);
+
+ if (report > 0) {
+ int trunc;
+
+ pout(" status=0\n");
+ trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+ pout(" Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ 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;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Areca RAID support
+
+///////////////////////////////////////////////////////////////////
+// SATA(ATA) device behind Areca RAID Controller
+class freebsd_areca_ata_device
+: public /*implements*/ areca_ata_device,
+ public /*extends*/ freebsd_smart_device
+{
+public:
+ 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);
+};
+
+///////////////////////////////////////////////////////////////////
+// SAS(SCSI) device behind Areca RAID Controller
+class freebsd_areca_scsi_device
+: public /*implements*/ areca_scsi_device,
+ 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);
+};
+
+
+// Areca RAID Controller(SATA Disk)
+freebsd_areca_ata_device::freebsd_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
+: smart_device(intf, dev_name, "areca", "areca"),
+ freebsd_smart_device("ATA")
+{
+ set_disknum(disknum);
+ set_encnum(encnum);
+ set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+}
+
+
+smart_device * freebsd_areca_ata_device::autodetect_open()
+{
+ int is_ata = 1;
+
+ // autodetect device type
+ is_ata = arcmsr_get_dev_type();
+ if(is_ata < 0)
+ {
+ set_err(EIO);
+ return this;
+ }
+
+ if(is_ata == 1)
+ {
+ // SATA device
+ return this;
+ }
+
+ // SAS device
+ smart_device_auto_ptr newdev(new freebsd_areca_scsi_device(smi(), get_dev_name(), get_disknum(), get_encnum()));
+ close();
+ delete this;
+ newdev->open(); // TODO: Can possibly pass open fd
+
+ return newdev.release();
+}
+
+int freebsd_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+ int ioctlreturn = 0;
+
+ if(!is_open()) {
+ if(!open()){
+ }
+ }
+
+ ioctlreturn = ioctl(get_fd(), ((sSRB_BUFFER *)(iop->dxferp))->srbioctl.ControlCode, iop->dxferp);
+ if (ioctlreturn)
+ {
+ // errors found