The serial-port interface

The serial-port interface enables input and output access to a specific serial port on a device typically running Ubuntu Core. As a result, and because it provides such privileged access to configure serial port hardware, serial-port is considered a restricted interface.

Use snap interface serial-port to see which serial-port devices are available on the system:

$ snap interface serial-port 
name:    serial-port
summary: allows accessing a specific serial port
slots:
  - core:model01 (allows accessing a specific serial port)
  - core:monome (allows accessing a specific serial port)

Once connected, the consuming snap can use the device via the path specified by the connected slot.

Interface documentation:

See Interface management and Supported interfaces for further details on how interfaces are used.


Developer details

Auto-Connect: no
Attributes:

  • Should specify a single path attribute:
    • path (slot): path to serial device node e.g. /dev/ttyS1
  • Or three attributes:
    • usb-vendor (slot): integer representing the USB Vendor ID, must be in range 0 < vid <= 65535
    • usb-product (slot): integer representing the USB Product ID, must be in range 0 <= vid <= 65535
    • path (slot): path of the form /dev/serial-port-... where a symlink will be created to the device e.g. /dev/serial-port-mydevice

Hardware IO interfaces covers some general considerations common to these kinds of devices.

To use a serial-port device, the snap developer must add plugs: [ serial-port ] to a snap’s snapcraft.yaml. The snap user can then access a specific serial-port device with an interface connection.

Code examples

The following example shows the slot configuration, such as from the gadget snap, and includes which snaps are permitted to connect automatically:

serial-port:
    allow-auto-connection:
      -
        on-store:
          - (whatever)
        plug-names:
          - serial-foo
        plug-snap-id:
          - foooVbn5YriRw2sRVw7Cuj5PbjJjwnFb
        slot-attributes:
          path: /dev/whatever
        slot-names:
          - serial-foo

All attributes must match for an auto-connection attempt to be successful. The above example requires a connecting snap to have a matching snap-id and plug name. For example, the following snapcraft.yaml stanza for the connecting snap would not connect:

apps:
  whatever:
     plugs:
        serial-port

While the following snapcraft.yaml stanza for the connecting snap would automatically connect:

apps:
  whatever:
     plugs:
        serial-foo
plugs:
   serial-foo:
      interface: serial-port

The test code can be found in the snapd repository: serial_port_test.go.

The source code for the interface is in the snapd repository: serial_port.go

2 Likes

Is there an example on how any application snap connect to the serial port.

Isn’t the description missing usb-interface-number?

1 Like

I think snap interface serial-port lists the snaps that provide slots and the snaps the have plugs for the named interface (in this case serial-port interface). That is, it does not seem list see “serial port devices” as the current text indicates. For example, I have a gadget named ‘my-test-gadg’ that declares the slot, and I have a snap ‘my-sp-app’ that has the plug, and I see this:

knitzsche@localhost:~$ snap interface serial-port
name:    serial-port
summary: allows accessing a specific serial port
plugs:
  - my-sp-app
slots:
  - my-test-gadg

I just found that declaring a usb-vendor or usb-product with a four-digit INT with a leading 0 causes a toolchain failure leading to a snap that cannot be uploaded, that is, these fail:

slots:
  serial-port:
    usb-vendor: 0658
    usb-product: 0200
    ....

Use this instead:

slots:
  serial-port:
    usb-vendor: 0x0658
    usb-product: 0x0200
    ....

I cannot yet build a gadget that declares the serial-port slot and declares BOTH the path and the two usb-* attributes.

This:

slots:
  serial-port:
    path: /dev/ttyACM0
    usb-vendor: 0x0658
    usb-product: 0x0200 

Results in this:
snap "my-gadge" has bad plugs or slots: serial-port (serial-port path attribute specifies invalid symlink location)

This is discussed up above, with usb-vendor and usb-product, ‘path’ specifies the symlink name: https://github.com/snapcore/snapd/blob/master/interfaces/builtin/serial_port.go#L77

