Support for multiple dynamic serial-port interfaces

Hi, I’m intending to write a Snap app for the Raspberry Pi 3 Ubuntu Core which intends to access several identical USB Serial Port devices.

Here is the rough listing of the devices I use.

$ ls -l /dev/serial/*
/dev/serial/by-id:
total 0
lrwxrwxrwx 1 root root 13 Mar 14 20:14 usb-FTDI_FT230X_Basic_UART_DM007FNY-if00-port0 -> ../../ttyUSB1
lrwxrwxrwx 1 root root 13 Mar 14 20:14 usb-FTDI_FT230X_Basic_UART_DM007X1O-if00-port0 -> ../../ttyUSB0
lrwxrwxrwx 1 root root 13 May 19 09:48 usb-Prolific_Technology_Inc._USB-Serial_Controller-if00-port0 -> ../../ttyUSB2


/dev/serial/by-path:
total 0
lrwxrwxrwx 1 root root 13 May 19 09:48 platform-3f980000.usb-usb-0:1.2:1.0-port0 -> ../../ttyUSB2
lrwxrwxrwx 1 root root 13 Mar 14 20:14 platform-3f980000.usb-usb-0:1.4:1.0-port0 -> ../../ttyUSB0
lrwxrwxrwx 1 root root 13 Mar 14 20:14 platform-3f980000.usb-usb-0:1.5:1.0-port0 -> ../../ttyUSB1

I mostly use the FTDI FT230X which will result in identical vendor and product IDs. The by-id paths also seem to be generated based on the serial number of the FTDI chip. I can’t use /dev/ttyUSBx due to the random enumeration order during system startup.

I can access these ports when I specify confinement: devmode but not confinement: strict in my app’s snapcraft.yaml. I understand I may need support in the Pi3-gadget snap for these ports to be exposed as slots.

Is there a way for my app to access these dynamically-generated /dev/serial/* ports? I tried specifying plugs: [serial-port] inside my app’s snapcraft.yaml but it does not seem to work.

Here are software versions I’m using.

core 16-2.26.3+201705181256.git.035e1 1977 canonical -
pi2-kernel 4.4.0-1051.52 30 canonical -
pi3 16.04-0.5 18 canonical -

1 Like

i think we should allow /dev/serial/by-id/* naming for the devices so you can keep identical USB dongles distinctable by their ID (given they will all have the same vendor and product id), but currently the serial port interface code only allows vendor/product IDs and a generated symlink for USB dongles, so you would end up with random order on every reboot.

@jdstrand and @niemeyer, can we extend the existing interface a slight bit for this ?

2 Likes

Yeah, these two items are related. The default gadget doesn’t expose a serial-port slot at all, so when you use plugs: [serial-port] in your app snap it has no slot to which it can connect. In order for that plug to do anything you need a custom gadget which exposes the slot. I’m not sure if we have explicit docs for doing that, but part 4 of the ROS Production series covered it.

note that this is a very specific interface issue here where multiple identical USB-serial dongles are attached and should be made available via serial interfaces from a custom gadget.

the problem is not adding a serial interface but the fact that we only allow a combination of vendor/product (which in this case are identical for all attached devices), but not access to /dev/serial/by-id which holds distinct info to tell the identical devices apart (the DM007FNY and DM007X1O serial numbers that are part of the id above).

Possible to extend the interface a slight bit like @ogra suggested?

Is there a clear problem statement and proposal to address it?

The problem I’m aware about and that needs some careful thinking is that when we have duplicated devices with matching characteristics there’s no good way to say connect this to that for both of them. In other words, given two serial dongles connected to the same laptop, how do we tell which is which so we can properly hand off control of the garage door and the safe door to the right snaps?

Today our serial-port interface is simplistic in that it’s not able to hotplug, and it’s able to uniquely identify only one vendor/product so that it can be assigned properly. The solution to this problem needs to handle this scenario appropriately.

1 Like

Currently, OP seems to be distinguishing the USB serial devices based on the listing in /dev/serial/by-id. The serial-port interface could try to resolve the issue by giving access to /dev/serial/by-id

Correction: I believe just giving access to /dev/serial/by-id will be insufficient as it may be unable to distinguish multiple usb-Prolific_Technology_Inc.USB-SerialController-if00-port0 devices.

Perhaps there is some other usb serial number/id of sorts

Just giving access doesn’t help, because we have N devices that we must be able to specifically assign to N snaps. Although simplistic, the current model purposefully supports that. The extension of that is that we’ll have multiple such devices, even when they share the exact same vendor/product, and will be able to assign to independent devices. If we just open up access to all serial ports, we lose the ability to do that.

With reference to this gadget snap, is it possible to define a key usb-serial in a custom gadget snap, based on this dmesg output?

While the mapping to /dev/tty(USB|ACM|AMA|XRUSB|S|O)[0-9] is random, at least the symlink at /dev/serial/by-id seems somewhat predictable.

Will these suggestions be helpful?

[  226.774166] usb 1-1.5: New USB device found, idVendor=0403, idProduct=6015
[  226.774187] usb 1-1.5: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[  226.774200] usb 1-1.5: Product: FT230X Basic UART
[  226.774212] usb 1-1.5: Manufacturer: FTDI
[  226.774225] usb 1-1.5: SerialNumber: DM007FNY
[  226.841250] usbcore: registered new interface driver usbserial
[  226.841341] usbcore: registered new interface driver usbserial_generic
[  226.841434] usbserial: USB Serial support registered for generic
[  226.894489] usbcore: registered new interface driver ftdi_sio
[  226.894597] usbserial: USB Serial support registered for FTDI USB Serial Device
[  226.894931] ftdi_sio 1-1.5:1.0: FTDI USB Serial Device converter detected
[  226.895165] usb 1-1.5: Detected FT-X
[  226.897141] usb 1-1.5: FTDI USB Serial Device converter now attached to ttyUSB0
foo:
    interface: serial-port
    usb-vendor: 0x0403
    usb-product: 0x6015
    usb-serial: DM007FNY
    path: /dev/serial-foo

As a reference, this is the dmesg output for the 2nd identical USB serial device, which shows the serial number being different

[ 4378.746600] usb 1-1.4: new full-speed USB device number 6 using dwc_otg
[ 4378.863337] usb 1-1.4: New USB device found, idVendor=0403, idProduct=6015
[ 4378.863357] usb 1-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[ 4378.863370] usb 1-1.4: Product: FT230X Basic UART
[ 4378.863383] usb 1-1.4: Manufacturer: FTDI
[ 4378.863395] usb 1-1.4: SerialNumber: DM007X1O
[ 4378.872217] ftdi_sio 1-1.4:1.0: FTDI USB Serial Device converter detected
[ 4378.872478] usb 1-1.4: Detected FT-X
[ 4378.873513] usb 1-1.4: FTDI USB Serial Device converter now attached to ttyUSB2

Taking a look at ls -la /dev/serial/by-path/, am wondering if the symlinks are something more predictable. They seems to be in the format platform-3f980000.usb-usb-0:1.*:1.0-port0.

total 0
drwxr-xr-x 2 root root 120 May 24 04:42 .
drwxr-xr-x 4 root root  80 May 24 02:57 ..
lrwxrwxrwx 1 root root  13 May 24 04:21 platform-3f980000.usb-usb-0:1.2:1.0-port0 -> ../../ttyUSB1
lrwxrwxrwx 1 root root  13 May 24 04:42 platform-3f980000.usb-usb-0:1.3:1.0-port0 -> ../../ttyUSB3
lrwxrwxrwx 1 root root  13 May 24 04:21 platform-3f980000.usb-usb-0:1.4:1.0-port0 -> ../../ttyUSB2
lrwxrwxrwx 1 root root  13 May 24 02:57 platform-3f980000.usb-usb-0:1.5:1.0-port0 -> ../../ttyUSB0

Would it also be feasible to consider identifying the physical port that the device is plugged to, as mentioned here? Though it may be an issue, if a USB hub is used.

how would you know which is which if they have different vendor/product id’s … this is usually not printed on the outside which means we already have the case that you can have two physical devices that can only be told apart programmatically. the request is just to add a third identifier here (usb-id: … ) to not block the use case where these come from th same vendor and are the same product.

this just enforces that you buy different dongles from different vendors if you need to use multiple ones, nothing more. allowing an optional “usb-id” will simply allow you to use multiple of the dongles you deem most trustful or reliable, thats all.

It’s not clear if that was really what was being requested. Note that the original request above includes “dynamically generated /dev/serial/* ports” in bold.

@yeokm1 Can you clarify the use case so we can put the proper feature in our shorter term roadmap?

Hi @niemeyer

Both @ogra and elfgoh have stated my problem quite clearly. I need to be able to connect to multiple identical (FTDI) serial adapters and yet still be able to tell them apart. Currently the only way I can do is is by the /dev/serial/by-id/* which is why I highlighted that point.

Alternatively as elfgoh has mentioned, adding the usb-serial/id field in addition to product and vendor id will also work as FTDI chips have unique serial numbers. It would be great if this is added to your roadmap.

@yeokm1 We understand that fundamental issue well. The question I’m asking above is whether these devices will be dynamically added to the system or not. The rest of the conversation above has more details about why that’s important.

is it actually important ?
i might understand something wrong here, but what i would expect is the following:

the setup will need a custom gadget anyway … in that custom gadget i’d imagine to have two serial definitions:

foo:
    interface: serial-port
    usb-vendor: 0x0403
    usb-product: 0x6015
    usb-id: DM007FNY
    path: /dev/serial-foo
bar:
    interface: serial-port
    usb-vendor: 0x0403
    usb-product: 0x6015
    usb-id: DM007X1O
    path: /dev/serial-bar

now one of the application snaps (your safe door app) connects to pi3:foo and /dev/serial-foo the other (garage door app) to pi3:bar and /dev/serial-bar …

due to the ID it will always be the right dongle for the right task.
as i understand it the udev based serial interfaces actually create udev rules so plug/unplug events should DTRT and set up the respective /dev/serial-* symlink dynamically.
(or is that not the case ?)

Again, @yeokm1 explicitly mentions dynamic serial ports in the problem description. Dynamic isn’t the same as a fixed list in gadget.yaml.

1 Like

Importantly, in terms of AppArmor, these are all symlinks to a particular device. Suppose we were to add policy to the serial-port interface for this, it would be simply:

  /dev/serial/by-id/,

this rule alone is harmless enough because it only allows listing the directory where an application could try to open one of the files. Because that directory contains only symlinks, AppArmor resolves them to what the symlink is pointing at, eg, /dev/ttyUSB1. Considering that for device assignment like this the device cgroup is used, a snap that was assigned to /dev/ttyUSB1 would only be able to open /dev/serial/by-id/usb-FTDI_FT230X_Basic_UART_DM007FNY-if00-port0 and not /dev/serial/bu-id/usb-FTDI_FT230X_Basic_UART_DM007X1O-if00-port0.

The problem comes when we use product/vendor in the gadget, because for these FTDI devices those are (apparently) the same and so snapd adds udev rules (for tagging the device to be used in the device cgroup) for both to a connected snap, thus breaking the 1 to 1 mapping that we want (and @niemeyer is discussing). I should note that this is already broken today since there is no way to differentiate between two identical product/vendor devices.

It sounds like this forum topic (topic subject notwithstanding) is really about how to differentiate between two identical product/vendor devices. In this specific example, I don’t think that the DM007FNY vs DM007X1O serial numbers are interesting in the gadget yaml (ie, the ‘usb-serial’ idea), since others have said every FTDI device has a different serial number and exposing that in the gadget yaml only helps the one person who has the devices with the serial numbers that match what is in the gadget. IME, that means ‘usb-serial’ is useless as a fixed list in the gadget yaml (at least for this specific FTDI scenario).

If the FTDI devices are hardwired on the device, then the gadget should instead use the alternative path: /dev/ttyUSB1 declaration in the gadget yaml and skip product/vendor. If the FTDI are not hard-wired, I think we must consider them as hot-pluggable. For the hot-pluggable situation, it is clear that when two devices are connected with identical product/vendor, snapd will (likely) have to look at other info to disambiguate them (perhaps the serial number is a part of that) and somehow remember that info and utilize it when performing interface connections and across hotplug/unplug, but this is still TBD (to be designed).

Hi @niemeyer and all

@jdstrand is right that I named the topic thread wrongly. I might have also been wrong when I used the term “dynamic ports”. All I needed was actually a way to access the serial ports of identical USB Serial adapters where using the product and vendor ID is insufficient.

It is fine for me to use a fixed list in gadget.yaml as I can get the USB serial numbers of the adapters in advance.

1 Like