syscalls.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package v4l2
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/fs"
  6. "os"
  7. sys "golang.org/x/sys/unix"
  8. )
  9. // OpenDevice offers a simpler file-open operation than the Go API's os.OpenFile (the Go API's
  10. // operation causes some drivers to return busy). It also applies file validation prior to opening the device.
  11. func OpenDevice(path string, flags int, mode uint32) (uintptr, error) {
  12. fstat, err := os.Stat(path)
  13. if err != nil {
  14. return 0, fmt.Errorf("open device: %w", err)
  15. }
  16. if (fstat.Mode() | fs.ModeCharDevice) == 0 {
  17. return 0, fmt.Errorf("device open: %s: not character device", path)
  18. }
  19. return openDev(path, flags, mode)
  20. }
  21. // openDev offers a simpler file open operation than the Go API OpenFile.
  22. // See https://cs.opensource.google/go/go/+/refs/tags/go1.19.1:src/os/file_unix.go;l=205
  23. func openDev(path string, flags int, mode uint32) (uintptr, error) {
  24. var fd int
  25. var err error
  26. for {
  27. fd, err = sys.Openat(sys.AT_FDCWD, path, flags, mode)
  28. if err == nil {
  29. break
  30. }
  31. if errors.Is(err, ErrorInterrupted) {
  32. continue //retry
  33. }
  34. return 0, &os.PathError{Op: "open", Path: path, Err: err}
  35. }
  36. return uintptr(fd), nil
  37. }
  38. // CloseDevice closes the device.
  39. func CloseDevice(fd uintptr) error {
  40. return closeDev(fd)
  41. }
  42. func closeDev(fd uintptr) error {
  43. return sys.Close(int(fd))
  44. }
  45. // ioctl is a wrapper for Syscall(SYS_IOCTL)
  46. func ioctl(fd, req, arg uintptr) (err sys.Errno) {
  47. for {
  48. _, _, errno := sys.Syscall(sys.SYS_IOCTL, fd, req, arg)
  49. switch errno {
  50. case 0:
  51. return 0
  52. case sys.EINTR:
  53. continue // retry
  54. default:
  55. return errno
  56. }
  57. }
  58. }
  59. // send sends a request to the kernel (via ioctl syscall)
  60. func send(fd, req, arg uintptr) error {
  61. errno := ioctl(fd, req, arg)
  62. if errno == 0 {
  63. return nil
  64. }
  65. parsedErr := parseErrorType(errno)
  66. switch parsedErr {
  67. case ErrorUnsupported, ErrorSystem, ErrorBadArgument:
  68. return parsedErr
  69. case ErrorTimeout, ErrorTemporary:
  70. // TODO add code for automatic retry/recovery
  71. return errno
  72. default:
  73. return errno
  74. }
  75. }
  76. // WaitForRead returns a channel that can be used to be notified when
  77. // a device's is ready to be read.
  78. func WaitForRead(dev Device) <-chan struct{} {
  79. sigChan := make(chan struct{})
  80. go func(fd uintptr) {
  81. defer close(sigChan)
  82. var fdsRead sys.FdSet
  83. for {
  84. fdsRead.Zero()
  85. fdsRead.Set(int(fd))
  86. tv := sys.Timeval{Sec: 2, Usec: 0}
  87. n, errno := sys.Select(int(fd+1), &fdsRead, nil, nil, &tv)
  88. if errno == sys.EINTR {
  89. continue
  90. }
  91. if n == 0 {
  92. // timeout, no data available
  93. continue
  94. }
  95. sigChan <- struct{}{}
  96. }
  97. }(dev.Fd())
  98. return sigChan
  99. }