github.com/racerxdl/gonx@v0.0.0-20210103083128-c5afc43bcbd2/services/nv/nv.go (about)

     1  package nv
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"github.com/racerxdl/gonx/nx/nxerrors"
     7  	"github.com/racerxdl/gonx/nx/nxtypes"
     8  	"github.com/racerxdl/gonx/services/ipc"
     9  	"github.com/racerxdl/gonx/services/sm"
    10  	"github.com/racerxdl/gonx/svc"
    11  	"unsafe"
    12  )
    13  
    14  const nvDebug = false
    15  const transferMemSize = 3 * 1024 * 1024
    16  
    17  //go:align 4096
    18  var gpuSharedBuffer [transferMemSize]byte
    19  
    20  var nvObject ipc.Object
    21  var transferMem = nxtypes.Handle(0)
    22  var nvInitializations = 0
    23  
    24  func Init() (err error) {
    25  	if nvDebug {
    26  		println("NV::Init()")
    27  	}
    28  	nvInitializations++
    29  
    30  	if nvInitializations > 1 {
    31  		return nil
    32  	}
    33  
    34  	smInitialize := false
    35  	nvsInitialize := false
    36  	memInitialize := false
    37  
    38  	defer func() {
    39  		if err != nil {
    40  			println("got error: %s", err)
    41  			nvInitializations--
    42  			// Only clean-up this if errored
    43  			if memInitialize {
    44  				svc.CloseHandle(transferMem)
    45  			}
    46  
    47  			if nvsInitialize {
    48  				_ = ipc.Close(&nvObject)
    49  			}
    50  		}
    51  
    52  		// Always de-init sm
    53  		if smInitialize {
    54  			sm.Finalize()
    55  		}
    56  	}()
    57  
    58  	err = sm.Init()
    59  	if err != nil {
    60  		return fmt.Errorf("error initializing sm: %s", err)
    61  	}
    62  	smInitialize = true
    63  
    64  	//err = sm.GetService(&nvObject, "nvdrv:a")
    65  	err = sm.GetService(&nvObject, "nvdrv")
    66  	if err != nil {
    67  		return fmt.Errorf("error getting \"nvdrv:a\": %s", err)
    68  	}
    69  	nvsInitialize = true
    70  
    71  	r := svc.CreateTransferMemory(&transferMem, uintptr(unsafe.Pointer(&gpuSharedBuffer[0])), transferMemSize, 0)
    72  
    73  	if r != nxtypes.ResultOK {
    74  		return fmt.Errorf("fail to create transfer memory: result code %d", r)
    75  	}
    76  	memInitialize = true
    77  
    78  	handles := []nxtypes.Handle{0xFFFF8001, transferMem}
    79  
    80  	rq := ipc.MakeDefaultRequest(3)
    81  	rq.SetRawDataFromUint32Slice([]uint32{transferMemSize})
    82  	rq.CopyHandles = handles
    83  
    84  	rs := ipc.ResponseFmt{}
    85  	rs.RawData = make([]byte, 4) // one uint32
    86  
    87  	err = ipc.Send(nvObject, &rq, &rs)
    88  
    89  	if err != nil {
    90  		return fmt.Errorf("error sending ipc: %s", err)
    91  	}
    92  
    93  	response := []uint32{
    94  		binary.LittleEndian.Uint32(rs.RawData[:4]),
    95  	}
    96  
    97  	if response[0] != 0 {
    98  		return fmt.Errorf("nvidia error: %d", response[0])
    99  	}
   100  
   101  	return nil
   102  }
   103  
   104  func nvForceFinalize() {
   105  	if nvDebug {
   106  		println("NV::ForceFinalize()")
   107  	}
   108  	if transferMem != 0 {
   109  		svc.CloseHandle(transferMem)
   110  	}
   111  	_ = ipc.Close(&nvObject)
   112  	nvObject = ipc.Object{}
   113  	nvInitializations = 0
   114  }
   115  
   116  func Finalize() {
   117  	if nvDebug {
   118  		println("NV::Finalize()")
   119  	}
   120  	nvInitializations--
   121  	if nvInitializations <= 0 {
   122  		nvForceFinalize()
   123  	}
   124  }
   125  
   126  func Open(path string) (int32, error) {
   127  	if nvDebug {
   128  		fmt.Printf("NV::Open(%s)\n", path)
   129  	}
   130  	if nvInitializations <= 0 {
   131  		return -1, nxerrors.NVNotInitialized
   132  	}
   133  
   134  	bytePath := []byte(path)
   135  
   136  	buff := ipc.Buffer{}
   137  	buff.Type = 0x5
   138  	buff.Size = uint64(len(bytePath))
   139  	buff.Addr = uintptr(unsafe.Pointer(&bytePath[0]))
   140  
   141  	rq := ipc.MakeDefaultRequest(0)
   142  	rq.Buffers = append(rq.Buffers, &buff)
   143  
   144  	rs := ipc.ResponseFmt{}
   145  	rs.RawData = make([]byte, 2*4) // Two uint32
   146  
   147  	err := ipc.Send(nvObject, &rq, &rs)
   148  	if err != nil {
   149  		return -1, err
   150  	}
   151  
   152  	response := []uint32{
   153  		binary.LittleEndian.Uint32(rs.RawData[:4]),
   154  		binary.LittleEndian.Uint32(rs.RawData[4:]),
   155  	}
   156  
   157  	if response[1] != 0 {
   158  		return int32(response[1]), fmt.Errorf("nvopen failed: %d", response[1])
   159  	}
   160  
   161  	return int32(response[0]), nil
   162  }
   163  
   164  func Close(fd int32) error {
   165  	if nvDebug {
   166  		fmt.Printf("NV::Close(%d)\n", fd)
   167  	}
   168  	if nvInitializations <= 0 {
   169  		return nxerrors.NVNotInitialized
   170  	}
   171  	rq := ipc.MakeDefaultRequest(2)
   172  	rq.SetRawDataFromUint32Slice([]uint32{uint32(fd)})
   173  
   174  	rs := ipc.ResponseFmt{}
   175  	rs.RawData = make([]byte, 4) // one uint32
   176  
   177  	err := ipc.Send(nvObject, &rq, &rs)
   178  	if err != nil {
   179  		return fmt.Errorf("error on nvClose: %s", err)
   180  	}
   181  
   182  	res := binary.LittleEndian.Uint32(rs.RawData)
   183  
   184  	if res != 0 {
   185  		return fmt.Errorf("error on nvClose: result code: %d", res)
   186  	}
   187  
   188  	return nil
   189  }
   190  
   191  func Ioctl(fd int32, rqid uint32, arg unsafe.Pointer, size uintptr) (uint32, error) {
   192  	if nvDebug {
   193  		fmt.Printf("NV::Ioctl(%d, %d, %p, %d)\n", fd, rqid, arg, size)
   194  	}
   195  	if nvInitializations <= 0 {
   196  		return 0xFFFFFFFF, nxerrors.NVNotInitialized
   197  	}
   198  	InB := ipc.Buffer{
   199  		Addr: uintptr(arg),
   200  		Size: uint64(size),
   201  		Type: 0x21,
   202  	}
   203  
   204  	OutB := ipc.Buffer{
   205  		Addr: uintptr(arg),
   206  		Size: uint64(size),
   207  		Type: 0x22,
   208  	}
   209  
   210  	rq := ipc.MakeDefaultRequest(1)
   211  	rq.Buffers = []*ipc.Buffer{&InB, &OutB}
   212  	rq.SetRawDataFromUint32Slice([]uint32{uint32(fd), rqid, 0, 0})
   213  
   214  	rs := ipc.ResponseFmt{}
   215  	rs.RawData = []byte{0, 0, 0, 0} // One uint32
   216  
   217  	err := ipc.Send(nvObject, &rq, &rs)
   218  	if err != nil {
   219  		return 0, err
   220  	}
   221  
   222  	res := binary.LittleEndian.Uint32(rs.RawData)
   223  	return res, nil
   224  }