|  | // Package nvme provides methods and data structures for issuing commands to | 
|  | // device speaking the NVMe protocol. | 
|  | // This package is written against the NVMe Specification Revision 1.3 and | 
|  | // all references to figures or other parts of the spec refer to this version. | 
|  | package nvme | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | "os" | 
|  | "syscall" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // Device is a handle for a NVMe device. | 
|  | type Device struct { | 
|  | fd syscall.Conn | 
|  | } | 
|  |  | 
|  | // NewFromFd creates a new NVMe device handle from a system handle. | 
|  | func NewFromFd(fd syscall.Conn) (*Device, error) { | 
|  | d := &Device{fd: fd} | 
|  | // There is no good way to validate that a file descriptor indeed points to | 
|  | // a NVMe device. For future compatibility let this return an error so that | 
|  | // code is already prepared to handle it. | 
|  | return d, nil | 
|  | } | 
|  |  | 
|  | // Open opens a new NVMe device handle from a device path (like /dev/nvme0). | 
|  | func Open(path string) (*Device, error) { | 
|  | f, err := os.Open(path) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("unable to open path: %w", err) | 
|  | } | 
|  | return NewFromFd(f) | 
|  | } | 
|  |  | 
|  | // Close closes the NVMe device handle. It returns an error if the handle was | 
|  | // not created by Open. Please close the handle passed to NewFromFd yourself | 
|  | // in that case. | 
|  | func (d *Device) Close() error { | 
|  | if f, ok := d.fd.(*os.File); ok { | 
|  | return f.Close() | 
|  | } else { | 
|  | return errors.New("unable to close device not opened via Open, please close it yourself") | 
|  | } | 
|  | } | 
|  |  | 
|  | const ( | 
|  | // GlobalNamespace is the namespace ID for operations not on a specific | 
|  | // namespace. | 
|  | GlobalNamespace = 0xffffffff | 
|  | ) | 
|  |  | 
|  | // Command represents a generic NVMe command. Only use this if the command | 
|  | // you need is not already wrapped by this library. | 
|  | type Command struct { | 
|  | Opcode                                   uint8 | 
|  | Flags                                    uint8 | 
|  | NamespaceID                              uint32 | 
|  | CDW2, CDW3                               uint32 | 
|  | Metadata                                 []byte | 
|  | Data                                     []byte | 
|  | CDW10, CDW11, CDW12, CDW13, CDW14, CDW15 uint32 | 
|  | Timeout                                  time.Duration | 
|  | } | 
|  |  | 
|  | func (d *Device) GetLogPage(ns uint32, logPageIdentifier uint8, logSpecificField uint8, logPageOffset uint64, pageBuf []byte) error { | 
|  | numberOfDwords := len(pageBuf) / 4 | 
|  | return d.RawCommand(&Command{ | 
|  | Opcode:      0x02, | 
|  | NamespaceID: ns, | 
|  | Data:        pageBuf, | 
|  | CDW10:       uint32(logPageIdentifier) | uint32(logSpecificField&0xF)<<8 | uint32(numberOfDwords)<<16, // TODO: RAE | 
|  | CDW11:       uint32(numberOfDwords >> 16 & 0xffff), | 
|  | CDW12:       uint32(logPageOffset & 0xffffffff), | 
|  | CDW13:       uint32(logPageOffset >> 32), | 
|  | }) | 
|  | } |