import ctypes
import fcntl


def read_nvme_disk_serial(dev):
    """Read hard drive model, serial and firmware version using NVME interface."""
    class NVMEAdminCmd(ctypes.Structure):
        _pack_ = 1
        _fields_ = [('opcode', ctypes.c_uint8),
                    ('flags', ctypes.c_uint8),
                    ('rsvd1', ctypes.c_uint16),
                    ('nsid', ctypes.c_uint32),
                    ('cdw2', ctypes.c_uint32),
                    ('cdw3', ctypes.c_uint32),
                    ('metadata', ctypes.c_uint64),
                    ('addr', ctypes.c_uint64),
                    ('metadata_len', ctypes.c_uint32),
                    ('data_len', ctypes.c_uint32),
                    ('cdw10', ctypes.c_uint32),
                    ('cdw11', ctypes.c_uint32),
                    ('cdw12', ctypes.c_uint32),
                    ('cdw13', ctypes.c_uint32),
                    ('cdw14', ctypes.c_uint32),
                    ('cdw15', ctypes.c_uint32),
                    ('timeout_ms', ctypes.c_uint32),
                    ('result', ctypes.c_uint32)]

    NVME_IOCTL_ADMIN_CMD = 0xc0484e41
    buf = ctypes.create_string_buffer(b'\x00', 4096)
    cmd = NVMEAdminCmd(opcode=0x06,  # NVME_IDENTIFY
                       addr=ctypes.addressof(buf),
                       data_len=4096,
                       cdw10=1)

    with open(dev, "rb") as fd:
        ret = fcntl.ioctl(fd, NVME_IOCTL_ADMIN_CMD, cmd, True)

    if ret != 0:
        raise Exception("NVME_IOCTL_ADMIN_CMD failed!")

    serial = buf[4:24]
    model = buf[24:64]
    fw = buf[64:72]
    return serial, model, fw