Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for (socat) pseudo serial ports #4

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jpnurmi
Copy link
Contributor

@jpnurmi jpnurmi commented Aug 8, 2020

For example:

# create a pseudo serial port pair with socat
$ socat pty,link=$HOME/dev/foo pty,link=$HOME/dev/bar &
[1] 28005

# open one side with the await_events example
$ ./await_events $HOME/dev/foo &
[2] 28006
Looking for port $HOME/dev/foo.
Opening port.
Setting port to 9600 8N1, no flow control.
Adding port RX event to event set.
Waiting up to 5 seconds for RX on any port...

# open the other side with the send_receive example
$ ./send_receive $HOME/dev/bar
Looking for port $HOME/dev/bar.
Opening port.
Setting port to 9600 8N1, no flow control.
Sending 'Hello!' (6 bytes) on port /dev/pts/2.
Sent 6 bytes successfully.
Receiving 6 bytes on port /dev/pts/2.
Port /dev/pts/1: 6 bytes received.  # <== await_events
Timed out, 0/6 bytes received.
Received ''.
[2]+  Done                    ./await_events $HOME/dev/foo

$ kill 28005 # socat

For example:

    # create a pseudo serial port pair with socat
    $ socat pty,link=$HOME/dev/foo pty,link=$HOME/dev/bar &
    [1] 28005

    # open one side with the await_events example
    $ ./await_events $HOME/dev/foo &
    [2] 28006
    Looking for port $HOME/dev/foo.
    Opening port.
    Setting port to 9600 8N1, no flow control.
    Adding port RX event to event set.
    Waiting up to 5 seconds for RX on any port...

    # open the other side with the send_receive example
    $ ./send_receive $HOME/dev/bar
    Looking for port $HOME/dev/bar.
    Opening port.
    Setting port to 9600 8N1, no flow control.
    Sending 'Hello!' (6 bytes) on port /dev/pts/2.
    Sent 6 bytes successfully.
    Receiving 6 bytes on port /dev/pts/2.
    Port /dev/pts/1: 6 bytes received.  # <== await_events
    Timed out, 0/6 bytes received.
    Received ''.
    [2]+  Done                    ./await_events $HOME/dev/foo

    $ kill 28005 # socat
@jpnurmi
Copy link
Contributor Author

jpnurmi commented Sep 26, 2020

ping @uwehermann @martinling

@martinling
Copy link
Contributor

martinling commented Sep 26, 2020

I'm not sure that dealing with PTY devices should be in-scope for libserialport.

Apart from reading & writing data, most parts of the libserialport API aren't applicable to PTYs. Configuration settings like baud rate, control lines, flow control etc aren't really applicable to them in the same way. They can't, or shouldn't, be enumerated as serial ports. So why include them?

On Unixish OSes, PTYs are inherently similar to serial ports because both show up as character devices and support termios ioctls. But that's a very platform-specific view of things. On Windows I guess the closest equivalent is a pipe or Winsock socket, and I don't think it makes any sense to have libserialport handle those too, just because you can read and write characters from them.

The point of libserialport is to be a cross-platform API to allow using the same code to do the same things across different OSes. But there isn't really an equivalent of a PTY on Windows, so it doesn't make much sense to have a program that uses them go through libserialport. The program wouldn't work on Windows.

I don't feel like it makes sense for the library. Would be interested to hear thoughts from others.

@jpnurmi
Copy link
Contributor Author

jpnurmi commented Sep 26, 2020

The main point is to make it possible to emulate serial ports so that you can run and test software without a real serial port device.

@jpnurmi
Copy link
Contributor Author

jpnurmi commented Sep 26, 2020

I believe the Windows equivalent is com0com, but I don't have much experience with that...

@martinling
Copy link
Contributor

Are you aware of https://github.com/lcgamboa/tty0tty? It's a Linux equivalent of com0com and I've used it successfully with libserialport in the past.

@jpnurmi
Copy link
Contributor Author

jpnurmi commented Sep 26, 2020

No, but thanks, I'll check it out.

@gsigh
Copy link
Contributor

gsigh commented Nov 2, 2020

Do you plan to use the pseudo terminals with sigrok on top of libserialport or with some other application that is not related to sigrok? Am asking because the extension is felt to not be compatible with the libserialport subproject itself, but might get implemented in libsigrok's serial abstraction layer if you want to. The "pipe/" namespace could be used, and IIUC the implementation could transparently cover pseudo terminals as well as (named) pipes? Device drivers for TM gear would benefit without any need for adjustment.

@jpnurmi
Copy link
Contributor Author

jpnurmi commented Nov 6, 2020

We are using this in a project not related to sigrok. This is very convenient for integration testing serial port pipelines e.g. outside a real target device. In contrast to installing the tty0tty kernel module, setting up socat is a matter of running a one-liner command. For what it's worth, socat works out of the box with Qt's QSerialPort, for example.

@martinling
Copy link
Contributor

