bugst / go-serial

A cross-platform serial library for go-lang.
BSD 3-Clause "New" or "Revised" License
621 stars 191 forks source link

Tentative support for OpenBSD #28

Closed cmaglie closed 7 years ago

cmaglie commented 7 years ago

See #27

This is just a tentative, I don't have a OpenBSD box to try this out.

The Termios struct in OpenBSD is:

type Termios struct {
        Iflag  uint32
        Oflag  uint32
        Cflag  uint32
        Lflag  uint32
        Cc     [20]uint8
        Ispeed int32
        Ospeed int32
}

where Ispeed and Ospeed are, incredibly, defined as int32 instead of uint32. This may not be a problem in C where casts are implicit but it is in golang where a compiler error occurs. To fix this I had to add a function func toTermiosSpeedType(speed uint32) int32 to convert from Cflag type to I/Ospeed type.

/cc @bconway

cmaglie commented 7 years ago

Moreover I don't know if the all the constants in serial_openbsd.go are correct, something to check with C headers file on a live system.

bconway commented 7 years ago

Great, thanks. I'll give it a try on Monday.

If it's any help before I get to it, you can probably check out the headers in question without doing an install by looking in the release sets: https://ftp4.usa.openbsd.org/pub/OpenBSD/6.0/amd64/ . Presumably either base60.tgz or comp60.tgz would have what you need.

bconway commented 7 years ago

Thanks, I gave it a spin this morning. I first updated my code to use your OpenBSD branch and tested on OS X with the tool attached - it worked perfectly. When compiling on OpenBSD and moving the cable over there for testing, I seem to see similar behavior I saw previously with tarm/serial: the first run seems to go successfully, followed by returning an ASCII linefeed (10) on all successive attempts.

For reference: the tool I'm using, when sent the status command (two bytes), will return one byte with either an ASCII 0 to signify off, and an ASCII 1 to signify on. The only time I've ever seen the linefeed is when debugging on OpenBSD.

Here's something sample code slimmed down:

func ExitOnErr(msg string, err error) {
    if err != nil {
        log.Fatalf("%v - %v", msg, err)
    }
}

func RunCmd(line string, id byte) {
    m := &serial.Mode{
        BaudRate: 115200,
    }

    s, err := serial.Open(line, m)
    if err != nil {
        ExitOnErr("Open serial port failure", err)
    }

    _, err = s.Write([]byte{254, id})
    if err != nil {
        ExitOnErr("Serial port write failure", err)
    }

    buf := make([]byte, 2)
    n, err := s.Read(buf)
    fmt.Printf("n: %d\n", n)
    if err != nil {
        ExitOnErr("Serial port read failure", err)
    }

    err = s.Close()
    if err != nil {
        ExitOnErr("Serial port close failure", err)
    }

    fmt.Printf("%v\n", buf[:n])
}
$ ./doorctl -r 1 -c status
n: 1
[1]
$ ./doorctl -r 1 -c status
n: 1
[10]
$ ./doorctl -r 1 -c status
n: 1
[10]

Let me know if more OpenBSD-specific info might help. Thanks.

bconway commented 7 years ago

It turns out the cause of the behavior I was seeing on OpenBSD was needing to explicitly flush the USB/serial concoction first: https://github.com/tarm/serial/blob/master/serial_posix.go#L189

Not sure if go-serial has a semantic for that, I didn't see it in the Godoc. Thanks for the effort thus far!

cmaglie commented 7 years ago

@bconway I've updated the PR, now there are two new methods ResetInputBuffer and ResetOutputBuffer, courtesy of @albenik ;-). Would you like to try it?

the first run seems to go successfully, followed by returning an ASCII linefeed (10) on all successive attempts.

I'm wondering where this LF comes from? Is it part of the protocol? If yes you should read it before exiting to "consume" it, if not it would be nice to understand where it comes from.

bconway commented 7 years ago

Thanks for the heads up. I spoke with the vendor and verified that their device never sends an LF. I'm thinking this may be part of either OpenBSD's handling of serial port buffers or something in their FTDI driver, though I'm not currently in a position to dig too deeply into that. I also haven't tried it on Linux hardware for comparison, as I don't have much (physical) on hand.

I did refactor the code earlier and verified that if I try to read more than the LF after sending the command, I only end up blocking and waiting forever for another response that never arrives (though admittedly I didn't try reading first to see if the LF was already present to clear it and then send the command - the flush seemed like a better work-around).