Use this:

slots:
  serial-port:
    path: /dev/serial-port-?
    usb-vendor: 0x0658
    usb-product: 0x0200 

Thanks Jamie. The text could be clearer by simply stating that when usb-vendor and usb-product attributes are declared, the path value must start with /dev/serial-port-.

Thanks Kyle - and yes, I think you’re right. I’ll update the text to try and make it clearer.

@degville - oh, I just did! Please adjust as you see fit :slight_smile:

That’s awesome, thank you :slight_smile:

Any chance it will be supported in the classic core snap?

Yes. And I think usb-interface-number may be needed when a (for example) USB device provides two interfaces and you need therefore two paths to them exposed deterministically.

What is the significance of the prefix of “serial-port-” when defining a slot for a USB serial port? Is this a prefix required by udev?

I have a pcie modem and the modem serial port is “/dev/wwan0at*” . What should I do to use these ports in snap app?

I see there is no support for /dev/wwan* in https://github.com/snapcore/snapd/blob/master/interfaces/builtin/serial_port.go#L77

Hey there,

A customer had some questions regarding how serial-port interfaces (and generally other IO-oriented ones) auto-connect. I wrote this. I’m pasting it here in case some of this information is useful to someone else; and it’d be awesome if some of this seems generically useful and could be added to the official documentation for this and/or other interfaces.


For IO interfaces like serial, gpio and i2c, our convention (in a brand store context) is to drive them from the gadget snap instead of from the connecting snap. This is more robust and allows the gadget snap (the one providing the slot) to centralize and arbitrate the conditions under which other snaps (identified by snap ID) can connect to the slots the gadget offers.

For these interfaces, then, usually no change is required in the plugging snap’s snap declaration. The only thing the plugging snap needs to do is declare and use an appropriately-configured plug in snap.yaml.

All the changes in the snap declaration are done in the slot-offering snap (usually a gadget snap). You’ll notice that the snap declaration indicates which snaps are allowed to auto-connect, assuming the corresponding plug matches the indicated attributes:


serial-port:
    allow-auto-connection:
      -
        on-store:
          - (whatever)
        plug-names:
          - serial-foo
        plug-snap-id:
          - foooVbn5YriRw2sRVw7Cuj5PbjJjwnFb
        slot-attributes:
          path: /dev/whatever
        slot-names:
          - serial-foo

Note that all attributes must match for an auto-connection attempt to be successful. In this case we are scoping by plug-snap-id, indicating the attributes of the candidate slot, and also the name of the plug that will be connected to this slot.

A possible reason if this is not auto-connecting is because the plugging snap is using the “serial-port” plug name which implicitly uses the “serial-port” interface, whereas it should declare instead a specific plug that contains the attributes that can match the slot rule above. You would need to apply these changes to the snap.yaml file (you typically make these changes in the snapcraft.yaml file and then rebuild the snap).

For example, this won’t work:

apps:
  whatever:
     plugs:
        serial-port

But this will:

apps:
  whatever:
     plugs:
        serial-foo
plugs:
   serial-foo:
      interface: serial-port

As for when a snap-declaration-driven auto-connection is processed: All auto-connections that are configured in snap-declarations, be it on the slot side or the plug side, are always processed when the snap is installed or refreshed. Strictly speaking, they are processed when a just-installed or refreshed snap-declaration is obtained by the device (even if the snap revision itself did not change). This means that they do not require a reboot to be connected. You should only need to "snap refresh " the gadget and application snap for the changes to apply.

The counterpart to this are auto-connections driven from the gadget snap’s connections section as described here: Gadget snaps. Those only auto-connect on first boot, but that is not what we are doing here.

  • Daniel
1 Like

Thank you so much for such a detailed write-up on how the interface is used. I’ve now updated the doc to incorporate the information you provided. I’d really like to do the same for other interfaces and I’ll try to plan for this in an upcoming cycle.