...

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

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

     1  // Copyright 2011 Aaron Jacobs. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package serial
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"sync"
    22  	"syscall"
    23  	"unsafe"
    24  )
    25  
    26  type serialPort struct {
    27  	f  *os.File
    28  	fd syscall.Handle
    29  	rl sync.Mutex
    30  	wl sync.Mutex
    31  	ro *syscall.Overlapped
    32  	wo *syscall.Overlapped
    33  }
    34  
    35  type structDCB struct {
    36  	DCBlength, BaudRate                            uint32
    37  	flags                                          [4]byte
    38  	wReserved, XonLim, XoffLim                     uint16
    39  	ByteSize, Parity, StopBits                     byte
    40  	XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
    41  	wReserved1                                     uint16
    42  }
    43  
    44  type structTimeouts struct {
    45  	ReadIntervalTimeout         uint32
    46  	ReadTotalTimeoutMultiplier  uint32
    47  	ReadTotalTimeoutConstant    uint32
    48  	WriteTotalTimeoutMultiplier uint32
    49  	WriteTotalTimeoutConstant   uint32
    50  }
    51  
    52  func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
    53  	if len(options.PortName) > 0 && options.PortName[0] != '\\' {
    54  		options.PortName = "\\\\.\\" + options.PortName
    55  	}
    56  
    57  	h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(options.PortName),
    58  		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
    59  		0,
    60  		nil,
    61  		syscall.OPEN_EXISTING,
    62  		syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
    63  		0)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	f := os.NewFile(uintptr(h), options.PortName)
    68  	defer func() {
    69  		if err != nil {
    70  			f.Close()
    71  		}
    72  	}()
    73  
    74  	if err = setCommState(h, options); err != nil {
    75  		return nil, err
    76  	}
    77  	if err = setupComm(h, 64, 64); err != nil {
    78  		return nil, err
    79  	}
    80  	if err = setCommTimeouts(h, options); err != nil {
    81  		return nil, err
    82  	}
    83  	if err = setCommMask(h); err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	ro, err := newOverlapped()
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	wo, err := newOverlapped()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	port := new(serialPort)
    96  	port.f = f
    97  	port.fd = h
    98  	port.ro = ro
    99  	port.wo = wo
   100  
   101  	return port, nil
   102  }
   103  
   104  func (p *serialPort) Close() error {
   105  	return p.f.Close()
   106  }
   107  
   108  func (p *serialPort) Write(buf []byte) (int, error) {
   109  	p.wl.Lock()
   110  	defer p.wl.Unlock()
   111  
   112  	if err := resetEvent(p.wo.HEvent); err != nil {
   113  		return 0, err
   114  	}
   115  	var n uint32
   116  	err := syscall.WriteFile(p.fd, buf, &n, p.wo)
   117  	if err != nil && err != syscall.ERROR_IO_PENDING {
   118  		return int(n), err
   119  	}
   120  	return getOverlappedResult(p.fd, p.wo)
   121  }
   122  
   123  func (p *serialPort) Read(buf []byte) (int, error) {
   124  	if p == nil || p.f == nil {
   125  		return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f)
   126  	}
   127  
   128  	p.rl.Lock()
   129  	defer p.rl.Unlock()
   130  
   131  	if err := resetEvent(p.ro.HEvent); err != nil {
   132  		return 0, err
   133  	}
   134  	var done uint32
   135  	err := syscall.ReadFile(p.fd, buf, &done, p.ro)
   136  	if err != nil && err != syscall.ERROR_IO_PENDING {
   137  		return int(done), err
   138  	}
   139  	return getOverlappedResult(p.fd, p.ro)
   140  }
   141  
   142  var (
   143  	nSetCommState,
   144  	nSetCommTimeouts,
   145  	nSetCommMask,
   146  	nSetupComm,
   147  	nGetOverlappedResult,
   148  	nCreateEvent,
   149  	nResetEvent uintptr
   150  )
   151  
   152  func init() {
   153  	k32, err := syscall.LoadLibrary("kernel32.dll")
   154  	if err != nil {
   155  		panic("LoadLibrary " + err.Error())
   156  	}
   157  	defer syscall.FreeLibrary(k32)
   158  
   159  	nSetCommState = getProcAddr(k32, "SetCommState")
   160  	nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
   161  	nSetCommMask = getProcAddr(k32, "SetCommMask")
   162  	nSetupComm = getProcAddr(k32, "SetupComm")
   163  	nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
   164  	nCreateEvent = getProcAddr(k32, "CreateEventW")
   165  	nResetEvent = getProcAddr(k32, "ResetEvent")
   166  }
   167  
   168  func getProcAddr(lib syscall.Handle, name string) uintptr {
   169  	addr, err := syscall.GetProcAddress(lib, name)
   170  	if err != nil {
   171  		panic(name + " " + err.Error())
   172  	}
   173  	return addr
   174  }
   175  
   176  func setCommState(h syscall.Handle, options OpenOptions) error {
   177  	var params structDCB
   178  	params.DCBlength = uint32(unsafe.Sizeof(params))
   179  
   180  	params.flags[0] = 0x01  // fBinary
   181  	params.flags[0] |= 0x10 // Assert DSR
   182  
   183  	if options.ParityMode != PARITY_NONE {
   184  		params.flags[0] |= 0x03 // fParity
   185  		params.Parity = byte(options.ParityMode)
   186  	}
   187  
   188  	if options.StopBits == 1 {
   189  		params.StopBits = 0
   190  	} else if options.StopBits == 2 {
   191  		params.StopBits = 2
   192  	}
   193  
   194  	params.BaudRate = uint32(options.BaudRate)
   195  	params.ByteSize = byte(options.DataBits)
   196  
   197  	if options.RTSCTSFlowControl {
   198  		params.flags[0] |= 0x04 // fOutxCtsFlow = 0x1
   199  		params.flags[1] |= 0x20 // fRtsControl = RTS_CONTROL_HANDSHAKE (0x2)
   200  	}
   201  
   202  	r, _, err := syscall.Syscall(nSetCommState, 2, uintptr(h), uintptr(unsafe.Pointer(&params)), 0)
   203  	if r == 0 {
   204  		return err
   205  	}
   206  	return nil
   207  }
   208  
   209  func setCommTimeouts(h syscall.Handle, options OpenOptions) error {
   210  	var timeouts structTimeouts
   211  	const MAXDWORD = 1<<32 - 1
   212  	timeoutConstant := uint32(round(float64(options.InterCharacterTimeout) / 100.0))
   213  	readIntervalTimeout := uint32(options.MinimumReadSize)
   214  
   215  	if timeoutConstant > 0 && readIntervalTimeout == 0 {
   216  		//Assume we're setting for non blocking IO.
   217  		timeouts.ReadIntervalTimeout = MAXDWORD
   218  		timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
   219  		timeouts.ReadTotalTimeoutConstant = timeoutConstant
   220  	} else if readIntervalTimeout > 0 {
   221  		// Assume we want to block and wait for input.
   222  		timeouts.ReadIntervalTimeout = readIntervalTimeout
   223  		timeouts.ReadTotalTimeoutMultiplier = 1
   224  		timeouts.ReadTotalTimeoutConstant = 1
   225  	} else {
   226  		// No idea what we intended, use defaults
   227  		// default config does what it did before.
   228  		timeouts.ReadIntervalTimeout = MAXDWORD
   229  		timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
   230  		timeouts.ReadTotalTimeoutConstant = MAXDWORD - 1
   231  	}
   232  
   233  	/*
   234  			Empirical testing has shown that to have non-blocking IO we need to set:
   235  				ReadTotalTimeoutConstant > 0 and
   236  				ReadTotalTimeoutMultiplier = MAXDWORD and
   237  				ReadIntervalTimeout = MAXDWORD
   238  
   239  				The documentation states that ReadIntervalTimeout is set in MS but
   240  				empirical investigation determines that it seems to interpret in units
   241  				of 100ms.
   242  
   243  				If InterCharacterTimeout is set at all it seems that the port will block
   244  				indefinitly until a character is received.  Not all circumstances have been
   245  				tested. The input of an expert would be appreciated.
   246  
   247  			From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx
   248  
   249  			 For blocking I/O see below:
   250  
   251  			 Remarks:
   252  
   253  			 If an application sets ReadIntervalTimeout and
   254  			 ReadTotalTimeoutMultiplier to MAXDWORD and sets
   255  			 ReadTotalTimeoutConstant to a value greater than zero and
   256  			 less than MAXDWORD, one of the following occurs when the
   257  			 ReadFile function is called:
   258  
   259  			 If there are any bytes in the input buffer, ReadFile returns
   260  			       immediately with the bytes in the buffer.
   261  
   262  			 If there are no bytes in the input buffer, ReadFile waits
   263  		               until a byte arrives and then returns immediately.
   264  
   265  			 If no bytes arrive within the time specified by
   266  			       ReadTotalTimeoutConstant, ReadFile times out.
   267  	*/
   268  
   269  	r, _, err := syscall.Syscall(nSetCommTimeouts, 2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)), 0)
   270  	if r == 0 {
   271  		return err
   272  	}
   273  	return nil
   274  }
   275  
   276  func setupComm(h syscall.Handle, in, out int) error {
   277  	r, _, err := syscall.Syscall(nSetupComm, 3, uintptr(h), uintptr(in), uintptr(out))
   278  	if r == 0 {
   279  		return err
   280  	}
   281  	return nil
   282  }
   283  
   284  func setCommMask(h syscall.Handle) error {
   285  	const EV_RXCHAR = 0x0001
   286  	r, _, err := syscall.Syscall(nSetCommMask, 2, uintptr(h), EV_RXCHAR, 0)
   287  	if r == 0 {
   288  		return err
   289  	}
   290  	return nil
   291  }
   292  
   293  func resetEvent(h syscall.Handle) error {
   294  	r, _, err := syscall.Syscall(nResetEvent, 1, uintptr(h), 0, 0)
   295  	if r == 0 {
   296  		return err
   297  	}
   298  	return nil
   299  }
   300  
   301  func newOverlapped() (*syscall.Overlapped, error) {
   302  	var overlapped syscall.Overlapped
   303  	r, _, err := syscall.Syscall6(nCreateEvent, 4, 0, 1, 0, 0, 0, 0)
   304  	if r == 0 {
   305  		return nil, err
   306  	}
   307  	overlapped.HEvent = syscall.Handle(r)
   308  	return &overlapped, nil
   309  }
   310  
   311  func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) {
   312  	var n int
   313  	r, _, err := syscall.Syscall6(nGetOverlappedResult, 4,
   314  		uintptr(h),
   315  		uintptr(unsafe.Pointer(overlapped)),
   316  		uintptr(unsafe.Pointer(&n)), 1, 0, 0)
   317  	if r == 0 {
   318  		return n, err
   319  	}
   320  
   321  	return n, nil
   322  }
   323  

View as plain text