...

Source file src/github.com/jacobsa/go-serial/serial/open_linux.go

Documentation: github.com/jacobsa/go-serial/serial

     1  package serial
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"os"
     7  	"syscall"
     8  	"unsafe"
     9  
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  //
    14  // Grab the constants with the following little program, to avoid using cgo:
    15  //
    16  // #include <stdio.h>
    17  // #include <stdlib.h>
    18  // #include <linux/termios.h>
    19  //
    20  // int main(int argc, const char **argv) {
    21  //   printf("TCSETS2 = 0x%08X\n", TCSETS2);
    22  //   printf("BOTHER  = 0x%08X\n", BOTHER);
    23  //   printf("NCCS    = %d\n",     NCCS);
    24  //   return 0;
    25  // }
    26  //
    27  const (
    28  	kTCSETS2 = 0x402C542B
    29  	kBOTHER  = 0x1000
    30  	kNCCS    = 19
    31  )
    32  
    33  //
    34  // Types from asm-generic/termbits.h
    35  //
    36  
    37  type cc_t byte
    38  type speed_t uint32
    39  type tcflag_t uint32
    40  type termios2 struct {
    41  	c_iflag  tcflag_t    // input mode flags
    42  	c_oflag  tcflag_t    // output mode flags
    43  	c_cflag  tcflag_t    // control mode flags
    44  	c_lflag  tcflag_t    // local mode flags
    45  	c_line   cc_t        // line discipline
    46  	c_cc     [kNCCS]cc_t // control characters
    47  	c_ispeed speed_t     // input speed
    48  	c_ospeed speed_t     // output speed
    49  }
    50  
    51  // Constants for RS485 operation
    52  
    53  const (
    54  	sER_RS485_ENABLED        = (1 << 0)
    55  	sER_RS485_RTS_ON_SEND    = (1 << 1)
    56  	sER_RS485_RTS_AFTER_SEND = (1 << 2)
    57  	sER_RS485_RX_DURING_TX   = (1 << 4)
    58  	tIOCSRS485               = 0x542F
    59  )
    60  
    61  type serial_rs485 struct {
    62  	flags                 uint32
    63  	delay_rts_before_send uint32
    64  	delay_rts_after_send  uint32
    65  	padding               [5]uint32
    66  }
    67  
    68  //
    69  // Returns a pointer to an instantiates termios2 struct, based on the given
    70  // OpenOptions. Termios2 is a Linux extension which allows arbitrary baud rates
    71  // to be specified.
    72  //
    73  func makeTermios2(options OpenOptions) (*termios2, error) {
    74  
    75  	// Sanity check inter-character timeout and minimum read size options.
    76  
    77  	vtime := uint(round(float64(options.InterCharacterTimeout)/100.0) * 100)
    78  	vmin := options.MinimumReadSize
    79  
    80  	if vmin == 0 && vtime < 100 {
    81  		return nil, errors.New("invalid values for InterCharacterTimeout and MinimumReadSize")
    82  	}
    83  
    84  	if vtime > 25500 {
    85  		return nil, errors.New("invalid value for InterCharacterTimeout")
    86  	}
    87  
    88  	ccOpts := [kNCCS]cc_t{}
    89  	ccOpts[syscall.VTIME] = cc_t(vtime / 100)
    90  	ccOpts[syscall.VMIN] = cc_t(vmin)
    91  
    92  	t2 := &termios2{
    93  		c_cflag:  syscall.CLOCAL | syscall.CREAD | kBOTHER,
    94  		c_ispeed: speed_t(options.BaudRate),
    95  		c_ospeed: speed_t(options.BaudRate),
    96  		c_cc:     ccOpts,
    97  	}
    98  
    99  	switch options.StopBits {
   100  	case 1:
   101  	case 2:
   102  		t2.c_cflag |= syscall.CSTOPB
   103  
   104  	default:
   105  		return nil, errors.New("invalid setting for StopBits")
   106  	}
   107  
   108  	switch options.ParityMode {
   109  	case PARITY_NONE:
   110  	case PARITY_ODD:
   111  		t2.c_cflag |= syscall.PARENB
   112  		t2.c_cflag |= syscall.PARODD
   113  
   114  	case PARITY_EVEN:
   115  		t2.c_cflag |= syscall.PARENB
   116  
   117  	default:
   118  		return nil, errors.New("invalid setting for ParityMode")
   119  	}
   120  
   121  	switch options.DataBits {
   122  	case 5:
   123  		t2.c_cflag |= syscall.CS5
   124  	case 6:
   125  		t2.c_cflag |= syscall.CS6
   126  	case 7:
   127  		t2.c_cflag |= syscall.CS7
   128  	case 8:
   129  		t2.c_cflag |= syscall.CS8
   130  	default:
   131  		return nil, errors.New("invalid setting for DataBits")
   132  	}
   133  
   134  	if options.RTSCTSFlowControl {
   135  		t2.c_cflag |= unix.CRTSCTS
   136  	}
   137  
   138  	return t2, nil
   139  }
   140  
   141  func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
   142  
   143  	file, openErr :=
   144  		os.OpenFile(
   145  			options.PortName,
   146  			syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK,
   147  			0600)
   148  	if openErr != nil {
   149  		return nil, openErr
   150  	}
   151  
   152  	// Clear the non-blocking flag set above.
   153  	nonblockErr := syscall.SetNonblock(int(file.Fd()), false)
   154  	if nonblockErr != nil {
   155  		return nil, nonblockErr
   156  	}
   157  
   158  	t2, optErr := makeTermios2(options)
   159  	if optErr != nil {
   160  		return nil, optErr
   161  	}
   162  
   163  	r, _, errno := syscall.Syscall(
   164  		syscall.SYS_IOCTL,
   165  		uintptr(file.Fd()),
   166  		uintptr(kTCSETS2),
   167  		uintptr(unsafe.Pointer(t2)))
   168  
   169  	if errno != 0 {
   170  		return nil, os.NewSyscallError("SYS_IOCTL", errno)
   171  	}
   172  
   173  	if r != 0 {
   174  		return nil, errors.New("unknown error from SYS_IOCTL")
   175  	}
   176  
   177  	if options.Rs485Enable {
   178  		rs485 := serial_rs485{
   179  			sER_RS485_ENABLED,
   180  			uint32(options.Rs485DelayRtsBeforeSend),
   181  			uint32(options.Rs485DelayRtsAfterSend),
   182  			[5]uint32{0, 0, 0, 0, 0},
   183  		}
   184  
   185  		if options.Rs485RtsHighDuringSend {
   186  			rs485.flags |= sER_RS485_RTS_ON_SEND
   187  		}
   188  
   189  		if options.Rs485RtsHighAfterSend {
   190  			rs485.flags |= sER_RS485_RTS_AFTER_SEND
   191  		}
   192  
   193  		r, _, errno := syscall.Syscall(
   194  			syscall.SYS_IOCTL,
   195  			uintptr(file.Fd()),
   196  			uintptr(tIOCSRS485),
   197  			uintptr(unsafe.Pointer(&rs485)))
   198  
   199  		if errno != 0 {
   200  			return nil, os.NewSyscallError("SYS_IOCTL (RS485)", errno)
   201  		}
   202  
   203  		if r != 0 {
   204  			return nil, errors.New("Unknown error from SYS_IOCTL (RS485)")
   205  		}
   206  	}
   207  
   208  	return file, nil
   209  }
   210  

View as plain text