go-serial icon indicating copy to clipboard operation
go-serial copied to clipboard

MacOS Open() Fails Occasionally w/ Non-Standard Baud Rate

Open jalius opened this issue 7 months ago • 0 comments

Describe the problem

Opening a serial port with non-standard baud rate 25000 fails occasionally. The failure occurs in nativeOpen() https://github.com/bugst/go-serial/blob/f12391c01f39791f85712bad01e6781276ff0f64/serial_unix.go#L232-L247 due to tcsetattr() rejecting non-standard baudrates.

Scenario: Sometimes, getTermSettings() populates with a non-standard baud rate. Once the settings struct is populated with a non-standard baud rate, setTermSettings() and thus nativeOpen() will fail with EINVAL. This appears to be a MacOS specific behavior for tcsetattr rejecting non-standard baud rates (see related tcsetattr() issue in avrdude)

Note that a workaround for non-standard baud rate, setSpecialBaudRate(), is already called out in SetMode(), but it does not apply properly when getTermSettings (tcgetattr()) populates with a non-standard baud rate: https://github.com/bugst/go-serial/blob/f12391c01f39791f85712bad01e6781276ff0f64/serial_unix.go#L271-L276

This problem also applies to subsequent calls to SetMode w/ non-standard baud rate on a port that was Open()'d successfully. Please see the example code that I attached to reproduce this issue.

Possible Solution: We could replace the Ispeed and Ospeed fields with a standard baud rate before calling setTermSettings() if they are non-standard, then allow the setSpecialBaudRate() to set the non-standard baud rates.

To reproduce

package main

import (
	"fmt"

	"go.bug.st/serial"
)

func t1() error {
	// setting non-standard baud-rate a second time fails on MacOS
	// baud rate was set first in serial.Open()
	// baud rate attempted to set again in SetMode()
	Port := "/dev/tty.usbserial-DK0FPOX4"
	port, err := serial.Open(Port, &serial.Mode{BaudRate: 250000})
	if err != nil {
		fmt.Println("Open() failed", err.Error())
		return err
	}
	defer port.Close()
	err = port.SetMode(&serial.Mode{BaudRate: 250000})
	if err != nil {
		fmt.Println("SetMode() failed", err.Error())
		return err
	}
	fmt.Println("no error")
	return nil
}
func t2() error {
	// setting non-standard baud-rate works once on MacOS
	// baud rate was set first in serial.Open()
	// baud rate attempted to set again in SetMode()
	Port := "/dev/tty.usbserial-DK0FPOX4"

	port, err := serial.Open(Port, &serial.Mode{BaudRate: 9600})
	if err != nil {
		fmt.Println("Open() failed", err.Error())
		return err
	}
	defer port.Close()
	err = port.SetMode(&serial.Mode{BaudRate: 250000})
	if err != nil {
		fmt.Println("SetMode() failed", err.Error())
		return err
	}
	fmt.Println("no error")
	return nil
}
func main() {
	fmt.Println("t1():")
	t1()
	fmt.Println("t2():")
	t2()
}
t1():
SetMode() failed invalid argument
t2():
no error

Please double-check that you have reported each of the following

before submitting the issue.

  • [x] I've provided the FULL source code that causes the problem
  • [x] I've provided all the actions required to reproduce the problem

Expected behavior

Serial port should Open, and the workaround for MacOS non-standard baud rates should apply even when the port already has a non-standard baudrate.

Operating system and version

MacOS: Sequoia 15.5

Please describe your hardware setup

No response

Additional context

No response

Issue checklist

  • [x] I searched for previous requests in the issue tracker
  • [x] My request contains all necessary details

jalius avatar May 20 '25 21:05 jalius