📄 os_linux.cpp
字号:
io_hdr.dxferp = iop->dxferp; io_hdr.cmdp = iop->cmnd; io_hdr.sbp = iop->sensep; /* sg_io_hdr interface timeout has millisecond units. Timeout of 0 defaults to 60 seconds. */ io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000; switch (iop->dxfer_dir) { case DXFER_NONE: io_hdr.dxfer_direction = SG_DXFER_NONE; break; case DXFER_FROM_DEVICE: io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; break; case DXFER_TO_DEVICE: io_hdr.dxfer_direction = SG_DXFER_TO_DEV; break; default: pout("do_scsi_cmnd_io: bad dxfer_dir\n"); return -EINVAL; } iop->resp_sense_len = 0; iop->scsi_status = 0; iop->resid = 0; if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) { if (report && (! unknown)) pout(" SG_IO ioctl failed, errno=%d [%s]\n", errno, strerror(errno)); return -errno; } iop->resid = io_hdr.resid; iop->scsi_status = io_hdr.status; if (report > 0) { pout(" scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n" " info=0x%x duration=%d milliseconds resid=%d\n", io_hdr.status, io_hdr.host_status, io_hdr.driver_status, io_hdr.info, io_hdr.duration, io_hdr.resid); if (report > 1) { if (DXFER_FROM_DEVICE == iop->dxfer_dir) { int trunc, len; len = iop->dxfer_len - iop->resid; trunc = (len > 256) ? 1 : 0; if (len > 0) { pout(" Incoming data, len=%d%s:\n", len, (trunc ? " [only first 256 bytes shown]" : "")); dStrHex((const char*)iop->dxferp, (trunc ? 256 : len), 1); } else pout(" Incoming data trimmed to nothing by resid\n"); } } } if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */ int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status); if (0 != io_hdr.host_status) { if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) || (LSCSI_DID_BUS_BUSY == io_hdr.host_status) || (LSCSI_DID_TIME_OUT == io_hdr.host_status)) return -ETIMEDOUT; else return -EIO; /* catch all */ } if (0 != masked_driver_status) { if (LSCSI_DRIVER_TIMEOUT == masked_driver_status) return -ETIMEDOUT; else if (LSCSI_DRIVER_SENSE != masked_driver_status) return -EIO; } if (LSCSI_DRIVER_SENSE == masked_driver_status) iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; iop->resp_sense_len = io_hdr.sb_len_wr; if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && iop->sensep && (iop->resp_sense_len > 0)) { if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", (int)iop->resp_sense_len); dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1); } } if (report) { if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { 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 pout(" status=0x%x\n", iop->scsi_status); } } return 0;#endif}struct linux_ioctl_send_command{ int inbufsize; int outbufsize; UINT8 buff[MAX_DXFER_LEN + 16];};/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't * support: CDB length (guesses it from opcode), resid and timeout. * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout * to 2 hours in order to allow long foreground extended self tests. */static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report){ struct linux_ioctl_send_command wrk; int status, buff_offset; size_t len; memcpy(wrk.buff, iop->cmnd, iop->cmnd_len); buff_offset = iop->cmnd_len; 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 : iop->dxfer_len) , 1); } else j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n"); pout(buff); } switch (iop->dxfer_dir) { case DXFER_NONE: wrk.inbufsize = 0; wrk.outbufsize = 0; break; case DXFER_FROM_DEVICE: wrk.inbufsize = 0; if (iop->dxfer_len > MAX_DXFER_LEN) return -EINVAL; wrk.outbufsize = iop->dxfer_len; break; case DXFER_TO_DEVICE: if (iop->dxfer_len > MAX_DXFER_LEN) return -EINVAL; memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len); wrk.inbufsize = iop->dxfer_len; wrk.outbufsize = 0; break; default: pout("do_scsi_cmnd_io: bad dxfer_dir\n"); return -EINVAL; } iop->resp_sense_len = 0; iop->scsi_status = 0; iop->resid = 0; status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk); if (-1 == status) { if (report) pout(" SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n", errno, strerror(errno)); return -errno; } if (0 == status) { if (report > 0) pout(" status=0\n"); if (DXFER_FROM_DEVICE == iop->dxfer_dir) { memcpy(iop->dxferp, wrk.buff, iop->dxfer_len); if (report > 1) { int 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((const char*)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1); } } return 0; } iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */ if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf)) iop->scsi_status = SCSI_STATUS_CHECK_CONDITION; len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ? SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len; if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && iop->sensep && (len > 0)) { memcpy(iop->sensep, wrk.buff, len); iop->resp_sense_len = len; if (report > 1) { pout(" >>> Sense buffer, len=%d:\n", (int)len); dStrHex((const char *)wrk.buff, len , 1); } } if (report) { if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) { pout(" status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff, wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]); } else pout(" status=0x%x\n", status); } if (iop->scsi_status > 0) return 0; else { if (report > 0) pout(" ioctl status=0x%x but scsi status=0, fail with EIO\n", status); return -EIO; /* give up, assume no device there */ }}/* SCSI command transmission interface function, linux version. * Returns 0 if SCSI command successfully launched and response * received. Even when 0 is returned the caller should check * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings * (e.g. CHECK CONDITION). If the SCSI command could not be issued * (e.g. device not present or timeout) or some other problem * (e.g. timeout) then returns a negative errno value */static int do_normal_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report){ int res; /* implementation relies on static sg_io_state variable. If not * previously set tries the SG_IO ioctl. If that succeeds assume * that SG_IO ioctl functional. If it fails with an errno value * other than ENODEV (no device) or permission then assume * SCSI_IOCTL_SEND_COMMAND is the only option. */ switch (sg_io_state) { case SG_IO_PRESENT_UNKNOWN: /* ignore report argument */ if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, 1))) { sg_io_state = SG_IO_PRESENT_YES; return 0; } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res)) return res; /* wait until we see a device */ sg_io_state = SG_IO_PRESENT_NO; /* drop through by design */ case SG_IO_PRESENT_NO: return sisc_cmnd_io(dev_fd, iop, report); case SG_IO_PRESENT_YES: return sg_io_cmnd_io(dev_fd, iop, report, 0); default: pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); sg_io_state = SG_IO_PRESENT_UNKNOWN; return -EIO; /* report error and reset state */ }}/* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better we can take off this crude way of calling the right interface */ int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report) { switch(con->controller_type) { case CONTROLLER_CCISS:#ifdef HAVE_LINUX_CCISS_IOCTL_H return cciss_io_interface(dev_fd, con->controller_port-1, iop, report);#else { static int warned = 0; if (!warned) { pout("CCISS support is not available in this build of smartmontools,\n" "<linux/cciss_ioctl.h> was not available at build time.\n\n"); warned = 1; } } errno = ENOSYS; return -1;#endif // not reached break; default: return do_normal_scsi_cmnd_io(dev_fd, iop, report); // not reached break; } }// >>>>>> End of general SCSI specific linux code// prototypevoid printwarning(smart_command_set command);// 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 linux interface to the 3ware 3w-xxxx 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// escalade_type indicates the type of controller type, and if scsi or char interface is used// 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"/* 512 is the max payload size: increase if needed */#define BUFFER_LEN_678K ( sizeof(TW_Ioctl) ) // 1044 unpacked, 1041 packed#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1 ) // 1539 unpacked, 1536 packed#define BUFFER_LEN_9000 ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) )int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){ // return value and buffer for ioctl() int ioctlreturn, readdata=0; // Used by both the SCSI and char interfaces TW_Passthru *passthru=NULL; char ioctl_buffer[TW_IOCTL_BUFFER_SIZE]; // only used for SCSI device interface TW_Ioctl *tw_ioctl=NULL; TW_Output *tw_output=NULL; // only used for 6000/7000/8000 char device interface TW_New_Ioctl *tw_ioctl_char=NULL; // only used for 9000 character device interface TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL; memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE); if (escalade_type==CONTROLLER_3WARE_9000_CHAR) { tw_ioctl_apache = (TW_Ioctl_Buf_Apache *)ioctl_buffer; tw_ioctl_apache->driver_command.control_code = TW_IOCTL_FIRMWARE_PASS_THROUGH; tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */ passthru = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand); } else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) { tw_ioctl_char = (TW_New_Ioctl *)ioctl_buffer; tw_ioctl_char->data_buffer_length = 512; passthru = (TW_Passthru *)&(tw_ioctl_char->firmware_command); } else if (escalade_type==CONTROLLER_3WARE_678K) { tw_ioctl = (TW_Ioctl *)ioctl_buffer; tw_ioctl->cdb[0] = TW_IOCTL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -