edisionnano / QDiskInfo

QDiskInfo is a frontend for smartctl (part of the smartmontools package). It provides a user experience similar to CrystalDiskInfo. It shows the SMART (Self-Monitoring, Analysis, and Reporting Technology) data of modern hard disk drives.
GNU General Public License v3.0
193 stars 13 forks source link

Feature request: CDI's Edit -> Copy Option -> ASCII View #27

Open Ranguvar opened 1 month ago

Ranguvar commented 1 month ago

Thanks for creating this project! It works great on latest Arch with [testing] repos under Sway.

Perhaps you might help me find this information, whether or not you could add the feature to QDiskInfo? I'm looking for a Linux equivalent to CrystalDiskInfo's Edit -> Copy Option -> ASCII View, which shows the raw hex SMART data including the vendor specific data which should follow it.

This would allow me to check microcontroller and flash details. For example see: https://hddoracle.com/viewtopic.php?p=22539#p22539

Even if you know of a way to read this via smartctl or otherwise for now, that would really help me. It would be a great feature to have in the GUI eventually as well. I've been trying to use smartctl with -A, -d, and -v but have not found the data I seek.

Thanks again!

edisionnano commented 1 month ago

Hello,

#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <scsi/sg.h>
#include <string.h>

#define SMART_READ_CMD_LEN 12
#define INQUIRY_CMD_LEN 6
#define SMART_READ_RESP_LEN 512
#define INQUIRY_RESP_LEN 96
#define SENSE_BUFFER_LEN 32

int main(int argc, char *argv[]) {
    int fd, output_fd;
    unsigned char smart_read_cmd[SMART_READ_CMD_LEN] = {0xa1, 0x0c, 0x0e, 0xd0, 1, 0, 0x4f, 0xc2, 0, 0xb0, 0, 0};
    unsigned char inquiry_cmd[INQUIRY_CMD_LEN] = {0x12, 0, 0, 0, INQUIRY_RESP_LEN, 0};
    unsigned char smart_read_resp[SMART_READ_RESP_LEN];
    unsigned char inquiry_resp[INQUIRY_RESP_LEN];
    unsigned char sense_buffer[SENSE_BUFFER_LEN];
    sg_io_hdr_t io_hdr;

    if (argc < 2) {
        printf("Please enter a device file\n");
        return 1;
    }
    char *device_file_name = argv[1];
    char *output_file_name = "smart_data.bin";

    if ((fd = open(device_file_name, O_RDONLY)) < 0) {
        printf("Could not open device file: %s\n", device_file_name);
        return 1;
    }

    output_fd = open(output_file_name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (output_fd < 0) {
        printf("Failed to open output file: %s\n", output_file_name);
        close(fd);
        return 1;
    }

    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.cmd_len = SMART_READ_CMD_LEN;
    io_hdr.mx_sb_len = SENSE_BUFFER_LEN;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
    io_hdr.dxfer_len = SMART_READ_RESP_LEN;
    io_hdr.dxferp = smart_read_resp;
    io_hdr.cmdp = smart_read_cmd;
    io_hdr.sbp = sense_buffer;
    io_hdr.timeout = 20000;

    if (ioctl(fd, SG_IO, &io_hdr) < 0) {
        printf("IOCTL call for SMART READ failed\n");
        close(output_fd);
        close(fd);
        return 1;
    }

    if (write(output_fd, smart_read_resp, SMART_READ_RESP_LEN) != SMART_READ_RESP_LEN) {
        printf("Error writing data to file\n");
        close(output_fd);
        close(fd);
        return 1;
    }

    memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
    io_hdr.interface_id = 'S';
    io_hdr.cmd_len = INQUIRY_CMD_LEN;
    io_hdr.mx_sb_len = SENSE_BUFFER_LEN;
    io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
    io_hdr.dxfer_len = INQUIRY_RESP_LEN;
    io_hdr.dxferp = inquiry_resp;
    io_hdr.cmdp = inquiry_cmd;
    io_hdr.sbp = sense_buffer;
    io_hdr.timeout = 20000;

    if (ioctl(fd, SG_IO, &io_hdr) < 0) {
        printf("IOCTL call for SCSI INQUIRY failed\n");
        close(output_fd);
        close(fd);
        return 1;
    }

    if (write(output_fd, inquiry_resp, INQUIRY_RESP_LEN) != INQUIRY_RESP_LEN) {
        printf("Error writing data to file\n");
        close(output_fd);
        close(fd);
        return 1;
    }

    printf("Data saved to %s\n", output_file_name);

    close(output_fd);
    close(fd);

    return 0;
}

Above is a small C script which should give you what you want, this is the bin file when inspected with hexedit image Compile it with gcc using

gcc file.c

then do

sudo ./a.out /dev/sda

or whatever the path to the device file is

Ranguvar commented 1 month ago

Thank you so much! This works perfectly. Really fantastic, I did not expect so much help so quickly. I was able to confirm that my drive also uses the SM2259 microcontroller and B47R NAND flash.

If you'd like to put a license on it, I would share with others in SSD/Linux communities. It'd be a very useful addition to smartmontools or QDiskInfo though, even as a command-line option.

edisionnano commented 1 month ago

Thank you so much! This works perfectly. Really fantastic, I did not expect so much help so quickly. I was able to confirm that my drive also uses the SM2259 microcontroller and B47R NAND flash.

If you'd like to put a license on it, I would share with others in SSD/Linux communities. It'd be a very useful addition to smartmontools or QDiskInfo though, even as a command-line option.

I might add it yes, feel free to share it under the BSD or GPL 3 License. Does CrystalDiskInfo also do this for NVMe drives?

Ranguvar commented 1 month ago

Yes, CrystalDiskInfo's Copy works on NVMe drives and outputs similar info. I can't compare it directly to SATA drives as my sole Windows install only has direct access to NVMe. Looks like this code would need changes to support NVMe as well (IOCTL call for SMART READ failed).

edisionnano commented 1 month ago

I will look into getting the same type of data from NVMe first and then I will try to add this functionality. I have some friends with NVMe drives on Windows so I'll ask them for some dumps.