Skip to content

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

@jalius

Description

@jalius

Describe the problem

Opening a serial port with non-standard baud rate 25000 fails occasionally. The failure occurs in nativeOpen()

go-serial/serial_unix.go

Lines 232 to 247 in f12391c

settings, err := port.getTermSettings()
if err != nil {
port.Close()
return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error getting term settings: %w", err)}
}
// Set raw mode
setRawMode(settings)
// Explicitly disable RTS/CTS flow control
setTermSettingsCtsRts(false, settings)
if err = port.setTermSettings(settings); err != nil {
port.Close()
return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error setting term settings: %w", err)}
}

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:

go-serial/serial_unix.go

Lines 271 to 276 in f12391c

// MacOSX require that this operation is the last one otherwise an
// 'Invalid serial port' error is returned... don't know why...
if err := port.SetMode(mode); err != nil {
port.Close()
return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error configuring port: %w", err)}
}

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.

  • I've provided the FULL source code that causes the problem
  • 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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions