Index: sbin/camcontrol/camcontrol.8 =================================================================== --- sbin/camcontrol/camcontrol.8 +++ sbin/camcontrol/camcontrol.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd August 6, 2019 +.Dd April 26, 2021 .Dt CAMCONTROL 8 .Os .Sh NAME @@ -366,6 +366,9 @@ .Ic devtype .Op device id .Nm +.Ic smart +.Op device id +.Nm .Ic help .Sh DESCRIPTION The @@ -2594,6 +2597,8 @@ .It illegal A programming error occurred .El +.It Ic smart +Print out the SMART status for specified device. .It Ic help Print out verbose usage information. .El Index: sbin/camcontrol/camcontrol.c =================================================================== --- sbin/camcontrol/camcontrol.c +++ sbin/camcontrol/camcontrol.c @@ -111,6 +111,7 @@ CAM_CMD_POWER_MODE, CAM_CMD_DEVTYPE, CAM_CMD_AMA, + CAM_CMD_SMART_STATUS, } cam_cmd; typedef enum { @@ -228,6 +229,7 @@ {"zone", CAM_CMD_ZONE, CAM_ARG_NONE, "ac:l:No:P:"}, {"epc", CAM_CMD_EPC, CAM_ARG_NONE, "c:dDeHp:Pr:sS:T:"}, {"timestamp", CAM_CMD_TIMESTAMP, CAM_ARG_NONE, "f:mrsUT:"}, + {"smart", CAM_CMD_SMART_STATUS, CAM_ARG_NONE, NULL}, {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-h", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, @@ -355,6 +357,7 @@ static int scsiopcodes(struct cam_device *device, int argc, char **argv, char *combinedopt, int task_attr, int retry_count, int timeout, int verbose); +static int atasmartstatus(struct cam_device *device); #ifndef min #define min(a,b) (((a)<(b))?(a):(b)) @@ -9865,6 +9868,72 @@ return (retval); } +static int +atasmartstatus(struct cam_device *device) +{ + union ccb *ccb; + camcontrol_devtype devtype; + uint32_t flags = CAM_DIR_NONE; + int retry_count = 1, timeout = 0, retval = 0; + + if (get_device_type(device, retry_count, timeout, 0, &devtype) != 0) { + warnx("atasmartstatus: Error getting device type.\n"); + return(1); + } + + if (devtype != CC_DT_ATA) { + warnx("atasmartstatus: %s is not an ATA device.\n", device->given_dev_name); + return(1); + } + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("atasmartstatus: Error allocating CCB.\n"); + return(1); + } + + ccb->ataio.cmd.flags |= CAM_ATAIO_NEEDRESULT; + ccb->ataio.cmd.command = ATA_SMART_CMD; + ccb->ataio.cmd.features = ATA_SMART_RETURN_STATUS_FEATURE; + ccb->ataio.cmd.lba_mid = (ATA_SMART_GOOD_STATUS & 0xFF); + ccb->ataio.cmd.lba_high = (ATA_SMART_GOOD_STATUS >> 8); + cam_fill_ataio(&ccb->ataio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*flags*/ flags, + /*tag_action*/ 0, + /*data_ptr*/ NULL, + /*dxfer_len*/ 0, + /*timeout*/ timeout ? timeout : 5000); + + if (((retval = cam_send_ccb(device, ccb)) < 0) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { + const char warnstr[] = + "atasmartstatus: error sending ATA smart status command"; + + if (retval < 0) + warn(warnstr); + else + warnx(warnstr); + return (1); + } + + if (((ccb->ataio.res.lba_high << 8) | (ccb->ataio.res.lba_mid)) == + ATA_SMART_GOOD_STATUS) { + printf("camcontrol: *Good* The device hasn't detected a threshold exceeding condition.\n"); + } else if (((ccb->ataio.res.lba_high << 8) | (ccb->ataio.res.lba_mid)) + == ATA_SMART_BAD_STATUS) { + printf("camcontrol: *FAILURE* : The device has detected a threshold exceeding condition.\n" \ + "Please shutdown the system and replace the device ASAP\n"); + } else if (((ccb->ataio.res.lba_high << 8) | (ccb->ataio.res.lba_mid)) + == ATA_SMART_DEAD_STATUS) { + printf("camcontrol: *Dead* Device cannot be accessed.\n"); + } else { + printf("camcontrol: *Warning* Device status unknown.\n"); + } + + return (retval); +} + void usage(int printlong) { @@ -9953,7 +10022,7 @@ " [-I]\n" " [-1 | -4]\n" " [-S high|normal]\n" -" \n" +" camcontrol smart [dev_id]\n" " camcontrol help\n"); if (!printlong) return; @@ -10000,6 +10069,7 @@ "timestamp report or set the device's timestamp\n" "devtype report the type of device\n" "mmcsdcmd send the given MMC command, needs -c and -a as well\n" +"smart display the smart status of the ATA device\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" @@ -10631,6 +10701,9 @@ task_attr, retry_count, timeout, arglist & CAM_ARG_VERBOSE); break; + case CAM_CMD_SMART_STATUS: + error = atasmartstatus(cam_dev); + break; case CAM_CMD_USAGE: usage(1); break; Index: sys/sys/ata.h =================================================================== --- sys/sys/ata.h +++ sys/sys/ata.h @@ -459,6 +459,10 @@ #define ATA_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/ #define ATA_SERVICE 0xa2 /* service command */ #define ATA_SMART_CMD 0xb0 /* SMART command */ +#define ATA_SMART_RETURN_STATUS_FEATURE 0xDA +#define ATA_SMART_GOOD_STATUS 0xC24F +#define ATA_SMART_BAD_STATUS 0x2CF4 +#define ATA_SMART_DEAD_STATUS 0xDEAD #define ATA_SANITIZE 0xb4 /* sanitize device */ #define ATA_CFA_ERASE 0xc0 /* CFA erase */ #define ATA_READ_MUL 0xc4 /* read multi */