I'm not completely opposed to having this in libserialport - I can see how it's useful, and the changes aren't particularly invasive; it's mostly just a case of skipping some operations that will fail for a PTY, rather than adding new code for that use case. It's more that I'm just a bit wary of feature creep in general.

On the specifics of the patch though, I don't like the approach of identifying PTYs only by them having pts/ in the device path - that seems fragile. Is there no better way?

@martinling
Copy link
Contributor

I don't like the approach of identifying PTYs only by them having pts/ in the device path - that seems fragile. Is there no better way?

Having looked into this briefly, checking the major number of the character device node should be a better way to reliably indicate a PTY, rather than relying on magic strings in the filesystem path, albeit commonly used ones.

On Linux, Unix98 PTY slaves use char major numbers 136-143.

p30arena added a commit to p30arena/flutter_libserialport that referenced this pull request Jun 9, 2021
@luizribeiro
Copy link

luizribeiro commented Jul 31, 2021

I like the idea of supporting pseudo terminals. With this patch I was able to get the EEVBlog 121GW to work without a BLE to UART gateway on macOS (where BLE isn't supported by sigrok), which makes it a lot easier to use that DMM.

All I had to do was to run ble-serial and update this patch to set port->transport to SP_TRANSPORT_PSEUDO on macos.c (which we should update on this PR btw - for now I just hard-coded the transport to test if this would work).

I share similar concerns to @martinling on how we're doing identification of PTYs. On macOS, my PTY is at /dev/ttys010. It seems that on BSD slave devices always match /dev/tty[p-sP-S][a-z0-9] (pty.7).

What if instead of checking for strings on filesystem path we introduced an option that can be passed to the driver?

@martinling
Copy link
Contributor

What if instead of checking for strings on filesystem path we introduced an option that can be passed to the driver?

The driver you're talking about is part of libsigrok, not libserialport.

For libsigrok, there's no need to make changes to libserialport in order to allow use of a PTY. Instead, libsigrok could just read and write the PTY directly. Or indeed it could integrate the use of ble-serial directly so that everything works automatically.

There is a serial API layer in libsigrok which already supports serial-ish things that aren't in-scope for libserialport, such as the serial-over-USB-HID chips commonly used in multimeters.

The role of libserialport is just to provide an OS-independent API to devices that the OS exposes as traditional serial ports. I'm wary of letting it feature-creep beyond that role.

On macOS, my PTY is at /dev/ttys010. It seems that on BSD slave devices always match /dev/tty[p-sP-S][a-z0-9]

I don't like relying on specific paths to device nodes. You should be able to create a matching device node with mknod anywhere on the filesystem and have it work. Are there documented ranges of major and minor char device numbers for PTY slaves, as is the case for Linux?

@luizribeiro
Copy link

Are there documented ranges of major and minor char device numbers for PTY slaves, as is the case for Linux?

After searching a bit, I don't think so. But to your point, we should be able to create a device node with mknod anywhere and have it work. If we relied on ranges of major char devices it wouldn't work, or am I misunderstanding the suggestion?


Either way, I think I agree with you that this is feature creep for libserialport. On this current implementation we are basically just bypassing all the flow control settings whenever the device is a PTY (and ignoring the current config of the port).

This defeats the purpose of going through libserialport, since we're pretty much disabling most of its features. I feel like it would be cleaner to add a serial_pty implementation (similar to serial_bt, serial_hid, etc) to libsigrok directly which read/writes to the PTY directly.

Would you be open to reviewing a PR to libsigrok that would introduce such feature? I imagine we would have to use a prefix like bt does so we can identify that a PTY will be used.

@mdjurfeldt
Copy link

mdjurfeldt commented Jan 16, 2023

[Cross-posting this comment to PR #4 and #5.]

Hi @martinling, @jpnurmi and @luizribeiro,

I have a setup with a Riden RD6012P. I'm using the esp-link firmware in my Wifi module which makes it possible to run modbus over telnet. Thus, I do:

sudo socat pty,link=/dev/virtualcom0,raw,group-late=dialout,mode=660 tcp:192.168.nnn.nnn:23

PRs #4 and #5 together make sigrok work with the socat pty and makes it possible for me to use the PSU over Wifi.

(And, yes, it is funny that PR #5 is needed. If PR #5 is not applied, there is an "sr: serial-libsp: Error opening port (16): Device or resource busy." error occurring after the first call to sp_open. This occurs also if the pty has been opened before in the same session with a different (erroneous) specified device. It is as if the socat opened pty is dependent on the port being in non-exclusive mode to close (fully).)

It would make life easier if you applied these two PRs. And I don't think it is strange at all to make it possible for libserialport to handle a "forwarded" serial port in this way.

@Spacefreak18
Copy link

Spacefreak18 commented Sep 3, 2023

Will this also add support for simlinks created through udev rules?

This is useful for static device names.

http://hintshop.ludvig.co.nz/show/persistent-names-usb-serial-devices/

for example:

SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A6008isP", SYMLINK+="arduino"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="A7004IXj", SYMLINK+="buspirate"
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", ATTRS{serial}=="FTDIF46B", SYMLINK+="ttyUSB.ARM"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants