blob: f46546d23864042bc305db2d388468d116ee7439 [file] [log] [blame]
Lorenz Brunfba5da02022-12-15 11:20:47 +00001// Package nvme provides methods and data structures for issuing commands to
2// device speaking the NVMe protocol.
3// This package is written against the NVMe Specification Revision 1.3 and
4// all references to figures or other parts of the spec refer to this version.
5package nvme
6
7import (
8 "errors"
9 "fmt"
10 "os"
11 "syscall"
12 "time"
13)
14
15// Device is a handle for a NVMe device.
16type Device struct {
17 fd syscall.Conn
18}
19
20// NewFromFd creates a new NVMe device handle from a system handle.
21func NewFromFd(fd syscall.Conn) (*Device, error) {
22 d := &Device{fd: fd}
23 // There is no good way to validate that a file descriptor indeed points to
24 // a NVMe device. For future compatibility let this return an error so that
25 // code is already prepared to handle it.
26 return d, nil
27}
28
29// Open opens a new NVMe device handle from a device path (like /dev/nvme0).
30func Open(path string) (*Device, error) {
31 f, err := os.Open(path)
32 if err != nil {
33 return nil, fmt.Errorf("unable to open path: %w", err)
34 }
35 return NewFromFd(f)
36}
37
38// Close closes the NVMe device handle. It returns an error if the handle was
39// not created by Open. Please close the handle passed to NewFromFd yourself
40// in that case.
41func (d *Device) Close() error {
42 if f, ok := d.fd.(*os.File); ok {
43 return f.Close()
44 } else {
45 return errors.New("unable to close device not opened via Open, please close it yourself")
46 }
47}
48
49const (
50 // GlobalNamespace is the namespace ID for operations not on a specific
51 // namespace.
52 GlobalNamespace = 0xffffffff
53)
54
55// Command represents a generic NVMe command. Only use this if the command
56// you need is not already wrapped by this library.
57type Command struct {
58 Opcode uint8
59 Flags uint8
60 NamespaceID uint32
61 CDW2, CDW3 uint32
62 Metadata []byte
63 Data []byte
64 CDW10, CDW11, CDW12, CDW13, CDW14, CDW15 uint32
65 Timeout time.Duration
66}
67
68func (d *Device) GetLogPage(ns uint32, logPageIdentifier uint8, logSpecificField uint8, logPageOffset uint64, pageBuf []byte) error {
69 numberOfDwords := len(pageBuf) / 4
70 return d.RawCommand(&Command{
71 Opcode: 0x02,
72 NamespaceID: ns,
73 Data: pageBuf,
74 CDW10: uint32(logPageIdentifier) | uint32(logSpecificField&0xF)<<8 | uint32(numberOfDwords)<<16, // TODO: RAE
75 CDW11: uint32(numberOfDwords >> 16 & 0xffff),
76 CDW12: uint32(logPageOffset & 0xffffffff),
77 CDW13: uint32(logPageOffset >> 32),
78 })
79}