📄 os_linux.cpp
字号:
// and set up return values *names=mp; return n;}// makes a list of device names to scan, for either ATA or SCSI// devices. Return -1 if no memory remaining, else the number of// devices on the list, which can be >=0.int make_device_names (char*** devlist, const char* name) { int retval, maxdev;#if 0 // for testing case where no device names are found return 0;#endif if (!strcmp(name,"SCSI")) retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26); else if (!strcmp(name,"ATA")) retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20); else // don't recognize disk type! return 0; // if we found traditional links, we are done if (retval>0) return retval; // else look for devfs entries without traditional links return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev);}// 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 one.// DETAILED DESCRIPTION OF ARGUMENTS// device: is the file descriptor provided by open()// 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"#define BUFFER_LENGTH (4+512)int ata_command_interface(int device, smart_command_set command, int select, char *data){ unsigned char buff[BUFFER_LENGTH]; // positive: bytes to write to caller. negative: bytes to READ from // caller. zero: non-data command int copydata=0; const int HDIO_DRIVE_CMD_OFFSET = 4; // See struct hd_drive_cmd_hdr in hdreg.h. Before calling ioctl() // buff[0]: ATA COMMAND CODE REGISTER // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER // buff[2]: ATA FEATURES REGISTER // buff[3]: ATA SECTOR COUNT REGISTER // Note that on return: // buff[2] contains the ATA SECTOR COUNT REGISTER // clear out buff. Large enough for HDIO_DRIVE_CMD (4+512 bytes) memset(buff, 0, BUFFER_LENGTH); buff[0]=ATA_SMART_CMD; switch (command){ case CHECK_POWER_MODE: buff[0]=ATA_CHECK_POWER_MODE; copydata=1; break; case READ_VALUES: buff[2]=ATA_SMART_READ_VALUES; buff[3]=1; copydata=512; break; case READ_THRESHOLDS: buff[2]=ATA_SMART_READ_THRESHOLDS; buff[1]=buff[3]=1; copydata=512; break; case READ_LOG: buff[2]=ATA_SMART_READ_LOG_SECTOR; buff[1]=select; buff[3]=1; copydata=512; break; case WRITE_LOG: break; case IDENTIFY: buff[0]=ATA_IDENTIFY_DEVICE; buff[3]=1; copydata=512; break; case PIDENTIFY: buff[0]=ATA_IDENTIFY_PACKET_DEVICE; buff[3]=1; copydata=512; break; case ENABLE: buff[2]=ATA_SMART_ENABLE; buff[1]=1; break; case DISABLE: buff[2]=ATA_SMART_DISABLE; buff[1]=1; break; case STATUS: // this command only says if SMART is working. It could be // replaced with STATUS_CHECK below. buff[2]=ATA_SMART_STATUS; break; case AUTO_OFFLINE: // NOTE: According to ATAPI 4 and UP, this command is obsolete // select == 241 for enable but no data transfer. Use TASK ioctl. buff[1]=ATA_SMART_AUTO_OFFLINE; buff[2]=select; break; case AUTOSAVE: // select == 248 for enable but no data transfer. Use TASK ioctl. buff[1]=ATA_SMART_AUTOSAVE; buff[2]=select; break; case IMMEDIATE_OFFLINE: buff[2]=ATA_SMART_IMMEDIATE_OFFLINE; buff[1]=select; break; case STATUS_CHECK: // This command uses HDIO_DRIVE_TASK and has different syntax than // the other commands. buff[1]=ATA_SMART_STATUS; break; default: pout("Unrecognized command %d in linux_ata_command_interface()\n" "Please contact " PACKAGE_BUGREPORT "\n", command); errno=ENOSYS; return -1; } // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the // only ioctl() that can be used to WRITE data to the disk. if (command==WRITE_LOG) { unsigned char task[sizeof(ide_task_request_t)+512]; ide_task_request_t *reqtask=(ide_task_request_t *) task; task_struct_t *taskfile=(task_struct_t *) reqtask->io_ports; int retval; memset(task, 0, sizeof(task)); taskfile->data = 0; taskfile->feature = ATA_SMART_WRITE_LOG_SECTOR; taskfile->sector_count = 1; taskfile->sector_number = select; taskfile->low_cylinder = 0x4f; taskfile->high_cylinder = 0xc2; taskfile->device_head = 0; taskfile->command = ATA_SMART_CMD; reqtask->data_phase = TASKFILE_OUT; reqtask->req_cmd = IDE_DRIVE_TASK_OUT; reqtask->out_size = 512; reqtask->in_size = 0; // copy user data into the task request structure memcpy(task+sizeof(ide_task_request_t), data, 512); if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) { if (retval==-EINVAL) pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n"); return -1; } return 0; } // There are two different types of ioctls(). The HDIO_DRIVE_TASK // one is this: if (command==STATUS_CHECK || command==AUTOSAVE || command==AUTO_OFFLINE){ int retval; // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You // have to read the IDE driver source code. Sigh. // buff[0]: ATA COMMAND CODE REGISTER // buff[1]: ATA FEATURES REGISTER // buff[2]: ATA SECTOR_COUNT // buff[3]: ATA SECTOR NUMBER // buff[4]: ATA CYL LO REGISTER // buff[5]: ATA CYL HI REGISTER // buff[6]: ATA DEVICE HEAD unsigned const char normal_lo=0x4f, normal_hi=0xc2; unsigned const char failed_lo=0xf4, failed_hi=0x2c; buff[4]=normal_lo; buff[5]=normal_hi; if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) { if (retval==-EINVAL) { pout("Error SMART Status command via HDIO_DRIVE_TASK failed"); pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n"); } else syserror("Error SMART Status command failed"); return -1; } // Cyl low and Cyl high unchanged means "Good SMART status" if (buff[4]==normal_lo && buff[5]==normal_hi) return 0; // These values mean "Bad SMART status" if (buff[4]==failed_lo && buff[5]==failed_hi) return 1; // We haven't gotten output that makes sense; print out some debugging info syserror("Error SMART Status command failed"); pout("Please get assistance from " PACKAGE_HOMEPAGE "\n"); pout("Register values returned from SMART Status command are:\n"); pout("ST =0x%02x\n",(int)buff[0]); pout("ERR=0x%02x\n",(int)buff[1]); pout("NS =0x%02x\n",(int)buff[2]); pout("SC =0x%02x\n",(int)buff[3]); pout("CL =0x%02x\n",(int)buff[4]); pout("CH =0x%02x\n",(int)buff[5]); pout("SEL=0x%02x\n",(int)buff[6]); return -1; }#if 1 // Note to people doing ports to other OSes -- don't worry about // this block -- you can safely ignore it. I have put it here // because under linux when you do IDENTIFY DEVICE to a packet // device, it generates an ugly kernel syslog error message. This // is harmless but frightens users. So this block detects packet // devices and make IDENTIFY DEVICE fail "nicely" without a syslog // error message. // // If you read only the ATA specs, it appears as if a packet device // *might* respond to the IDENTIFY DEVICE command. This is // misleading - it's because around the time that SFF-8020 was // incorporated into the ATA-3/4 standard, the ATA authors were // sloppy. See SFF-8020 and you will see that ATAPI devices have // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their // command set, and return 'Command Aborted' to IDENTIFY DEVICE. if (command==IDENTIFY || command==PIDENTIFY){ unsigned short deviceid[256]; // check the device identity, as seen when the system was booted // or the device was FIRST registered. This will not be current // if the user has subsequently changed some of the parameters. If // device is a packet device, swap the command interpretations. if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000)) buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE; }#endif // We are now doing the HDIO_DRIVE_CMD type ioctl. if ((ioctl(device, HDIO_DRIVE_CMD, buff))) return -1; // CHECK POWER MODE command returns information in the Sector Count // register (buff[3]). Copy to return data buffer. if (command==CHECK_POWER_MODE) buff[HDIO_DRIVE_CMD_OFFSET]=buff[2]; // if the command returns data then copy it back if (copydata) memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata); return 0;}// >>>>>> Start of general SCSI specific linux code/* Linux specific code. * Historically smartmontools (and smartsuite before it) used the * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device * nodes that use the SCSI subsystem. A better interface has been available * via the SCSI generic (sg) driver but this involves the extra step of * mapping disk devices (e.g. /dev/sda) to the corresponding sg device * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of * the sg driver have become available via the SG_IO ioctl which is available * on all SCSI devices (on SCSI tape devices from lk 2.6.6). * So the strategy below is to find out if the SG_IO ioctl is available and * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl. * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */#define MAX_DXFER_LEN 1024 /* can be increased if necessary */#define SEND_IOCTL_RESP_SENSE_LEN 16 /* ioctl limitation */#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */#define LSCSI_DRIVER_MASK 0xf /* mask out "suggestions" */#define LSCSI_DRIVER_SENSE 0x8 /* alternate CHECK CONDITION indication */#define LSCSI_DRIVER_TIMEOUT 0x6#define LSCSI_DID_TIME_OUT 0x3#define LSCSI_DID_BUS_BUSY 0x2#define LSCSI_DID_NO_CONNECT 0x1#ifndef SCSI_IOCTL_SEND_COMMAND#define SCSI_IOCTL_SEND_COMMAND 1#endif#define SG_IO_PRESENT_UNKNOWN 0#define SG_IO_PRESENT_YES 1#define SG_IO_PRESENT_NO 2static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, int unknown);static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);static int sg_io_state = SG_IO_PRESENT_UNKNOWN;/* Preferred implementation for issuing SCSI commands in linux. This * function uses the SG_IO ioctl. Return 0 if command issued successfully * (various status values should still be checked). If the SCSI command * cannot be issued then a negative errno value is returned. */static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report, int unknown){#ifndef SG_IO ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report); return -ENOTTY;#else struct sg_io_hdr io_hdr; 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); } memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = iop->cmnd_len; io_hdr.mx_sb_len = iop->max_sense_len; io_hdr.dxfer_len = iop->dxfer_len;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -