#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/cpt.h>
#include <sys/dcmd_cam.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ntoscsi.h>
#define INVALID -1
#define WRITE_BUF_CMDLEN 10
#define READ_BUF_CMDLEN 10
#define WRITE_BUFFER_CMD 0x3B
#define READ_BUFFER_CMD 0x3c
#define BUFFER_VENDOR_MODE 0x01
#define SG_DXFER_TO_DEV -2
#define SG_DXFER_FROM_DEV -3
#define VENDOR_KEY 0x7D9C69
#define FW_REL_DATE_LEN 12
#define FW_REL_TIME_LEN 8
#define BLOCK_SIZE 512
#define OK 0
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
enum field_width {
BYTE_SIZE = 1,
WORD_SIZE = 2,
DOUBLE_WORD_SIZE = 4
};
struct desc_field_offset {
char *name;
int offset;
enum field_width width_byte;
};
static struct desc_field_offset device_report_field_name[] = {
{"AverageEraseEnh", 0, 1 << 2},
{"AverageEraseTypeA", 4, 1 << 2},
{"AverageEraseTypeB", 8, 1 << 2},
{"ReadReclaimCountTypeC", 12, 1 << 2},
{"ReadReclaimCountTypeA", 16, 1 << 2},
{"ReadReclaimCountTypeB", 20, 1 << 2},
{"BadBlockManufactory", 24, 1 << 2},
{"BadBlockRuntimeTypeC", 28, 1 << 2},
{"BadBlockRuntimeTypeA", 32, 1 << 2},
{"BadBlockRuntimeTypeB", 36, 1 << 2},
{"FieldFWUpdatesCount", 40, 1 << 2},
{"FWReleaseDate", 44, FW_REL_DATE_LEN},
{"FWReleaseTime", 56, 1 << 3},
{"CumulativeHostWriteDataSize", 64, 1 << 2},
{"NumVccVoltageDropsOccur", 68, 1 << 2},
{"NumVccVoltageDroopsOccur", 72, 1 << 2},
{"NumFailuresRecover", 76, 1 << 2},
{"TotalRecoveryOperation", 80, 1 << 2},
{"CumulativeSSLCWritePayload", 84, 1 << 2},
{"CumulativeSSLCBigFileWritePayload", 88, 1 << 2},
{"NumberTimesSSLCBigFileOperated", 92, 1 << 2},
{"AverageEraseSSLCBigModeCycles", 96, 1 << 2},
{"CumulativeInitCount", 100, 1 << 2},
{"MaxEraseCyclesTypeC", 104, 1 << 2},
{"MaxEraseCyclesTypeA", 108, 1 << 2},
{"MaxEraseCyclesTypeB", 112, 1 << 2},
{"MinEraseCyclesTypeC", 116, 1 << 2},
{"MinEraseCyclesTypeA", 120, 1 << 2},
{"MinEraseCyclesTypeB", 124, 1 << 2},
{"PreEOLWarningTypeC", 152, 1 << 2},
{"PreEOLWarningTypeB", 156, 1 << 2},
{"UncorrectErrCorrectionCode", 160, 1 << 2},
{"CurrentTemperature", 164, 1 << 2},
{"MinTemperature", 168, 1 << 2},
{"MaxTemperature", 172, 1 << 2},
{"EnrichedDeviceHealthTypeC", 180, 1 << 2},
{"EnrichedDeviceHealthTypeB", 184, 1 << 2},
{"CurrentPowerMode", 191, 1},
{"EnrichedDeviceHealthTypeA", 192, 1 << 2},
{"PreEOLWarningTypeB", 196, 1 << 2},
{"NumIOVoltDroopsOccurrences", 200, 1 << 2},
{"CumulativeHostReadDataSize", 204, 1 << 2},
{"BitFlipDetectionCounter", 208, 1 << 2},
{"BitFlipCorrectionCounter", 212, 1 << 2}
};
static int send_scsi_cmd(int fd, const unsigned char *cmdSeq, int cmdSeqLen,
unsigned char *data, int dataLen, int dir)
{
int status;
unsigned char *cdb; // CDB = Command Descriptor Block
iov_t iov[3];
SCSI_SENSE sense;
CAM_PASS_THRU cpt;
printf("Sending command 0x%02X\n", cmdSeq[0]);
memset((void *) &cpt, 0, sizeof(CAM_PASS_THRU));
cdb = (unsigned char *) cpt.cam_cdb; // Pointer to start of CAM CDB buffer.
cpt.cam_cdb_len = cmdSeqLen;
cpt.cam_timeout = 200000;
cpt.cam_path_id = 0;
cpt.cam_target_id = 0;
cpt.cam_target_lun = 0;
cpt.cam_sense_ptr = sizeof(cpt);
cpt.cam_sense_len = sizeof(sense);
SETIOV(&iov[0], &cpt, sizeof(cpt));
SETIOV(&iov[1], &sense, sizeof(sense));
memcpy(cdb, cmdSeq, cmdSeqLen);
if (dir == SG_DXFER_TO_DEV) {
cpt.cam_flags |= CAM_DIR_OUT;
} else {
cpt.cam_flags |= CAM_DIR_IN;
}
cpt.cam_data_ptr = sizeof(cpt) + sizeof(sense);
cpt.cam_dxfer_len = dataLen;
memset((char *) &sense, 0, sizeof(sense));
SETIOV(&iov[2], data, dataLen);
status = devctlv(fd, DCMD_CAM_PASS_THRU, 3, 3, iov, iov, NULL);
if (status != EOK) {
fprintf(stderr, "Command failed: %s\n", strerror(status));
fprintf(stderr, "Returned CAM status: %02x SCSI status: %02x\n",
(unsigned char) cpt.cam_status,
(unsigned char) cpt.cam_scsi_status);
fprintf(stderr, "SCSI sense: %02x key: %02x ASC: %02x ASCQ: %02x\n",
sense.error, sense.sense, sense.asc, sense.ascq);
fprintf(stderr, "CDB content: ");
int i;
for (i = 0; i < 16; i++) {
fprintf(stderr, "%02x ", (unsigned char) cdb[i]);
}
fprintf(stderr, "\n");
}
return status;
}
static inline void put_unaligned_be24(uint32_t val, void *p)
{
((uint8_t *)p)[0] = (val >> 16) & 0xff;
((uint8_t *)p)[1] = (val >> 8) & 0xff;
((uint8_t *)p)[2] = val & 0xff;
}
static void print_device_report(char *str, uint8_t *buf,
struct desc_field_offset *array,
int arr_size)
{
int i;
struct desc_field_offset *tmp;
char fw_rel_date[FW_REL_DATE_LEN + 1] = {0};
char fw_rel_time[FW_REL_TIME_LEN + 1] = {0};
for (i = 0; i < arr_size; ++i) {
tmp = &array[i];
if (tmp->width_byte == 1) {
printf("%s [Byte offset 0x%x]: %s = 0x%x\n",
str,
tmp->offset,
tmp->name,
buf[tmp->offset]);
} else if (tmp->width_byte == (1 << 2)) {
printf("%s [Byte offset 0x%x]: %s = 0x%x\n",
str,
tmp->offset,
tmp->name,
*(uint32_t *)&buf[tmp->offset]);
} else if (tmp->width_byte == (1 << 3)) {
memcpy(fw_rel_time, &buf[tmp->offset],
FW_REL_TIME_LEN);
printf("%s [Byte offset 0x%x]: %s = %s\n",
str,
tmp->offset,
tmp->name,
fw_rel_time);
} else if (tmp->width_byte == FW_REL_DATE_LEN) {
memcpy(fw_rel_date, &buf[tmp->offset],
FW_REL_DATE_LEN);
printf("%s [Byte offset 0x%x]: %s = %s\n",
str,
tmp->offset,
tmp->name,
fw_rel_date);
} else {
printf("%s [Byte offset 0x%x]: %s Wrong Width = %d",
str,
tmp->offset,
tmp->name,
tmp->width_byte);
}
}
printf("\n");
}
int read_buffer(int fd,
uint8_t *buf,
uint8_t mode,
uint8_t buf_id,
uint32_t buf_offset,
int byte_count)
{
int ret;
unsigned char rbufCmdBlk[READ_BUF_CMDLEN] = {READ_BUFFER_CMD,
0, 0, 0, 0, 0, 0, 0, 0, 0};
if (fd <= 0 || buf == NULL || byte_count <= 0) {
fprintf(stderr, "scsi write cmd: wrong parameters\n");
return -1;
}
rbufCmdBlk[1] = mode;
rbufCmdBlk[2] = buf_id;
put_unaligned_be24((uint32_t)buf_offset, rbufCmdBlk + 3);
put_unaligned_be24((uint32_t)byte_count, rbufCmdBlk + 6);
ret = send_scsi_cmd(fd, rbufCmdBlk, READ_BUF_CMDLEN,
buf, byte_count, SG_DXFER_FROM_DEV);
if (ret < 0) {
fprintf(stderr, "SG_IO READ BUFFER data error ret %d\n", ret);
}
return ret;
}
int do_vendor(int fd)
{
int rc = INVALID;
uint8_t buf[BLOCK_SIZE] = {0};
rc = read_buffer(fd, buf, BUFFER_VENDOR_MODE, 1,
VENDOR_KEY, BLOCK_SIZE);
if (!rc) {
print_device_report("Device Report:",
buf,
device_report_field_name,
ARRAY_SIZE(device_report_field_name));
printf("device_vendor_report.dat is created\n");
}
return rc;
}
int main(int argc, char **argv)
{
int fd;
if (argc != 2 ){
fprintf(stderr, "usage: %s <device>\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (-1 == fd) {
printf("Open of %s failed with error %i: %s\n", argv[1], errno, strerror(errno));
return errno;
} else {
printf("Open of %s succeeded\n", argv[1]);
}
do_vendor(fd);
close(fd);
return 0;
}