+// Interface to SPT SCSI devices. See scsicmds.h and os_linux.c
+static long scsi_pass_through_direct(HANDLE fd, UCHAR targetid, struct scsi_cmnd_io * iop)
+{
+ int report = scsi_debugmode; // TODO
+
+ if (report > 0) {
+ int k, j;
+ const unsigned char * ucp = iop->cmnd;
+ const char * np;
+ char buff[256];
+ const int sz = (int)sizeof(buff);
+
+ np = scsi_get_opcode_name(ucp[0]);
+ j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+ for (k = 0; k < (int)iop->cmnd_len; ++k)
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+ if ((report > 1) &&
+ (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\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
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+ pout("%s", buff);
+ }
+
+ SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+ if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+ return EINVAL;
+ }
+
+ memset(&sb, 0, sizeof(sb));
+ sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+ //sb.spt.PathId = 0;
+ sb.spt.TargetId = targetid;
+ //sb.spt.Lun = 0;
+ sb.spt.CdbLength = iop->cmnd_len;
+ memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+ sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+ sb.spt.SenseInfoOffset =
+ offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+ sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+
+ bool direct = true;
+ switch (iop->dxfer_dir) {
+ case DXFER_NONE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+ break;
+ case DXFER_FROM_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+ // transfers (needed for SMART STATUS check of JMicron USB bridges)
+ if (sb.spt.DataTransferLength == 1)
+ direct = false;
+ break;
+ case DXFER_TO_DEVICE:
+ sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+ sb.spt.DataTransferLength = iop->dxfer_len;
+ sb.spt.DataBuffer = iop->dxferp;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ long err = 0;
+ if (direct) {
+ DWORD num_out;
+ if (!DeviceIoControl(fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+ err = GetLastError();
+ }
+ else
+ err = scsi_pass_through_indirect(fd, &sb);
+
+ if (err)
+ {
+ return err;
+ }
+
+ iop->scsi_status = sb.spt.ScsiStatus;
+ if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+ int slen = sb.ucSenseBuf[7] + 8;
+
+ if (slen > (int)sizeof(sb.ucSenseBuf))
+ slen = sizeof(sb.ucSenseBuf);
+ if (slen > (int)iop->max_sense_len)
+ slen = iop->max_sense_len;
+ memcpy(iop->sensep, sb.ucSenseBuf, slen);
+ iop->resp_sense_len = slen;
+ if (report) {
+ if (report > 1) {
+ pout(" >>> Sense buffer, len=%d:\n", slen);
+ dStrHex(iop->sensep, slen , 1);
+ }
+ if ((iop->sensep[0] & 0x7f) > 0x71)
+ pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+ iop->scsi_status, iop->sensep[1] & 0xf,
+ iop->sensep[2], iop->sensep[3]);
+ else
+ pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
+ iop->scsi_status, iop->sensep[2] & 0xf,
+ iop->sensep[12], iop->sensep[13]);
+ }
+ } else
+ iop->resp_sense_len = 0;
+
+ if (iop->dxfer_len > sb.spt.DataTransferLength)
+ iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+ else
+ iop->resid = 0;
+
+ if ((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)) {
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+ pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+ }
+
+ return 0;
+}
+
+// Areca RAID Controller(SAS Device)
+win_areca_scsi_device::win_areca_scsi_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
+: smart_device(intf, dev_name, "areca", "areca")
+{
+ set_fh(INVALID_HANDLE_VALUE);
+ set_disknum(disknum);
+ set_encnum(encnum);
+ set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+}
+
+bool win_areca_scsi_device::open()
+{
+ HANDLE hFh;
+
+ if( is_open() )
+ {
+ return true;
+ }
+ hFh = CreateFile( get_dev_name(),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+ if(hFh == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+
+ set_fh(hFh);
+ return true;
+}
+
+smart_device * win_areca_scsi_device::autodetect_open()
+{
+ return this;
+}
+
+int win_areca_scsi_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+ int ioctlreturn = 0;
+
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ // errors found
+ return -1;
+ }
+ }
+
+ return ioctlreturn;
+}
+
+bool win_areca_scsi_device::arcmsr_lock()
+{
+#define SYNCOBJNAME "Global\\SynIoctlMutex"
+ int ctlrnum = -1;
+ char mutexstr[64];
+
+ if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+ return set_err(EINVAL, "unable to parse device name");
+
+ snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+ m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+ if ( m_mutex == NULL )
+ {
+ return set_err(EIO, "CreateMutex failed");
+ }
+
+ // atomic access to driver
+ WaitForSingleObject(m_mutex, INFINITE);
+
+ return true;
+}
+
+
+bool win_areca_scsi_device::arcmsr_unlock()
+{
+ if( m_mutex != NULL)
+ {
+ ReleaseMutex(m_mutex);
+ CloseHandle(m_mutex);
+ }
+
+ return true;
+}
+
+
+// Areca RAID Controller(SATA Disk)
+win_areca_ata_device::win_areca_ata_device(smart_interface * intf, const char * dev_name, int disknum, int encnum)
+: smart_device(intf, dev_name, "areca", "areca")
+{
+ set_fh(INVALID_HANDLE_VALUE);
+ set_disknum(disknum);
+ set_encnum(encnum);
+ set_info().info_name = strprintf("%s [areca_disk#%02d_enc#%02d]", dev_name, disknum, encnum);
+}
+
+bool win_areca_ata_device::open()
+{
+ HANDLE hFh;
+
+ if( is_open() )
+ {
+ return true;
+ }
+ hFh = CreateFile( get_dev_name(),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL );
+ if(hFh == INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+
+ set_fh(hFh);
+ return true;
+}
+
+smart_device * win_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 win_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 win_areca_ata_device::arcmsr_do_scsi_io(struct scsi_cmnd_io * iop)
+{
+ int ioctlreturn = 0;
+
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 16, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ ioctlreturn = scsi_pass_through_direct(get_fh(), 127, iop);
+ if ( ioctlreturn || iop->scsi_status )
+ {
+ // errors found
+ return -1;
+ }
+ }
+
+ return ioctlreturn;
+}
+
+bool win_areca_ata_device::arcmsr_lock()
+{
+#define SYNCOBJNAME "Global\\SynIoctlMutex"
+ int ctlrnum = -1;
+ char mutexstr[64];
+
+ if (sscanf(get_dev_name(), "\\\\.\\scsi%d:", &ctlrnum) < 1)
+ return set_err(EINVAL, "unable to parse device name");
+
+ snprintf(mutexstr, sizeof(mutexstr), "%s%d", SYNCOBJNAME, ctlrnum);
+ m_mutex = CreateMutex(NULL, FALSE, mutexstr);
+ if ( m_mutex == NULL )
+ {
+ return set_err(EIO, "CreateMutex failed");
+ }
+
+ // atomic access to driver
+ WaitForSingleObject(m_mutex, INFINITE);
+
+ return true;
+}
+
+
+bool win_areca_ata_device::arcmsr_unlock()
+{
+ if( m_mutex != NULL)
+ {
+ ReleaseMutex(m_mutex);
+ CloseHandle(m_mutex);
+ }
+
+ return true;
+}
+
+// AACRAID
+win_aacraid_device::win_aacraid_device(smart_interface * intf,
+ const char *dev_name, unsigned ctrnum, unsigned target, unsigned lun)
+: smart_device(intf, dev_name, "aacraid", "aacraid"),
+ m_ctrnum(ctrnum), m_lun(lun), m_target(target)
+{
+ set_info().info_name = strprintf("%s [aacraid_disk_%02d_%02d_%d]", dev_name, m_ctrnum, m_lun, m_target);
+ set_info().dev_type = strprintf("aacraid,%d,%d,%d", m_ctrnum, m_lun, m_target);
+}
+
+win_aacraid_device::~win_aacraid_device() throw()
+{
+}
+
+bool win_aacraid_device::open()
+{
+ if (is_open())
+ return true;
+
+ HANDLE hFh = CreateFile( get_dev_name(),
+ GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ 0);
+ if (hFh == INVALID_HANDLE_VALUE)
+ return set_err(ENODEV, "Open failed, Error=%u", (unsigned)GetLastError());
+
+ set_fh(hFh);
+ return true;
+}
+
+bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
+{
+ int report = scsi_debugmode;
+ if (report > 0)
+ {
+ int k, j;
+ const unsigned char * ucp = iop->cmnd;
+ const char * np;
+ char buff[256];
+ const int sz = (int)sizeof(buff);
+ np = scsi_get_opcode_name(ucp[0]);
+ j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+ for (k = 0; k < (int)iop->cmnd_len; ++k)
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+ if ((report > 1) &&
+ (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing "
+ "data, len=%d%s:\n", (int)iop->dxfer_len,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ dStrHex((const char *)iop->dxferp,
+ (trunc ? 256 : (int)iop->dxfer_len) , 1);
+ }
+ else
+ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+ pout("buff %s\n",buff);
+ }
+
+ char ioBuffer[1000];
+ SRB_IO_CONTROL * pSrbIO = (SRB_IO_CONTROL *) ioBuffer;
+ SCSI_REQUEST_BLOCK * pScsiIO = (SCSI_REQUEST_BLOCK *) (ioBuffer + sizeof(SRB_IO_CONTROL));
+ DWORD scsiRequestBlockSize = sizeof(SCSI_REQUEST_BLOCK);
+ char *pRequestSenseIO = (char *) (ioBuffer + sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize);
+ DWORD dataOffset = (sizeof(SRB_IO_CONTROL) + scsiRequestBlockSize + 7) & 0xfffffff8;
+ char *pDataIO = (char *) (ioBuffer + dataOffset);
+ memset(pScsiIO, 0, scsiRequestBlockSize);
+ pScsiIO->Length = (USHORT) scsiRequestBlockSize;
+ pScsiIO->Function = SRB_FUNCTION_EXECUTE_SCSI;
+ pScsiIO->PathId = 0;
+ pScsiIO->TargetId = m_target;
+ pScsiIO->Lun = m_lun;
+ pScsiIO->CdbLength = (int)iop->cmnd_len;
+ switch(iop->dxfer_dir){
+ case DXFER_NONE:
+ pScsiIO->SrbFlags = SRB_NoDataXfer;
+ break;
+ case DXFER_FROM_DEVICE:
+ pScsiIO->SrbFlags |= SRB_DataIn;
+ break;
+ case DXFER_TO_DEVICE:
+ pScsiIO->SrbFlags |= SRB_DataOut;
+ break;
+ default:
+ pout("aacraid: bad dxfer_dir\n");
+ return set_err(EINVAL, "aacraid: bad dxfer_dir\n");
+ }
+ pScsiIO->DataTransferLength = (ULONG)iop->dxfer_len;
+ pScsiIO->TimeOutValue = iop->timeout;
+ UCHAR *pCdb = (UCHAR *) pScsiIO->Cdb;
+ memcpy(pCdb, iop->cmnd, 16);
+ if (iop->max_sense_len){
+ memset(pRequestSenseIO, 0, iop->max_sense_len);
+ }
+ if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_OUT){
+ memcpy(pDataIO, iop->dxferp, iop->dxfer_len);
+ }
+ else if (pScsiIO->SrbFlags & SRB_FLAGS_DATA_IN){
+ memset(pDataIO, 0, iop->dxfer_len);
+ }
+
+ DWORD bytesReturned = 0;
+ memset(pSrbIO, 0, sizeof(SRB_IO_CONTROL));
+ pSrbIO->HeaderLength = sizeof(SRB_IO_CONTROL);
+ memcpy(pSrbIO->Signature, "AACAPI", 7);
+ pSrbIO->ControlCode = ARCIOCTL_SEND_RAW_SRB;
+ pSrbIO->Length = (dataOffset + iop->dxfer_len - sizeof(SRB_IO_CONTROL) + 7) & 0xfffffff8;
+ pSrbIO->Timeout = 3*60;
+
+ if (!DeviceIoControl(
+ get_fh(),
+ IOCTL_SCSI_MINIPORT,
+ ioBuffer,
+ sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+ ioBuffer,
+ sizeof(SRB_IO_CONTROL) + pSrbIO->Length,
+ &bytesReturned,
+ NULL)
+ ) {
+ return set_err(EIO, "ARCIOCTL_SEND_RAW_SRB failed, Error=%u", (unsigned)GetLastError());
+ }
+
+ iop->scsi_status = pScsiIO->ScsiStatus;
+ if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+ int slen = sizeof(pRequestSenseIO) + 8;
+ if (slen > (int)sizeof(pRequestSenseIO))
+ slen = sizeof(pRequestSenseIO);
+ if (slen > (int)iop->max_sense_len)
+ slen = (int)iop->max_sense_len;
+ memcpy(iop->sensep, pRequestSenseIO, slen);
+ iop->resp_sense_len = slen;
+ if (report) {
+ if (report > 1) {
+ pout(" >>> Sense buffer, len=%d:\n", slen);
+ dStrHex(iop->sensep, slen , 1);
+ }
+ if ((iop->sensep[0] & 0x7f) > 0x71)
+ pout(" status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+ iop->scsi_status, iop->sensep[1] & 0xf,
+ iop->sensep[2], iop->sensep[3]);
+ else
+ pout(" status=%x: sense_key=%x asc=%x ascq=%x\n",
+ iop->scsi_status, iop->sensep[2] & 0xf,
+ iop->sensep[12], iop->sensep[13]);
+ }
+ }
+ else {
+ iop->resp_sense_len = 0;
+ }
+
+ if (iop->dxfer_dir == DXFER_FROM_DEVICE){
+ memcpy(iop->dxferp,pDataIO, iop->dxfer_len);
+ }
+ if((iop->dxfer_dir == DXFER_FROM_DEVICE) && (report > 1)){
+ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+ pout(" Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
+ (trunc ? " [only first 256 bytes shown]" : ""));
+ dStrHex((CHAR*)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
+ }
+ return true;
+}